├── blur.vp ├── ssao.vp ├── shaders.qrc ├── phong.vp ├── main.cpp ├── VectorFunctions.h ├── phong.fp ├── README.md ├── MainWindow.h ├── Osg3dSSAOView.cpp ├── NodeMask.h ├── Osg3dSSAOView.h ├── ssao.fp ├── OSGWidget.h ├── VectorFunctions.cpp ├── UiEventWidget.h ├── blur.fp ├── Osg3dViewWithCamera.h ├── OSGWidget.cpp ├── CMakeLists.txt ├── NodeMask.cpp ├── SSAONode.h ├── CameraModel.h ├── UiEventWidget.cpp ├── Osg3dViewWithCamera.cpp ├── MainWindow.ui ├── LICENSE ├── MainWindow.cpp ├── SSAONode.cpp └── CameraModel.cpp /blur.vp: -------------------------------------------------------------------------------- 1 | #version 120 2 | 3 | void main(void) 4 | { 5 | gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; 6 | gl_TexCoord[0] = gl_MultiTexCoord0; 7 | } -------------------------------------------------------------------------------- /ssao.vp: -------------------------------------------------------------------------------- 1 | #version 120 2 | 3 | void main(void) 4 | { 5 | gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; 6 | gl_TexCoord[0] = gl_MultiTexCoord0; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /shaders.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | blur.fp 4 | blur.vp 5 | phong.fp 6 | phong.vp 7 | ssao.fp 8 | ssao.vp 9 | 10 | 11 | -------------------------------------------------------------------------------- /phong.vp: -------------------------------------------------------------------------------- 1 | #version 120 2 | 3 | varying vec4 vertColor; 4 | varying vec4 vertPosition; 5 | varying vec3 vertNormal; 6 | 7 | void main(void) 8 | { 9 | vertColor = gl_Color; 10 | vertPosition = gl_ModelViewMatrix * gl_Vertex; 11 | vertNormal = normalize(gl_NormalMatrix * gl_Normal); 12 | gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; 13 | } 14 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "MainWindow.h" 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | QApplication a(argc, argv); 10 | // If resources are in shared library, Make all resources loaded 11 | // see https://wiki.qt.io/QtResources 12 | // and http://doc.qt.io/qt-5/resources.html 13 | // Q_INIT_RESOURCE(shaders); 14 | 15 | 16 | 17 | MainWindow w; 18 | w.show(); 19 | 20 | return a.exec(); 21 | } 22 | -------------------------------------------------------------------------------- /VectorFunctions.h: -------------------------------------------------------------------------------- 1 | // This software is covered by the MIT open source license. See misc/MIT.txt 2 | 3 | #ifndef VECTORFUNCTIONS_H 4 | #define VECTORFUNCTIONS_H 5 | #include 6 | #include 7 | #include 8 | 9 | double preAcosClamp(double val, const double minval, const double maxval); 10 | 11 | class VectorFunctions 12 | { 13 | VectorFunctions() {} 14 | public: 15 | static void vecFromBallisticAzEl(osg::Vec2d ae, 16 | osg::Vec3d &dir, 17 | osg::Vec3d &up); 18 | static osg::Vec2d azElFromVec(osg::Vec3d vecDir); 19 | static bool vecFromString(osg::Vec3d &v, QString str); 20 | }; 21 | 22 | #endif // VECTORFUNCTIONS_H 23 | -------------------------------------------------------------------------------- /phong.fp: -------------------------------------------------------------------------------- 1 | #version 120 2 | 3 | varying vec3 vertNormal; 4 | varying vec4 vertColor; 5 | varying vec4 vertPosition; 6 | 7 | vec3 phong(vec3 normal, vec3 lightPosition, vec3 position, vec3 ka, vec3 kd, vec3 ks, float shine) 8 | { 9 | vec3 s = normalize(vec3(lightPosition) - position); 10 | vec3 v = normalize(vec3(-position)); 11 | vec3 r = reflect(-s, normal); 12 | return ka + kd * max( dot(s, normal), 0.0 ) + ks * pow(max(dot(r,v), 0.0), shine); 13 | } 14 | 15 | void main(void) 16 | { 17 | vec3 n = normalize(vertNormal); 18 | vec3 lightPosition = vec3(0); // headlight 19 | 20 | vec3 color = phong(n, lightPosition, vertPosition.xyz, vec3(0), vertColor.rgb, vec3(0), 16.0f); 21 | 22 | // Color buffer 23 | gl_FragData[0] = vec4(color, 1.0); 24 | 25 | // Normal buffer 26 | gl_FragData[1] = vec4(n * 0.5 + 0.5, 1.0); 27 | } 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # osgSSAO 2 | Implementation of screen space ambient occlusion (ssao) using 3 | OpenSceneGraph (OSG). 4 | 5 | The application window consists of 3 views. Two OSG views on the left and 6 | center, and a panel to adjust the screen space ambient occlusion parameters on 7 | the right. 8 | 9 | The left-most OSG view is rendered with an OSGWidget which derives from the 10 | legacy QOGLWidget. This properly displays ssao and is enabled for SSAO at 11 | application startup. 12 | 13 | The center OSG view is rendered with an Osg3dSSAOView which derives from a 14 | Osg3dViewWithCamera which in turn derives from the new QOpenGLWidget. This QT 15 | widget renders GL into a texture which Qt then composites into the application 16 | window. This does not properly render the scene. 17 | 18 | Inspection of the existing SSAO implementation shows that it does all the work 19 | in PRE- and POST- render passes. This makes the primary render traversal 20 | of no value. Ideally, SSAO would render as PRE- render passes and the primary 21 | render traversal would produce the final output. This would likely solve the 22 | problem with using QOpenGLWidget as well. 23 | -------------------------------------------------------------------------------- /MainWindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include "UiEventWidget.h" 6 | #include 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 | public slots: 19 | void on_actionOpen_triggered(); 20 | void setMouseModeOrbit(); 21 | void setMouseModePan(); 22 | void setMouseModeRotate(); 23 | void setMouseModeZoom(); 24 | void handleDisplayModeCombo(); 25 | 26 | void ssaoHalo(bool tf); 27 | void ssaoBlur(bool tf); 28 | void setSSAOEnabled(bool tf); 29 | 30 | void setRadius(); 31 | void setThreshold(); 32 | void setPower(); 33 | 34 | 35 | private: 36 | void applicationSetup(); 37 | osg::ref_ptr buildAxes(); 38 | osg::ref_ptr buildScene(); 39 | 40 | void setupOSGWidget(Osg3dSSAOView *ssaoView); 41 | void setupSSAOWidget(Osg3dSSAOView *ssaoView); 42 | void connectHandlers(Osg3dSSAOView *ssaoView); 43 | 44 | Ui::MainWindow *ui; 45 | osg::ref_ptr m_world; 46 | 47 | 48 | }; 49 | 50 | #endif // MAINWINDOW_H 51 | -------------------------------------------------------------------------------- /Osg3dSSAOView.cpp: -------------------------------------------------------------------------------- 1 | #include "Osg3dSSAOView.h" 2 | 3 | Osg3dSSAOView::Osg3dSSAOView(QWidget *parent) 4 | : Osg3dViewWithCamera(parent) 5 | , m_ssao(new SSAONode(width(), height())) 6 | { 7 | 8 | m_root->removeChild(m_scene); // un-do the Osg3dViewWithCamera setup 9 | SSAONode::buildGraph(m_root, m_scene, m_ssao); 10 | } 11 | 12 | void Osg3dSSAOView::setSSAOEnabled(bool tf) 13 | { 14 | SSAONode::setSSAOEnabled(m_root, m_scene, m_ssao, tf); 15 | 16 | emit ssaoIsEnabledChanged(tf); 17 | update(); 18 | } 19 | 20 | void Osg3dSSAOView::paintGL() 21 | { 22 | // Update the camera 23 | osg::Camera *cam = this->getCamera(); 24 | 25 | cam->setCullMask( m_cameraModel->cullMask() ); 26 | 27 | m_cameraModel->setAspect((double)width() / (double)height()); 28 | 29 | cam->setViewMatrix(m_cameraModel->getModelViewMatrix() ); 30 | cam->setProjectionMatrix(m_cameraModel->computeProjection()); 31 | 32 | // Let SSAO class know that camera has changed 33 | m_ssao->updateProjectionMatrix(getCamera()->getProjectionMatrix()); 34 | 35 | // Invoke the OSG traversal pipeline 36 | frame(); 37 | } 38 | 39 | void Osg3dSSAOView::resizeGL(int width, int height) 40 | { 41 | m_ssao->Resize(width, height); 42 | m_osgGraphicsWindow->resized(0,0,width,height); 43 | } 44 | -------------------------------------------------------------------------------- /NodeMask.h: -------------------------------------------------------------------------------- 1 | #ifndef NODEMASK_H 2 | #define NODEMASK_H 3 | 4 | #include 5 | 6 | namespace osg { 7 | class Node; 8 | } 9 | class QRegExpValidator; 10 | 11 | class NodeMask 12 | { 13 | private: 14 | NodeMask() {} 15 | public: 16 | static const unsigned ChildShift = 16; 17 | // Node masks. Note that for groups we also set whether there exists a child 18 | // with each bit set by shifting the bits up two bytes. So a node with the 19 | // SELECTED bit set (0x02) should have a parent with the (0x020000) bit set. This 20 | // allows the OSG cullMask to select drawing "selected items" by traversing the 21 | // graph with a cullMask of 0x0202. 22 | enum NodeMaskValue { 23 | NONE = 0, 24 | UNSELECTED = 1<<0, 25 | SELECTED = 1<<1, 26 | HIDDEN = 1<<2, 27 | SHOTLINE = 1<<7, 28 | GROUP = 1<<8, 29 | AXIS = 1<<9, 30 | CUTTINGPLANE = 1<<10, 31 | POINTINDICATOR = 1<<11, 32 | ALL = ~0 // camera cullMask always has this set so we traverse to leaves 33 | // Group nodes should have it set, only Geode/Geometry need not. 34 | }; 35 | 36 | static QString maskToString(unsigned mask); 37 | static unsigned stringToMask(QString s); 38 | static void setNodeMasksOnHeirarchy(osg::Node *n, NodeMaskValue mask); 39 | static void setNodeMasksBitOnHeirarchy(osg::Node *n, NodeMaskValue mask); 40 | static void clearNodeMasksBitOnHeirarchy(osg::Node *n, NodeMaskValue mask); 41 | static void markGroups(osg::Node *n); 42 | static void markGroupsAndLeafNodes(osg::Node *n, NodeMaskValue mask); 43 | static QRegExpValidator * createValidator(); 44 | static const int bitsForDisplayMask = 12; 45 | static const char * const yadda; 46 | 47 | static const char * const bitNames[]; 48 | }; 49 | 50 | 51 | 52 | #endif // NODEMASK_H 53 | -------------------------------------------------------------------------------- /Osg3dSSAOView.h: -------------------------------------------------------------------------------- 1 | #ifndef OSG3DVIEWWITHSSAO_H 2 | #define OSG3DVIEWWITHSSAO_H 3 | 4 | #include "Osg3dViewWithCamera.h" 5 | #include "SSAONode.h" 6 | 7 | class Osg3dSSAOView : public Osg3dViewWithCamera 8 | { 9 | Q_OBJECT 10 | public: 11 | explicit Osg3dSSAOView(QWidget *parent = 0); 12 | 13 | bool ssaoIsEnabled() const { return m_root->getValue(m_root->getChildIndex(m_ssao)); } 14 | bool ssaoBlurIsEnabled() const { return m_ssao->IsAOBlurEnabled(); } 15 | bool ssaoHaloRemovalIsEnabled() const {return m_ssao->IsHaloRemovalEnabled();} 16 | float ssaoRadius() const { return m_ssao->GetSSAORadius(); } 17 | float ssaoPower() const { return m_ssao->GetSSAOPower(); } 18 | float ssaoHaloThreshold() const { return m_ssao->GetHaloTreshold(); } 19 | unsigned ssaoDisplayMode() const { return m_ssao->GetDisplayMode(); } 20 | 21 | signals: 22 | void ssaoRadiusChanged(float f); 23 | void ssaoPowerChanged(float f); 24 | void ssaoHaloThresholdChanged(float f); 25 | void ssaoHaloRemovalChanged(bool tf); 26 | void ssaoBlurEnabledChanged(bool tf); 27 | void ssaoIsEnabledChanged(bool tf); 28 | 29 | public slots: 30 | virtual void setSSAOEnabled(bool tf); 31 | void setSSAOBlurEnabled(bool tf) { m_ssao->setAOBlurEnabled(tf); 32 | emit ssaoBlurEnabledChanged(tf);} 33 | void setSSAOHaloRemoval(bool tf) { m_ssao->setHaloRemovalEnabled(tf); 34 | emit ssaoHaloRemovalChanged(tf);} 35 | void setSSAORadius(float r) { m_ssao->SetSSAORadius(r); 36 | emit ssaoRadiusChanged(r);} 37 | void setSSAOPower(float f) { m_ssao->SetSSAOPower(f); 38 | emit ssaoPowerChanged(f);} 39 | void setSSAOHaloThreshold(float f) { m_ssao->SetHaloTreshold(f); 40 | emit ssaoHaloThresholdChanged(f);} 41 | void setSSAODisplayMode(SSAONode::DisplayMode mode) { m_ssao->SetDisplayMode(mode); update();} 42 | /// Render one frame 43 | virtual void paintGL() override; 44 | 45 | /// Updates OpenGL viewport when window size changes 46 | virtual void resizeGL( int width, int height ) override; 47 | 48 | protected: 49 | SSAONode *m_ssao; 50 | }; 51 | 52 | #endif // OSG3DVIEWWITHSSAO_H 53 | -------------------------------------------------------------------------------- /ssao.fp: -------------------------------------------------------------------------------- 1 | #version 120 2 | 3 | // G-buffer 4 | uniform sampler2D linearDepthTexture; 5 | uniform sampler2D normalTexture; 6 | uniform sampler2D colorTexture; 7 | 8 | uniform sampler2D noiseTexture; 9 | 10 | const int MAX_KERNEL_SIZE = 128; 11 | uniform vec3 ssaoKernel[MAX_KERNEL_SIZE]; 12 | 13 | uniform mat4 projMatrix; 14 | uniform mat4 invProjMatrix; 15 | uniform vec2 noiseTextureRcp; 16 | uniform int kernelSize; 17 | 18 | uniform float ssaoRadius; 19 | uniform float ssaoPower; 20 | 21 | uniform int displayType = 0; 22 | 23 | vec3 reconstruct_pos(float z, vec2 vTexCoord, in mat4 projMatrix){ 24 | vec4 vProjectedPos = vec4(vTexCoord * 2.0 - 1.0, z, 1.0f); 25 | vProjectedPos = invProjMatrix * vProjectedPos; 26 | return vProjectedPos.xyz / vProjectedPos.w; 27 | } 28 | 29 | float reconstruct_z(in float depth, in mat4 projMatrix){ 30 | return -projMatrix[3][2] / (depth + projMatrix[2][2]); 31 | } 32 | 33 | float ssao() 34 | { 35 | // Calculate view space position 36 | float originDepthNormalized = texture2D(linearDepthTexture, gl_TexCoord[0].st).r; 37 | 38 | // Skip fragments on far plane 39 | if (originDepthNormalized == 1.0f) return 1.0f; 40 | 41 | vec3 origin = reconstruct_pos(originDepthNormalized, gl_TexCoord[0].st, projMatrix); 42 | 43 | // Fetch view space normal 44 | vec3 normal = normalize(texture2D(normalTexture, gl_TexCoord[0].st).xyz * 2.0 - 1.0); 45 | 46 | // Fetch noise 47 | vec3 rvec = texture2D(noiseTexture, gl_TexCoord[0].st * noiseTextureRcp).xyz * 2.0 - 1.0; 48 | 49 | // Calculate change-of-basis matrix (view space -> "face" space) 50 | vec3 tangent = normalize(rvec - dot(rvec, normal) * normal); 51 | vec3 bitangent = cross(tangent, normal); 52 | mat3 tbn = mat3(tangent, bitangent, normal); 53 | 54 | float occlusion = 0.0; 55 | 56 | for (int i = 0; i < kernelSize; ++i) { 57 | 58 | // get sample position: 59 | vec3 _sample = origin + (tbn * (ssaoKernel[i])) * ssaoRadius; 60 | 61 | // project sample position: 62 | vec4 offset = projMatrix * vec4(_sample, 1.0); 63 | 64 | offset.xy /= offset.w; 65 | offset.xy = offset.xy * 0.5 + 0.5; 66 | 67 | // get sample depth: 68 | float sampleDepth = texture2D(linearDepthTexture, offset.xy).r; 69 | sampleDepth = reconstruct_z(sampleDepth, projMatrix); 70 | 71 | float dist = abs(origin.z - sampleDepth); 72 | 73 | float rangeCheck = smoothstep(0.0, 1.0, ssaoRadius / dist); 74 | occlusion += rangeCheck * step(_sample.z, sampleDepth); 75 | } 76 | 77 | occlusion = 1.0 - (occlusion / float(kernelSize)); 78 | occlusion = pow(occlusion, ssaoPower); 79 | 80 | return occlusion; 81 | } 82 | 83 | 84 | void main(void) 85 | { 86 | float occlusion = ssao(); 87 | vec3 color = texture2D(colorTexture, gl_TexCoord[0].st).rgb; 88 | gl_FragColor = vec4(color, occlusion); 89 | } 90 | -------------------------------------------------------------------------------- /OSGWidget.h: -------------------------------------------------------------------------------- 1 | #ifndef OSGWIDGET_H 2 | #define OSGWIDGET_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "CameraModel.h" 10 | #include "SSAONode.h" 11 | 12 | class OSGWidget : public QGLWidget, 13 | public osgViewer::Viewer 14 | { 15 | Q_OBJECT 16 | 17 | public: 18 | OSGWidget(QWidget *parent); 19 | 20 | // Overridden from osgViewer::Viewer ////////////////////////////////////// 21 | /// Let others tell what scene graph we should be drawing 22 | void setScene(osg::Node *scene); 23 | 24 | public: 25 | bool ssaoIsEnabled() const { return m_root->getValue(m_root->getChildIndex(m_ssao)); } 26 | bool ssaoBlurIsEnabled() const { return m_ssao->IsAOBlurEnabled(); } 27 | bool ssaoHaloRemovalIsEnabled() const {return m_ssao->IsHaloRemovalEnabled();} 28 | float ssaoRadius() const { return m_ssao->GetSSAORadius(); } 29 | float ssaoPower() const { return m_ssao->GetSSAOPower(); } 30 | float ssaoHaloThreshold() const { return m_ssao->GetHaloTreshold(); } 31 | unsigned ssaoDisplayMode() const { return m_ssao->GetDisplayMode(); } 32 | 33 | 34 | public slots: 35 | void setSSAOEnabled(bool tf); 36 | void setSSAOBlurEnabled(bool tf) { m_ssao->setAOBlurEnabled(tf);} 37 | void setSSAOHaloRemoval(bool tf) { m_ssao->setHaloRemovalEnabled(tf);} 38 | void setSSAORadius(float r) { m_ssao->SetSSAORadius(r);;} 39 | void setSSAOPower(float f) { m_ssao->SetSSAOPower(f);} 40 | void setSSAOHaloThreshold(float f) { m_ssao->SetHaloTreshold(f);} 41 | void setSSAODisplayMode(SSAONode::DisplayMode mode); 42 | /// Render one frame 43 | virtual void paintGL() override; 44 | 45 | void setCameraModel(osg::ref_ptr cameraModel); 46 | 47 | private: 48 | // SSAO support ////////////////////////////////////// 49 | 50 | SSAONode* m_ssao; 51 | // Helper functions /////////////////////////////////////////////////////// 52 | 53 | /// OSG uses singleSided drawing/display by default. 54 | /// This is annoying when you are "inside" something and the back wall of 55 | /// it simply disappears. This gets called to set up to draw both front and 56 | /// back facing polygons so that the world behaves in a more normal fashion. 57 | /// Yes it's a performance hit. 58 | void setLightingTwoSided(); 59 | 60 | /// Invoked whenever the window size changes so that the OpenGL viewport 61 | /// gets updated appropriately 62 | void resizeGL( int width, int height ); 63 | 64 | // Private data /////////////////////////////////////////////////////////// 65 | 66 | osg::ref_ptr m_root; 67 | osg::ref_ptr m_scene; 68 | 69 | /// OSG graphics window 70 | osg::ref_ptr m_osgGraphicsWindow; 71 | 72 | /// Viewing Core --> controls the camera of the osgViewer 73 | osg::ref_ptr< CameraModel > m_cameraModel; 74 | 75 | }; 76 | 77 | #endif // OSGVIEW_H 78 | -------------------------------------------------------------------------------- /VectorFunctions.cpp: -------------------------------------------------------------------------------- 1 | // This software is covered by the MIT open source license. See misc/MIT.txt 2 | 3 | #include "VectorFunctions.h" 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | void VectorFunctions::vecFromBallisticAzEl(osg::Vec2d ae, 10 | osg::Vec3d &dir, 11 | osg::Vec3d &up) 12 | { 13 | osg::Vec3d xAxis(1.0, 0.0, 0.0); 14 | osg::Vec3d yAxis(0.0, 1.0, 0.0); 15 | osg::Vec3d zAxis(0.0, 0.0, 1.0); 16 | dir = osg::Vec3d(-1.0, 0.0, 0.0); 17 | up = zAxis; 18 | 19 | osg::Matrixd mat; 20 | mat.makeRotate(osg::DegreesToRadians(-ae[1]), yAxis); 21 | dir = dir * mat; 22 | up = up * mat; 23 | 24 | mat.makeRotate(osg::DegreesToRadians(ae[0]), zAxis); 25 | dir = dir * mat; 26 | up = up * mat; 27 | } 28 | osg::Vec2d VectorFunctions::azElFromVec(osg::Vec3d vecDir) 29 | { 30 | const double twoPi( 2. * osg::PI ); 31 | osg::Vec3d xAxis(1.0, 0.0, 0.0); 32 | osg::Vec3d yAxis(0.0, 1.0, 0.0); 33 | osg::Vec3d zAxis(0.0, 0.0, 1.0); 34 | 35 | vecDir.normalize(); 36 | osg::Vec3d rightHandDirection = vecDir ^ zAxis; 37 | 38 | osg::Vec3d viewDirInYXPlane = zAxis ^ rightHandDirection; 39 | viewDirInYXPlane.normalize(); 40 | 41 | double cosAngleRadiansInPlane = preAcosClamp(viewDirInYXPlane * xAxis, -1.0, 1.0); 42 | double azimuthInRadians = acos(cosAngleRadiansInPlane); 43 | 44 | bool pointsTowardsNegativeYAxis = (viewDirInYXPlane * yAxis) < 0.0; 45 | if ( pointsTowardsNegativeYAxis ) 46 | azimuthInRadians = twoPi - azimuthInRadians; 47 | 48 | double cosAngleRadiansOfElevation = preAcosClamp(vecDir * viewDirInYXPlane, -1.0, 1.0); 49 | 50 | double elevationInRadians = acos(cosAngleRadiansOfElevation); 51 | 52 | bool pointsTowardsZAxis = (vecDir * zAxis) >= 0.0; 53 | if (pointsTowardsZAxis) elevationInRadians *= -1.0; 54 | 55 | 56 | 57 | return osg::Vec2d(osg::RadiansToDegrees( azimuthInRadians ), 58 | osg::RadiansToDegrees(elevationInRadians)); 59 | 60 | } 61 | 62 | bool VectorFunctions::vecFromString(osg::Vec3d &v, QString str) 63 | { 64 | static const char *floatRegExpPattern = "([+-]?([0-9]*[\\.])?[0-9]+)"; 65 | QRegExp m_regExp(floatRegExpPattern); 66 | QStringList tokens; 67 | 68 | for (int pos = 0; 69 | (pos = m_regExp.indexIn(str, pos)) != -1 ; 70 | pos += m_regExp.matchedLength()) { 71 | tokens << m_regExp.cap(1); 72 | } 73 | if (tokens.size() != 3) return false; 74 | v = osg::Vec3d(tokens[0].toDouble(), 75 | tokens[1].toDouble(), 76 | tokens[2].toDouble() ); 77 | return true; 78 | } 79 | 80 | double preAcosClamp(double val, const double minval, const double maxval) 81 | { 82 | if (val < minval) { 83 | if ( (minval-val) > 1.0) { qDebug("gross clamping error"); abort(); } 84 | val = minval; 85 | } 86 | else if (val > maxval) { 87 | if ( (val - maxval) > 1.0) { qDebug("gross clamping error"); abort(); } 88 | val = maxval; 89 | } 90 | return val; 91 | } 92 | -------------------------------------------------------------------------------- /UiEventWidget.h: -------------------------------------------------------------------------------- 1 | #ifndef SHOTSELWIDGET_H 2 | #define SHOTSELWIDGET_H 3 | 4 | #include 5 | #include "CameraModel.h" 6 | class Osg3dSSAOView; 7 | 8 | class UiEventWidget : public QWidget 9 | { 10 | Q_OBJECT 11 | public: 12 | enum MouseMode { 13 | MM_ORBIT = 1<<0, 14 | MM_PAN = 1<<1, 15 | MM_ZOOM = 1<<2 , 16 | MM_FLY = 1<<3, 17 | MM_ROTATE = 1<<4, 18 | MM_PICK_CENTER = 1<<5, 19 | MM_PICK_POINT = 1<<6, 20 | MM_SELECTOBJECT = 1<<7, 21 | MM_SHOOT = 1<<8, 22 | MM_MOVE = 1<<9, 23 | MM_CUTTINGPLANE = 1<<10, 24 | MM_NONE = 0 25 | }; 26 | explicit UiEventWidget(QWidget *parent = 0); 27 | MouseMode mouseMode() const { return m_mouseMode; } 28 | bool autoFocus() const { return m_shouldAutoFocus; } 29 | Osg3dSSAOView *ssaoView() const { return m_ssaoWidget; } 30 | 31 | signals: 32 | void handleCloseEvent(QCloseEvent *event); 33 | void startOrbit(osg::Vec2d); 34 | void orbit(osg::Vec2d); 35 | void finishOrbit(osg::Vec2d); 36 | void startRotate(osg::Vec2d); 37 | void rotate(osg::Vec2d); 38 | void finishRotate(osg::Vec2d); 39 | 40 | void startPan(osg::Vec2d, osg::Vec4d); 41 | void pan(osg::Vec2d); 42 | void finishPan(osg::Vec2d); 43 | 44 | void startZoom(osg::Vec2d); 45 | void zoom(osg::Vec2d); 46 | void finishZoom(osg::Vec2d); 47 | 48 | void startDolly(osg::Vec2d); 49 | /** Dolly the camera forwards and backwards. Changes the view distance. 50 | This function is a no-op for orthographic projections. */ 51 | void dolly(double); 52 | void dolly(osg::Vec2d); 53 | void finishDolly(osg::Vec2d); 54 | 55 | void pickaPoint(osg::Vec2d, int, int); 56 | void pickCenter(int, int); 57 | void shoot(int, int); 58 | 59 | void shotShift(Qt::Key); 60 | void shotRotate(Qt::Key); 61 | 62 | void clipPlaneShift(Qt::Key); 63 | void clipPlaneRotate(Qt::Key); 64 | 65 | public slots: 66 | void closeEvent(QCloseEvent *event) { emit handleCloseEvent(event); } 67 | 68 | virtual void enterEvent(QEvent *event); 69 | virtual void leaveEvent(QEvent *event); 70 | virtual void keyPressEvent(QKeyEvent *event); 71 | virtual void keyReleaseEvent(QKeyEvent *event); 72 | 73 | virtual void mousePressEvent(QMouseEvent* event) override; 74 | virtual void mouseReleaseEvent(QMouseEvent* event) override; 75 | virtual void mouseMoveEvent(QMouseEvent* event) override; 76 | virtual void wheelEvent(QWheelEvent *event) override; 77 | 78 | void setMouseMode(MouseMode mode); 79 | void setAutoFocus(bool tf) { m_shouldAutoFocus = tf; } 80 | 81 | private: 82 | void shootKeyEvent(QKeyEvent *event); 83 | void clipKeyEvent(QKeyEvent *event); 84 | MouseMode m_mouseMode; 85 | 86 | /// since we always want the 3D view to have focus when entered 87 | /// it is polite to remember who had the focus when we stole it 88 | /// and restore it when the cursor leaves the window 89 | QWidget *m_lastFocused; 90 | bool m_shouldAutoFocus; 91 | 92 | Osg3dSSAOView *m_ssaoWidget; 93 | void startPanning(osg::Vec2d ndc, int x, int y); 94 | }; 95 | 96 | #endif // SHOTSELWIDGET_H 97 | -------------------------------------------------------------------------------- /blur.fp: -------------------------------------------------------------------------------- 1 | #version 120 2 | 3 | uniform sampler2D sceneTex; 4 | uniform sampler2D linearDepthTexture; 5 | uniform int uBlurSize; // use size of noise texture 6 | uniform vec2 sceneSize; 7 | 8 | uniform mat4 projMatrix; 9 | 10 | uniform int displayType = 0; 11 | uniform int blurAOEnable = 0; 12 | uniform int haloRemoval = 0; 13 | uniform float haloTreshold = 0; 14 | 15 | float reconstruct_z(in float depth, in mat4 projMatrix){ 16 | return -projMatrix[3][2] / (depth + projMatrix[2][2]); 17 | } 18 | 19 | float blurAOHaloRemoval() 20 | { 21 | vec2 texelSize = 1.0 / vec2(sceneSize); 22 | float result = 0.0; 23 | vec2 hlim = vec2(float(-uBlurSize) * 0.5 + 0.5); 24 | float depth = reconstruct_z(texture2D(linearDepthTexture, gl_TexCoord[0].st).r, projMatrix); 25 | 26 | float totalWeight = float(uBlurSize * uBlurSize); 27 | float pixelWeight = 1.0f; 28 | 29 | for (int i = 0; i < uBlurSize; ++i) { 30 | for (int j = 0; j < uBlurSize; ++j) { 31 | vec2 offset = (hlim + vec2(float(i), float(j))) * texelSize; 32 | float offsetDepth = reconstruct_z(texture2D(linearDepthTexture, gl_TexCoord[0].st + offset).r, projMatrix); 33 | 34 | if (abs(offsetDepth - depth) > haloTreshold) { 35 | totalWeight -= pixelWeight; 36 | continue; 37 | } 38 | 39 | result += texture2D(sceneTex, gl_TexCoord[0].st + offset).a; 40 | } 41 | } 42 | 43 | // This is blurred occlusion factor 44 | float occlusionFactor = result / totalWeight; 45 | 46 | return occlusionFactor; 47 | } 48 | 49 | float blurAO() 50 | { 51 | vec2 texelSize = 1.0 / vec2(sceneSize); 52 | float result = 0.0; 53 | vec2 hlim = vec2(float(-uBlurSize) * 0.5 + 0.5); 54 | for (int i = 0; i < uBlurSize; ++i) { 55 | for (int j = 0; j < uBlurSize; ++j) { 56 | vec2 offset = (hlim + vec2(float(i), float(j))) * texelSize; 57 | result += texture2D(sceneTex, gl_TexCoord[0].st + offset).a; 58 | } 59 | } 60 | 61 | // This is blurred occlusion factor 62 | float occlusionFactor = (result / float(uBlurSize * uBlurSize)); 63 | 64 | return occlusionFactor; 65 | } 66 | 67 | float AO() 68 | { 69 | return texture2D(sceneTex, gl_TexCoord[0].st).a; 70 | } 71 | 72 | vec3 Color() 73 | { 74 | return texture2D(sceneTex, gl_TexCoord[0].st).rgb; 75 | } 76 | 77 | void main(void) 78 | { 79 | float occlusion; 80 | vec3 color = Color(); 81 | vec3 resultColor; 82 | 83 | if (displayType == 1) { 84 | if (haloRemoval == 1) { 85 | if (blurAOEnable == 1) { 86 | resultColor = color * blurAOHaloRemoval(); 87 | } else { 88 | resultColor = color * AO(); 89 | } 90 | } else { 91 | if (blurAOEnable == 1) { 92 | resultColor = color * blurAO(); 93 | } else { 94 | resultColor = color * AO(); 95 | } 96 | } 97 | } else if(displayType == 2) { 98 | if (haloRemoval == 1) { 99 | if (blurAOEnable == 1) { 100 | resultColor = vec3(blurAOHaloRemoval()); 101 | } else { 102 | resultColor = vec3(AO()); 103 | } 104 | } else { 105 | if (blurAOEnable == 1) { 106 | resultColor = vec3(blurAO()); 107 | } else { 108 | resultColor = vec3(AO()); 109 | } 110 | } 111 | } else { 112 | resultColor = color; 113 | } 114 | 115 | gl_FragColor = vec4(resultColor, 1); 116 | } 117 | -------------------------------------------------------------------------------- /Osg3dViewWithCamera.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "CameraModel.h" 15 | 16 | 17 | /// 18 | /// \brief The Osg3dViewBase class 19 | /// 20 | /// This inherits from QOpenGLWidget which renders to a texture and can be 21 | /// easily unit tested, whereas the (deprecated) QGLWidget does not and is 22 | /// not reliably unit-testable. 23 | class Osg3dViewWithCamera : 24 | public QOpenGLWidget, 25 | protected QOpenGLFunctions, 26 | public osgViewer::Viewer 27 | { 28 | Q_OBJECT 29 | public: 30 | Osg3dViewWithCamera(QWidget *parent=nullptr); 31 | ~Osg3dViewWithCamera(); 32 | 33 | unsigned getCameraMask() const { return getCamera()->getCullMask(); } 34 | 35 | const QString & getGlInfo() const { return m_glInfo; } 36 | 37 | /// Get the Normalized Device Coordinates for a given X,Y pixel location 38 | osg::Vec2d getNormalizedDeviceCoords(const int ix, const int iy); 39 | 40 | 41 | osg::ref_ptr cameraModel() const { return m_cameraModel; } 42 | void setCameraModel(osg::ref_ptr cameraModel); 43 | osg::ref_ptr 44 | intersectUnderCursor(const int x, const int y, unsigned mask=~0); 45 | public slots: 46 | 47 | /// Let others tell what scene graph we should be drawing 48 | void addNode(osg::Node *n); 49 | void removeNode(osg::Node *n); 50 | void clearNodes(); 51 | 52 | /// OSG uses singleSided drawing/display by default. 53 | /// This is annoying when you are "inside" something and the back wall of 54 | /// it simply disappears. The constructor calls this to set up to 55 | /// draw both front and back facing polygons so that the world behaves 56 | /// in a more normal fashion. 57 | /// Yes it's a performance hit. 58 | void setLightingTwoSided(bool tf=true); 59 | 60 | // These support changing the display between drawing in FILL/Line/Point 61 | // mode and setting the line/point size when in those modes 62 | void setDrawMode(osg::PolygonMode::Mode drawMode); 63 | void setDrawModeFromQAction(); 64 | void setLineWidth(int size); 65 | 66 | 67 | signals: 68 | void updated(); 69 | void lineWidthChanged(int); 70 | void drawModeChanged(osg::PolygonMode::Mode drawMode); 71 | 72 | protected: // These are from QOpenGLWidget. There are implementations here, 73 | // but they can be overridden 74 | 75 | /// Render one frame 76 | virtual void paintGL() override; 77 | 78 | /// Updates OpenGL viewport when window size changes 79 | virtual void resizeGL( int width, int height ) override; 80 | 81 | virtual void initializeGL() override; 82 | 83 | 84 | 85 | /// This is the scene model that will be drawn. Callers can add/remove 86 | /// items to/from this with addNode() removeNode() and clearNodes(). 87 | /// This allows per-widget drawing modes when sharing a scene graph 88 | /// across multiple widgets/osgViewers. 89 | osg::ref_ptr< osg::Group > m_scene; 90 | 91 | /// This is the root of the everything that will be drawn. It allows 92 | /// per-widget elements and drawing modes. This is a switch so that 93 | /// it is easy to toggle between different rendering mechanisms. 94 | osg::ref_ptr< osg::Switch > m_root; 95 | 96 | /// OSG graphics window 97 | osg::ref_ptr m_osgGraphicsWindow; 98 | 99 | //Variables for controlling the size of points and lines 100 | osg::ref_ptr m_point; 101 | osg::ref_ptr m_lineWidth; 102 | float m_currentLineWidth; 103 | 104 | /// CameraModel --> controls the camera of the osgViewer 105 | osg::ref_ptr< CameraModel > m_cameraModel; 106 | 107 | QString m_glInfo; 108 | QList m_menus; 109 | 110 | }; 111 | 112 | -------------------------------------------------------------------------------- /OSGWidget.cpp: -------------------------------------------------------------------------------- 1 | #include "OSGWidget.h" 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | OSGWidget::OSGWidget(QWidget *parent) 15 | : QGLWidget(parent) 16 | , m_ssao(new SSAONode(width(), height())) 17 | , m_root(new osg::Switch) 18 | , m_scene(new osg::Group) 19 | , m_cameraModel(new CameraModel) 20 | { 21 | // Strong focus policy needs to be set to capture keyboard events 22 | setFocusPolicy(Qt::StrongFocus); 23 | 24 | // Construct the embedded graphics window 25 | m_osgGraphicsWindow = new osgViewer::GraphicsWindowEmbedded(0,0,width(),height()); 26 | getCamera()->setGraphicsContext(m_osgGraphicsWindow); 27 | 28 | // Set up the camera 29 | getCamera()->setViewport(new osg::Viewport(0,0,width(),height())); 30 | 31 | // By default draw everthing that has even 1 bit set in the nodemask 32 | getCamera()->setCullMask( (unsigned)~0 ); 33 | getCamera()->setDataVariance(osg::Object::DYNAMIC); 34 | 35 | // As of July 2010 there wasn't really a good way to multi-thread OSG 36 | // under Qt so just set the threading model to be SingleThreaded 37 | setThreadingModel(osgViewer::Viewer::SingleThreaded); 38 | 39 | // draw both sides of polygons 40 | setLightingTwoSided(); 41 | 42 | // Set the minimum size for this viewer window 43 | setMinimumSize(64, 64); 44 | 45 | 46 | SSAONode::buildGraph(m_root, m_scene, m_ssao); 47 | 48 | this->setSSAOEnabled(true); 49 | 50 | this->setSceneData(m_root); 51 | 52 | m_cameraModel->setBoundingNode(m_scene); 53 | m_cameraModel->computeInitialView(); 54 | connect(m_cameraModel, SIGNAL(changed()), this, SLOT(update())); 55 | } 56 | 57 | 58 | void OSGWidget::setSSAOEnabled(bool tf) 59 | { 60 | SSAONode::setSSAOEnabled(m_root, m_scene, m_ssao, tf); 61 | 62 | update(); 63 | } 64 | 65 | void OSGWidget::setSSAODisplayMode(SSAONode::DisplayMode mode) 66 | { 67 | m_ssao->SetDisplayMode(mode); 68 | update(); 69 | } 70 | 71 | void OSGWidget::paintGL() 72 | { 73 | // Update the camera 74 | osg::Camera *cam = this->getCamera(); 75 | #ifdef VIEWINGCORE 76 | const osg::Viewport* vp = cam->getViewport(); 77 | 78 | m_viewingCore->setAspect(vp->width() / vp->height()); 79 | cam->setViewMatrix(m_viewingCore->getInverseMatrix()); 80 | cam->setProjectionMatrix(m_viewingCore->computeProjection()); 81 | 82 | #else 83 | 84 | cam->setCullMask( m_cameraModel->cullMask() ); 85 | 86 | //m_cameraModel->setAspect((double)width() / (double)height()); 87 | 88 | cam->setViewMatrix(m_cameraModel->getModelViewMatrix() ); 89 | cam->setProjectionMatrix(m_cameraModel->computeProjection()); 90 | #endif 91 | // Let SSAO class know that camera has changed 92 | 93 | m_ssao->updateProjectionMatrix(getCamera()->getProjectionMatrix()); 94 | 95 | // Invoke the OSG traversal pipeline 96 | frame(); 97 | 98 | // Start the timer again 99 | //m_redrawTimer.start(); 100 | } 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | void OSGWidget::setScene(osg::Node *scene) 110 | { 111 | m_scene->removeChildren(0, m_scene->getNumChildren()); 112 | m_scene->addChild(scene); 113 | 114 | // m_viewingCore->setSceneData(m_scene); // redundant after first 115 | // m_viewingCore->fitToScreen(); 116 | } 117 | 118 | void OSGWidget::setLightingTwoSided() 119 | { 120 | osg::ref_ptr lm = new osg::LightModel; 121 | lm->setTwoSided(true); 122 | lm->setAmbientIntensity(osg::Vec4(0.1f,0.1f,0.1f,1.0f)); 123 | 124 | osg::StateSet *ss; 125 | 126 | for (int i=0 ; i < 2 ; i++ ) { 127 | ss = ((osgViewer::Renderer *)getCamera() 128 | ->getRenderer())->getSceneView(i)->getGlobalStateSet(); 129 | 130 | ss->setAttributeAndModes(lm, osg::StateAttribute::ON); 131 | } 132 | } 133 | 134 | void OSGWidget::resizeGL(int width, int height) 135 | { 136 | m_ssao->Resize(width, height); 137 | m_osgGraphicsWindow->resized(0,0,width,height); 138 | } 139 | 140 | 141 | void OSGWidget::setCameraModel(osg::ref_ptr cameraModel) 142 | { 143 | disconnect(m_cameraModel, SIGNAL(changed()), this, SLOT(update())); 144 | m_cameraModel = cameraModel; 145 | connect(m_cameraModel, SIGNAL(changed()), this, SLOT(update())); 146 | update(); 147 | } 148 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project( ssao ) 2 | cmake_minimum_required(VERSION 3.2) 3 | message(STATUS ${CMAKE_CURRENT_SOURCE_DIR}) 4 | 5 | # Allow all sub CMakeLists to use our custom distributed CMake modules 6 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} 7 | "${CMAKE_SOURCE_DIR}/misc/CMake" 8 | "${CMAKE_SOURCE_DIR}/misc/CMake/compiler" 9 | ) 10 | SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) 11 | 12 | # target build locations 13 | 14 | find_package(OpenSceneGraph 3.4.0 COMPONENTS 15 | osg osgDB osgViewer osgGA osgUtil OpenThreads ) 16 | if (OPENSCENEGRAPH_FOUND) 17 | include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}) 18 | else (OPENSCENEGRAPH_FOUND) 19 | message(STATUS "----- You probably want to get OpenSceneGraph set up properly ------") 20 | endif (OPENSCENEGRAPH_FOUND) 21 | 22 | # if cmake cannot find Qt you can set Qt5_DIR or uncomment next line 23 | # set(Qt5_DIR "C:/Qt/5.5/msvc2013_64/lib/cmake/Qt5" CACHE PATH "Qt cmake dir") 24 | find_package(Qt5 COMPONENTS Core Gui Widgets OpenGL REQUIRED) 25 | 26 | # make all sources part of the build 27 | file(GLOB product_SRCS 28 | "${CMAKE_CURRENT_LIST_DIR}/*cpp" 29 | "${CMAKE_CURRENT_LIST_DIR}/*h" ) 30 | 31 | # Make all *.ui files part of the build 32 | file(GLOB product_UIS "${CMAKE_CURRENT_LIST_DIR}/*ui") 33 | 34 | # Make all resource (.qrc) part of the build 35 | file(GLOB product_RCS "${CMAKE_CURRENT_LIST_DIR}/*.qrc") 36 | 37 | # necessary to pick up uic headers 38 | include_directories(${PROJECT_BINARY_DIR}) 39 | 40 | include_directories("${CMAKE_SOURCE_DIR}/src/lib/applib") 41 | 42 | # Qt headers 43 | include_directories( 44 | ${Qt5Core_INCLUDE_DIRS} 45 | ${Qt5Gui_INCLUDE_DIRS} 46 | ${Qt5Widgets_INCLUDE_DIRS} 47 | ${Qt5OpenGL_INCLUDE_DIRS} 48 | ) 49 | 50 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 51 | set(CMAKE_AUTOMOC ON) 52 | set(CMAKE_AUTORCC ON) 53 | 54 | qt5_wrap_ui(UIS ${product_UIS}) 55 | qt5_add_resources( RCS ${product_RCS}) 56 | 57 | # the list of dependencies is needed both for linking 58 | # and for the install step. So make the list once 59 | # so we are sure to use the same list in both places 60 | set(product_LIBS 61 | Qt5::Core 62 | Qt5::Gui 63 | Qt5::Widgets 64 | Qt5::OpenGL 65 | ${OPENSCENEGRAPH_LIBRARIES} 66 | ) 67 | 68 | set(EXEC_NAME "ssao") 69 | add_executable(${EXEC_NAME} ${product_SRCS} ${UIS} ${RCS} ) 70 | target_link_libraries(${EXEC_NAME} ${product_LIBS}) 71 | 72 | install(TARGETS ${EXEC_NAME} 73 | BUNDLE DESTINATION . COMPONENT Runtime 74 | RUNTIME DESTINATION bin COMPONENT Runtime ) 75 | 76 | # 77 | # gather up dependencies for installation. This works as follows: 78 | # 79 | # INSTALL(CODE CMake code to have build system run at install time 80 | # " begin content of install-time CMake code 81 | # include(BundleUtilities) load definition of fixup_bundle 82 | # fixup_bundle( executable pluginpaths librarypaths ) 83 | # 84 | # executable: executable to be examined for library dependencies. 85 | # ---------- WARNING ------------------------------------------------------- 86 | # CPack runs CMake on the code provided when you make the PACKAGE target 87 | # (eg. % make PACKAGE). IT USES A DIFFERENT INSTALL DIRECTORY THAN THE ONE 88 | # YOU PROVIDE AT CMAKE TIME. Therefore you want to defer evaluation of the 89 | # variable CMAKE_INSTALL_PREFIX to CPack (or make PACKAGE) time to get the 90 | # right value. On the other hand ${EXEC_NAME} wants to be evaluated at 91 | # at the original CMake time. 92 | # 93 | # pluginpaths: It is ok to evaluate $ENV{PATH} at the original CMake time. 94 | # You probably want to create a list you can append other paths to. 95 | # Converting to CMAKE path separators seems to be a good idea to prevent 96 | # Windows back-slashes from being mis-interpreted as escape chars. 97 | # 98 | # librarypaths: It is ok to evaluate $ENV{PATH} at the original CMake time. 99 | # You probably want to create a list you can append other paths to. 100 | # Converting to CMAKE path separators seems to be a good idea to prevent 101 | # Windows back-slashes from being mis-interpreted as escape chars. 102 | # 103 | file(TO_CMAKE_PATH "$ENV{PATH}" DIRS) # directories to search for prerequisites 104 | # optional: list(append DIRS "anotherDir") 105 | 106 | INSTALL(CODE "FILE(COPY \"${OSG_INCLUDE_DIR}/../bin/osgPlugins-3.4.0\" DESTINATION \"\${CMAKE_INSTALL_PREFIX}/bin\") 107 | FILE(GLOB QTPLUGINS \"${Qt5_DIR}/../../../plugins/*\") 108 | FILE(COPY \${QTPLUGINS} DESTINATION \"\${CMAKE_INSTALL_PREFIX}/bin\") 109 | FILE(COPY \"${Qt5_DIR}/../../../bin/libEGL.dll\" DESTINATION \"\${CMAKE_INSTALL_PREFIX}/bin\") 110 | file(GLOB_RECURSE VSLPLUGINS \"\${CMAKE_INSTALL_PREFIX}/bin/plugins/*${CMAKE_SHARED_LIBRARY_SUFFIX}\") 111 | include(BundleUtilities) 112 | fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/bin/${EXEC_NAME}${CMAKE_EXECUTABLE_SUFFIX}\" \"\${VSLPLUGINS}\" \"${DIRS}\") 113 | " COMPONENT Runtime) 114 | -------------------------------------------------------------------------------- /NodeMask.cpp: -------------------------------------------------------------------------------- 1 | #include "NodeMask.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | //static const int bitsForDisplayMask = 12; 8 | static const unsigned defaultMask = 0x0fff; 9 | 10 | const char * const NodeMask::yadda = "hello"; 11 | 12 | const char * const NodeMask::bitNames[] = { 13 | "Unselected", // 0 14 | "Selected", // 1 15 | "Hidden", // 2 16 | "", // 3 17 | "", // 4 18 | "", // 5 19 | "", // 6 20 | "Shotline", // 7 21 | "Group", // 8 22 | "Axis", // 9 23 | "CuttingPlane", // 10 24 | "PointIndicator"// 11 25 | }; 26 | class GroupLeafNodeSetVisitor : public osg::NodeVisitor 27 | { 28 | public: 29 | GroupLeafNodeSetVisitor(unsigned int mask=defaultMask, 30 | TraversalMode tm=TRAVERSE_ALL_CHILDREN) 31 | : NodeVisitor(tm) 32 | , m_mask(mask) 33 | { // force traversal of all nodes 34 | _traversalMask = _nodeMaskOverride = ~0; 35 | } 36 | virtual void apply (osg::Group &node) { 37 | node.setNodeMask(NodeMask::GROUP|m_mask); 38 | traverse(node); 39 | } 40 | virtual void apply (osg::Drawable &node) { 41 | node.setNodeMask(m_mask); 42 | this->traverse(node); 43 | } 44 | private: 45 | unsigned m_mask; 46 | }; 47 | 48 | /// Assign a nodemask to nodes 49 | /// This overrides any nodeMask that has been set entirely 50 | class NodeMaskSetVisitor : public osg::NodeVisitor 51 | { 52 | public: 53 | NodeMaskSetVisitor(unsigned int mask=defaultMask, 54 | TraversalMode tm=TRAVERSE_ALL_CHILDREN) 55 | : NodeVisitor(tm) 56 | , m_mask(mask) 57 | { // force traversal of all nodes 58 | _traversalMask = _nodeMaskOverride = ~0; 59 | } 60 | 61 | virtual void apply (osg::Node &node) { 62 | node.setNodeMask(m_mask); 63 | this->traverse(node); 64 | } 65 | private: 66 | unsigned m_mask; 67 | }; 68 | 69 | 70 | /// Set individual bits of the NodeMask in a tree by doing an OR with the 71 | /// provided bitmask. This is usefull for turning on additional bits in 72 | /// the nodemask 73 | class NodeMaskOrBitVisitor : public osg::NodeVisitor 74 | { 75 | public: 76 | NodeMaskOrBitVisitor(unsigned int mask=defaultMask, 77 | TraversalMode tm=TRAVERSE_ALL_CHILDREN) 78 | : NodeVisitor(tm) 79 | , m_mask(mask) 80 | { // force traversal of all nodes 81 | _traversalMask = _nodeMaskOverride = ~0; 82 | } 83 | 84 | virtual void apply (osg::Node &node) { 85 | this->traverse(node); 86 | unsigned int before = node.getNodeMask(); 87 | unsigned int after = before | m_mask; 88 | node.setNodeMask(after); 89 | } 90 | private: 91 | unsigned m_mask; 92 | }; 93 | 94 | 95 | /// Clear individual bits of the nodemask in a tree by doing 96 | /// and AND with the inverse of the bits set in the mask provided. 97 | class NodeMaskAndBitVisitor : public osg::NodeVisitor 98 | { 99 | public: 100 | NodeMaskAndBitVisitor(unsigned int mask=defaultMask, 101 | TraversalMode tm=TRAVERSE_ALL_CHILDREN) 102 | : NodeVisitor(tm) 103 | , m_mask(mask) 104 | { // force traversal of all nodes 105 | _traversalMask = _nodeMaskOverride = ~0; 106 | } 107 | 108 | virtual void apply (osg::Node &node) { 109 | this->traverse(node); 110 | unsigned int before = node.getNodeMask(); 111 | unsigned int after = before & m_mask; 112 | node.setNodeMask(after); 113 | } 114 | private: 115 | unsigned m_mask; 116 | }; 117 | 118 | QString NodeMask::maskToString(unsigned mask) 119 | { 120 | std::bitset bits(mask); 121 | QString s = QString::fromStdString(bits.to_string()); 122 | 123 | s.insert(8, ' '); 124 | s.insert(4, ' '); 125 | return s; 126 | } 127 | 128 | unsigned NodeMask::stringToMask(QString s) 129 | { 130 | s.remove(' '); 131 | std::bitset bits(s.toStdString()); 132 | return (unsigned)bits.to_ulong(); 133 | } 134 | 135 | void NodeMask::setNodeMasksOnHeirarchy(osg::Node *n, NodeMaskValue mask) 136 | { 137 | NodeMaskSetVisitor nmsv(mask); 138 | 139 | n->accept(nmsv); 140 | } 141 | 142 | void NodeMask::setNodeMasksBitOnHeirarchy(osg::Node *n, NodeMaskValue mask) 143 | { 144 | unsigned settingMask = mask & 0x0ff; 145 | 146 | NodeMaskOrBitVisitor nmbsv(settingMask); 147 | n->accept(nmbsv); 148 | } 149 | 150 | void NodeMask::clearNodeMasksBitOnHeirarchy(osg::Node *n, NodeMaskValue mask) 151 | { 152 | unsigned clearingMask = ~(mask & 0x0ff); 153 | 154 | NodeMaskAndBitVisitor nmcbv(clearingMask); 155 | n->accept(nmcbv); 156 | } 157 | 158 | void NodeMask::markGroups(osg::Node *n) 159 | { 160 | GroupLeafNodeSetVisitor gv; 161 | n->accept(gv); 162 | } 163 | 164 | void NodeMask::markGroupsAndLeafNodes(osg::Node *n, NodeMask::NodeMaskValue mask) 165 | { 166 | GroupLeafNodeSetVisitor gv(mask); 167 | n->accept(gv); 168 | } 169 | 170 | #include 171 | QRegExpValidator *NodeMask::createValidator() 172 | { 173 | return new QRegExpValidator(QRegExp("([01]* )*[01]+")); 174 | } 175 | -------------------------------------------------------------------------------- /SSAONode.h: -------------------------------------------------------------------------------- 1 | // Utility functions for SSAO implementation 2 | 3 | #ifndef SSAO_H 4 | #define SSAO_H 5 | 6 | #include "osg/Vec3f" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | class SSAONode : public osg::Group { 18 | public: 19 | enum DisplayMode { 20 | SSAO_AOOnly = 2, 21 | SSAO_ColorAndAO = 1, 22 | SSAO_ColorOnly = 0 23 | }; 24 | SSAONode(int m_width, 25 | int m_height, 26 | int m_kernelSize = 8, // 4 = good performance, 10 = good quality 27 | int m_noiseSize = 2, // should be same as blurSize 28 | int m_blurSize = 2, // 2-4, performance goes to hell above 4 29 | float radius = 0.65f, 30 | float power = 3.0f); 31 | ~SSAONode(); 32 | 33 | // Initialize SSAO for given node. New Group will be returned that you should add to your scene 34 | void Initialize(); 35 | 36 | static void buildGraph(osg::ref_ptr root, 37 | osg::ref_ptr scene, 38 | osg::ref_ptr ssao) 39 | { 40 | root->setNewChildDefaultValue(true); 41 | root->addChild(scene); 42 | 43 | ssao->addNode(scene); 44 | 45 | root->addChild(ssao); 46 | root->setValue(root->getChildIndex(ssao), false ); 47 | } 48 | 49 | static void setSSAOEnabled(osg::ref_ptr root, 50 | osg::ref_ptr scene, 51 | osg::ref_ptr ssao, 52 | bool tf) 53 | { 54 | int ssaoIndex = root->getChildIndex(ssao); 55 | int sceneIndex = root->getChildIndex(scene); 56 | 57 | root->setValue(ssaoIndex, tf); 58 | root->setValue(sceneIndex, !tf); 59 | } 60 | 61 | 62 | 63 | void SetSSAORadius(float radius); 64 | float GetSSAORadius(); 65 | void SetSSAOPower(float power); 66 | float GetSSAOPower(); 67 | 68 | void SetDisplayMode(SSAONode::DisplayMode mode); 69 | DisplayMode GetDisplayMode(); 70 | 71 | void setHaloRemovalEnabled(bool tf) { m_haloRemovalEnabled = tf; } 72 | bool IsHaloRemovalEnabled(); 73 | void setAOBlurEnabled(bool tf) { m_blurAOEnabled = tf; } 74 | bool IsAOBlurEnabled(); 75 | 76 | void SetHaloTreshold(float treshold); 77 | float GetHaloTreshold(); 78 | 79 | void updateProjectionMatrix(osg::Matrixd projMatrix); 80 | 81 | void addNode(osg::Node* node); 82 | 83 | void Resize(int m_width, int m_height); 84 | 85 | private: 86 | 87 | // Effect settings 88 | int m_kernelSize; // 4 = good performance, 10 = good quality 89 | int m_noiseSize; // should be same as blurSize 90 | int m_blurSize; // make this 2 to 4. Performance goes to hell above 4 91 | float m_ssaoRadius; 92 | float m_ssaoPower; 93 | 94 | bool m_blurAOEnabled; 95 | bool m_haloRemovalEnabled; 96 | float m_haloTreshold; 97 | 98 | int m_width; 99 | int m_height; 100 | 101 | osg::Vec3f* m_kernelData; 102 | osg::Vec3f* m_noiseData; 103 | 104 | osg::ref_ptr rttCamera; 105 | osg::ref_ptr ssaoCamera; 106 | osg::ref_ptr blurCamera; 107 | osg::Matrixd projMatrix; 108 | 109 | DisplayMode displayType; 110 | 111 | // Shader uniforms 112 | osg::Uniform* projMatUniform; 113 | osg::Uniform* invProjMatrixUniform; 114 | osg::Uniform* radiusUniform; 115 | osg::Uniform* powerUniform; 116 | osg::Uniform* displayTypeUniform; 117 | osg::Uniform* noiseTextureRcpUniform; 118 | osg::Uniform* haloRemovalUniform; 119 | osg::Uniform* haloTresholdUniform; 120 | osg::Uniform* blurProjMatrixUniform; 121 | osg::Uniform* blurAOUniform; 122 | osg::Uniform* sceneSizeUniform; 123 | 124 | void setUniforms(); 125 | 126 | // G Buffer 127 | osg::ref_ptr colorTex; 128 | osg::ref_ptr linearDepthTex; 129 | osg::ref_ptr normalTex; 130 | osg::ref_ptr secondPassTex; 131 | 132 | // Math utils - possibly replace with calls to some math library 133 | unsigned int xorshift32(); 134 | float random(float min, float max); 135 | float lerp(float min, float max, float t); 136 | 137 | // SSAO data generation 138 | void generateHemisphereSamples(osg::Vec3f* kernel, size_t kernelSize); 139 | void generateNoise(osg::Vec3f* noise, size_t m_noiseSize); 140 | 141 | osg::StateSet* phongState; 142 | 143 | // OSG Utils 144 | bool setShaderStringFromResource(osg::Shader* shader, 145 | const std::string resourceName); 146 | 147 | osg::Camera* createRTTCamera(osg::Camera::BufferComponent buffer, osg::Texture* tex, bool isAbsolute); 148 | osg::Camera* createRTTCameraGBuffer(osg::Camera::BufferComponent buffer1, osg::Texture* tex1, osg::Camera::BufferComponent buffer2, osg::Texture* tex2, osg::Camera::BufferComponent buffer3, osg::Texture* tex3, bool isAbsolute); 149 | osg::Texture* createTexture2D(int m_width, int m_height, osg::Vec3f* data); 150 | osg::Geode* createScreenQuad(float m_width, float m_height, float scale = 1.0f); 151 | osg::Camera* createHUDCamera(double left, double right, double bottom, double top); 152 | 153 | std::string stringFromResource(const char *resourceName); 154 | void addKernelUniformToStateSet(osg::StateSet *stateset, int kernelLength); 155 | void removeAttachedCameras(); 156 | void createFirstPassCamera(); 157 | void createSecondPassCamera(int kernelLength); 158 | void createThirdPassCamera(); 159 | void setProjectionMatrixUniforms(); 160 | }; 161 | 162 | #endif // SSAO_H 163 | -------------------------------------------------------------------------------- /CameraModel.h: -------------------------------------------------------------------------------- 1 | #ifndef CAMERAMODEL_H 2 | #define CAMERAMODEL_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class CameraModel : public QObject, public osg::Object 11 | { 12 | Q_OBJECT 13 | public: 14 | explicit CameraModel(QObject *parent = 0); 15 | CameraModel( const CameraModel& rhs, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY ); 16 | 17 | META_Object(osgwTools,CameraModel) 18 | 19 | // simple accessors /////////////////////////////////////////////////////// 20 | 21 | osg::Vec3d viewUp() const { return m_viewUp; } 22 | osg::Vec3d viewDir() const { return m_viewDir; } 23 | osg::Vec3d viewCenter() const { return m_viewCenter; } 24 | double fovY() const { return m_fovY; } 25 | double viewDistance() const { return m_viewDistance; } 26 | double aspect() const { return m_aspect; } 27 | double trackballRollSensitivity() const { return m_trackballRollSensitivity; } 28 | bool isOrtho() const { return m_ortho; } 29 | double orthoTop() const { return m_orthoTop; } 30 | double orthoBottom() const { return m_orthoBottom; } 31 | bool viewChangeInProgress() const { return m_viewChangeInProgress; } 32 | bool dollyCanChangeCenter() const { return m_dollyCanChangeCenter; } 33 | int dollyCenterChangeThreshold() const { return m_dollyCenterChangeThreshold; } 34 | int dollyCenterPressure() const { return m_dollyCurrentPressure; } 35 | unsigned cullMask() const { return m_cullMask; } 36 | 37 | // derive other representations of member variables //////////////////// 38 | osg::Matrixd computeProjection() const; 39 | osg::Matrixd getModelViewMatrix() const; 40 | osg::Matrixd getMatrix() const; 41 | osg::Vec3d getEyePosition() const; 42 | 43 | double getFovyRadians() const; 44 | osg::Vec3d getAzElTwist() const; 45 | osg::Vec3d getYawPitchRoll() const; 46 | 47 | signals: 48 | void changed(); 49 | void cullMaskChanged(unsigned); 50 | 51 | public slots: 52 | void computeInitialView(); 53 | void fitToScreen(); 54 | 55 | void setUpAndDir(osg::Vec3d up, osg::Vec3d dir); 56 | void setViewUp(osg::Vec3d v); 57 | void setViewDir(osg::Vec3d v); 58 | void setViewCenter(osg::Vec3d newCenter); 59 | void setFovY(double fov) { m_fovY = fov; emit changed(); } 60 | void setViewDistance(double distance); 61 | void setEyePosition(osg::Vec3d p); 62 | void setAspect(double a); 63 | void setOrtho(bool tf); 64 | void fovYScaleUp(); 65 | void fovYScaleDown(); 66 | 67 | 68 | void setClampFovyScale(bool clamp, osg::Vec2d range); 69 | void setViewDirFromAzEl(osg::Vec2d aet); 70 | void saveView(std::stringstream &stream); 71 | void loadView(std::stringstream &stream); 72 | void setTrackballRollSensitivity(double s) { m_trackballRollSensitivity=s; } 73 | void setOrthoFromQAction(); 74 | 75 | void stashView(); 76 | void restoreView(); 77 | 78 | // Manipulation operations /////////////////////////////////////////////// 79 | 80 | void startOrbit(osg::Vec2d startingNDC); 81 | void orbit(osg::Vec2d currentNDC); 82 | void finishOrbit(osg::Vec2d currentNDC); 83 | void finishOrbit() {finishOrbit(m_startingNDC);} 84 | 85 | void startRotate(osg::Vec2d startingNDC); 86 | void rotate(osg::Vec2d currentNDC); 87 | void finishRotate(osg::Vec2d currentNDC); 88 | void finishRotate() { finishRotate(m_startingNDC); } 89 | 90 | void startPan(osg::Vec2d startingNDC, osg::Vec4d panPlane); 91 | void pan(osg::Vec2d currentNDC); 92 | void finishPan(osg::Vec2d currentNDC); 93 | void finishPan() { finishPan(m_startingNDC); } 94 | 95 | void startZoom(osg::Vec2d startingNDC); 96 | void zoom(osg::Vec2d currentNDC); 97 | void finishZoom(osg::Vec2d currentNDC); 98 | void finishZoom() { finishZoom(m_startingNDC); } 99 | 100 | /** Dolly the camera forwards and backwards. Changes the view distance. 101 | This function is a no-op for orthographic projections. */ 102 | void startDolly(osg::Vec2d startingNDC); 103 | void dolly( const double deltaMovement ); 104 | void dolly(osg::Vec2d currentNDC); 105 | void finishDolly(osg::Vec2d currentNDC); 106 | void finishDolly() { finishDolly(m_startingNDC); } 107 | void setDollyCanChangeCenter(bool tf) { m_dollyCanChangeCenter = tf; emit changed(); } 108 | void setDollyCenterChangeThreshold(int threshold) { m_dollyCenterChangeThreshold = threshold; emit changed(); } 109 | 110 | void setCullMask(unsigned mask) { m_cullMask = mask; emit cullMaskChanged(m_cullMask); } 111 | void setCullMaskBits(unsigned mask) { setCullMask(m_cullMask | mask); } 112 | void clearCullMaskBits(unsigned mask) { setCullMask(m_cullMask & ~mask); } 113 | 114 | 115 | void setBoundingNode( osg::ref_ptr< osg::Node > boundNode) { m_boundingNode = boundNode; emit changed(); } 116 | private: 117 | void applyViewChangeMatrix(); 118 | 119 | 120 | /// Assure m_viewUp and m_viewDir are orthogonal 121 | inline void orthoNormalize(); 122 | bool intersectPlaneRay(osg::Vec3d &result, const osg::Vec4d &plane, const osg::Vec3d &p0, const osg::Vec3d &p1); 123 | void getZNearZFarProj(double &zNear, double &zFar, const osg::Matrixd &projMat); 124 | osg::Matrixd getOrientationMatrix() const; 125 | 126 | // Data ///////////////////////////////////////// 127 | 128 | // basic camera parameters 129 | osg::Vec3d m_viewUp; 130 | osg::Vec3d m_viewDir; 131 | osg::Vec3d m_viewCenter; 132 | double m_fovY; 133 | double m_viewDistance; 134 | 135 | // Projection matrix support 136 | double m_aspect; 137 | bool m_ortho; 138 | double m_orthoBottom; 139 | double m_orthoTop; 140 | 141 | // manipulation parameters 142 | double m_fovYScaleFactor; 143 | osg::Vec2d m_clampFovyRange; 144 | bool m_shouldClampFovYScale; 145 | double m_trackballRollSensitivity; 146 | bool m_dollyCanChangeCenter; 147 | int m_dollyCenterChangeThreshold; 148 | int m_dollyCurrentPressure; 149 | 150 | osg::Vec4d m_panPlane; 151 | 152 | bool m_viewChangeInProgress; // is the user in the process of adjusting the view 153 | osg::Matrixd m_viewChangeMatrix; // matrix representing the change thus far 154 | osg::Vec2d m_startingNDC; // NDC where view change began 155 | 156 | // node for doing scene bound computation. 157 | // This is necesary for fitToScreen() because nodes such as OriginAxis 158 | // have AutoTransform nodes which skew the bounding box based upon the 159 | // results of the last draw. 160 | osg::ref_ptr< osg::Node > m_boundingNode; 161 | 162 | std::string m_stashedView; 163 | 164 | unsigned m_cullMask; 165 | }; 166 | 167 | #endif // CAMERACONTROL_H 168 | -------------------------------------------------------------------------------- /UiEventWidget.cpp: -------------------------------------------------------------------------------- 1 | #include "UiEventWidget.h" 2 | #include 3 | #include "Osg3dSSAOView.h" 4 | #include 5 | #include 6 | 7 | UiEventWidget::UiEventWidget(QWidget *parent) 8 | : QWidget(parent) 9 | , m_mouseMode(MM_ORBIT) 10 | , m_lastFocused(nullptr) 11 | , m_shouldAutoFocus(true) 12 | , m_ssaoWidget(new Osg3dSSAOView(this)) 13 | { 14 | QHBoxLayout *horizontalLayout = new QHBoxLayout(this); 15 | horizontalLayout->addWidget(m_ssaoWidget); 16 | horizontalLayout->setContentsMargins(0, 0, 0, 0); 17 | 18 | osg::ref_ptr cameraModel = m_ssaoWidget->cameraModel(); 19 | connect(this, SIGNAL(startOrbit(osg::Vec2d)), 20 | cameraModel.get(), SLOT(startOrbit(osg::Vec2d))); 21 | connect(this, SIGNAL(startRotate(osg::Vec2d)), 22 | cameraModel.get(), SLOT(startRotate(osg::Vec2d))); 23 | connect(this, SIGNAL(startPan(osg::Vec2d, osg::Vec4d)), 24 | cameraModel.get(), SLOT(startPan(osg::Vec2d, osg::Vec4d))); 25 | connect(this, SIGNAL(startDolly(osg::Vec2d)), 26 | cameraModel.get(), SLOT(startDolly(osg::Vec2d))); 27 | connect(this, SIGNAL(startZoom(osg::Vec2d)), 28 | cameraModel.get(), SLOT(startZoom(osg::Vec2d))); 29 | 30 | connect(this, SIGNAL(orbit(osg::Vec2d)), 31 | cameraModel.get(), SLOT(orbit(osg::Vec2d))); 32 | connect(this, SIGNAL(rotate(osg::Vec2d)), 33 | cameraModel.get(), SLOT(rotate(osg::Vec2d))); 34 | connect(this, SIGNAL(pan(osg::Vec2d)), 35 | cameraModel.get(), SLOT(pan(osg::Vec2d))); 36 | connect(this, SIGNAL(dolly(osg::Vec2d)), 37 | cameraModel.get(), SLOT(dolly(osg::Vec2d))); 38 | connect(this, SIGNAL(zoom(osg::Vec2d)), 39 | cameraModel.get(), SLOT(zoom(osg::Vec2d))); 40 | 41 | connect(this, SIGNAL(finishOrbit(osg::Vec2d)), 42 | cameraModel.get(), SLOT(finishOrbit(osg::Vec2d))); 43 | connect(this, SIGNAL(finishRotate(osg::Vec2d)), 44 | cameraModel.get(), SLOT(finishRotate(osg::Vec2d))); 45 | connect(this, SIGNAL(finishPan(osg::Vec2d)), 46 | cameraModel.get(), SLOT(finishPan(osg::Vec2d))); 47 | connect(this, SIGNAL(finishDolly(osg::Vec2d)), 48 | cameraModel.get(), SLOT(finishDolly(osg::Vec2d))); 49 | connect(this, SIGNAL(finishZoom(osg::Vec2d)), 50 | cameraModel.get(), SLOT(finishZoom(osg::Vec2d))); 51 | 52 | } 53 | 54 | void UiEventWidget::enterEvent(QEvent *event) 55 | { 56 | if (!m_shouldAutoFocus) return; 57 | QWidget *w = qApp->focusWidget(); // get currently focused widget 58 | 59 | if (w == this) m_lastFocused = (QWidget *)0; 60 | else m_lastFocused = w; 61 | 62 | this->setFocus(); 63 | } 64 | 65 | void UiEventWidget::leaveEvent(QEvent *event) 66 | { 67 | if (!m_shouldAutoFocus) return; 68 | 69 | this->clearFocus(); 70 | 71 | if (m_lastFocused && m_lastFocused != this) { 72 | m_lastFocused->setFocus(); 73 | m_lastFocused = (QWidget *)0; 74 | } 75 | } 76 | 77 | 78 | void UiEventWidget::shootKeyEvent(QKeyEvent *event) 79 | { 80 | switch (event->key()) { 81 | case Qt::Key_Right: // fallthrough 82 | case Qt::Key_Up: // fallthrough 83 | case Qt::Key_Down: // fallthrough 84 | case Qt::Key_Left: { 85 | if (event->modifiers() == Qt::NoModifier) 86 | emit shotShift( (Qt::Key)event->key()); 87 | else if (event->modifiers() == Qt::ControlModifier) 88 | emit shotRotate((Qt::Key)event->key()); 89 | break; 90 | } 91 | default: break; 92 | } 93 | } 94 | void UiEventWidget::clipKeyEvent(QKeyEvent *event) 95 | { 96 | switch (event->key()) { 97 | case Qt::Key_Right: // fallthrough 98 | case Qt::Key_Up: // fallthrough 99 | case Qt::Key_Down: // fallthrough 100 | case Qt::Key_Left: { 101 | if (event->modifiers() == Qt::NoModifier) 102 | emit clipPlaneShift( (Qt::Key)event->key()); 103 | else if (event->modifiers() == Qt::ControlModifier) 104 | emit clipPlaneRotate((Qt::Key)event->key()); 105 | break; 106 | } 107 | default: break; 108 | } 109 | } 110 | 111 | void UiEventWidget::keyPressEvent(QKeyEvent *event) 112 | { 113 | switch (m_mouseMode) { 114 | case MouseMode::MM_SHOOT: { shootKeyEvent(event); break; } 115 | case MouseMode::MM_CUTTINGPLANE: { clipKeyEvent(event); break; } 116 | default: break; 117 | } 118 | } 119 | 120 | void UiEventWidget::keyReleaseEvent(QKeyEvent *event) 121 | { 122 | 123 | } 124 | 125 | void UiEventWidget::startPanning(osg::Vec2d ndc, int x, int y) 126 | { 127 | setCursor(Qt::ClosedHandCursor); 128 | auto lsi = m_ssaoWidget->intersectUnderCursor(x, y); 129 | 130 | osg::Vec3d pickPoint; 131 | if (lsi->containsIntersections()) { 132 | auto intersections = lsi->getIntersections(); 133 | auto intersection = *( intersections.begin() ); 134 | pickPoint = intersection.getWorldIntersectPoint(); 135 | } else { 136 | pickPoint = m_ssaoWidget->cameraModel()->viewCenter(); 137 | } 138 | osg::Vec3d viewDir = m_ssaoWidget->cameraModel()->viewDir(); 139 | osg::Vec4d panPlane = osg::Vec4d( viewDir, -( pickPoint * viewDir ) ); 140 | 141 | emit startPan(ndc, panPlane); 142 | } 143 | 144 | void UiEventWidget::mousePressEvent(QMouseEvent *event) 145 | { 146 | osg::Vec2d ndc = m_ssaoWidget->getNormalizedDeviceCoords( 147 | event->x(), event->y()); 148 | 149 | if ( event->buttons() != Qt::LeftButton) return; 150 | 151 | switch (m_mouseMode) { 152 | case MM_ORBIT: { emit startOrbit(ndc); break; } 153 | case MM_PAN: { this->startPanning(ndc, event->x(), event->y()); break; } 154 | case MM_ZOOM: { emit startZoom(ndc); break; } 155 | case MM_FLY: { emit startDolly(ndc); break; } 156 | case MM_ROTATE: { emit startRotate(ndc); break; } 157 | case MM_PICK_CENTER: { emit pickCenter(event->x(), event->y()); break; } 158 | case MM_SHOOT: { emit shoot(event->x(), event->y()); break;} 159 | case MM_SELECTOBJECT: {break;} 160 | case MM_PICK_POINT: { emit pickaPoint( ndc, event->x(), event->y()); break; } 161 | 162 | default: break; 163 | } 164 | 165 | } 166 | 167 | void UiEventWidget::mouseReleaseEvent(QMouseEvent *event) 168 | { 169 | if ( event->button() != Qt::LeftButton) return; 170 | osg::Vec2d ndc = m_ssaoWidget->getNormalizedDeviceCoords( 171 | event->x(), event->y()); 172 | 173 | switch (m_mouseMode) { 174 | case MM_ORBIT: {emit finishOrbit(ndc); break; } 175 | case MM_PAN: { setCursor(Qt::OpenHandCursor); 176 | emit finishPan(ndc); break; } 177 | case MM_ZOOM: { emit finishZoom(ndc); break; } 178 | case MM_FLY: { emit finishDolly(ndc); break; } 179 | case MM_ROTATE: { emit finishRotate(ndc); break; } 180 | case MM_PICK_CENTER: { break; } 181 | case MM_SELECTOBJECT: break; 182 | case MM_PICK_POINT:{emit pickaPoint( ndc, event->x(), event->y()); break;} 183 | case MM_SHOOT: {break;} 184 | case MM_MOVE: break; 185 | } 186 | 187 | } 188 | 189 | void UiEventWidget::mouseMoveEvent(QMouseEvent *event) 190 | { 191 | if ( event->buttons() != Qt::LeftButton) return; 192 | 193 | osg::Vec2d ndc = m_ssaoWidget->getNormalizedDeviceCoords( 194 | event->x(), event->y()); 195 | 196 | switch (m_mouseMode) { 197 | case MM_ORBIT: { emit orbit(ndc); break; } 198 | case MM_PAN: { emit pan(ndc); break; } 199 | case MM_ZOOM: { emit zoom(ndc); break; } 200 | case MM_FLY: { emit dolly(ndc); break; } 201 | case MM_ROTATE: { emit rotate(ndc); break; } 202 | case MM_PICK_CENTER: { break; } 203 | case MM_SHOOT: { emit shoot(event->x(), event->y()); break;} 204 | case MM_SELECTOBJECT: break; 205 | case MM_PICK_POINT:{emit pickaPoint( ndc, event->x(), event->y()); break;} 206 | case MM_MOVE: 207 | break; 208 | } 209 | } 210 | 211 | void UiEventWidget::wheelEvent(QWheelEvent *event) 212 | { 213 | if (event->delta() > 0) 214 | emit dolly(0.5); 215 | else 216 | emit dolly(-0.5); 217 | } 218 | 219 | void UiEventWidget::setMouseMode(UiEventWidget::MouseMode mode) 220 | { 221 | if (m_mouseMode == MM_PAN && mode != MM_PAN) 222 | setCursor(Qt::ArrowCursor); 223 | 224 | if (m_mouseMode != MM_PAN && mode == MM_PAN) 225 | setCursor(Qt::OpenHandCursor); 226 | 227 | m_mouseMode = mode; 228 | } 229 | 230 | -------------------------------------------------------------------------------- /Osg3dViewWithCamera.cpp: -------------------------------------------------------------------------------- 1 | #include "Osg3dViewWithCamera.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | #include "NodeMask.h" 16 | Osg3dViewWithCamera::Osg3dViewWithCamera(QWidget *parent) 17 | : QOpenGLWidget(parent) 18 | , m_scene(new osg::Group) 19 | , m_root(new osg::Switch) 20 | , m_currentLineWidth(1.0) 21 | , m_cameraModel(new CameraModel) 22 | { 23 | setFocusPolicy(Qt::StrongFocus); 24 | 25 | // Construct the embedded graphics window 26 | m_osgGraphicsWindow = new osgViewer::GraphicsWindowEmbedded(0,0,width(),height()); 27 | getCamera()->setGraphicsContext(m_osgGraphicsWindow); 28 | 29 | // Set up the camera 30 | getCamera()->setViewport(new osg::Viewport(0,0,width(),height())); 31 | 32 | // By default draw everthing that has even 1 bit set in the nodemask 33 | getCamera()->setCullMask( ~(unsigned)0 ); 34 | getCamera()->setDataVariance(osg::Object::DYNAMIC); 35 | 36 | // As of July 2010 there wasn't really a good way to multi-thread OSG 37 | // under Qt so just set the threading model to be SingleThreaded 38 | setThreadingModel(osgViewer::Viewer::SingleThreaded); 39 | 40 | // draw both sides of polygons 41 | setLightingTwoSided(); 42 | 43 | // Set the minimum size for this viewer window 44 | setMinimumSize(64, 64); 45 | 46 | osg::ref_ptr ss = m_scene->getOrCreateStateSet(); 47 | m_point = new osg::Point(m_currentLineWidth); 48 | ss->setAttribute(m_point); 49 | 50 | m_lineWidth = new osg::LineWidth(m_currentLineWidth); 51 | ss->setAttribute(m_lineWidth); 52 | 53 | m_root->setNewChildDefaultValue(true); 54 | m_root->addChild(m_scene); 55 | 56 | this->setSceneData(m_root); 57 | 58 | m_cameraModel->setBoundingNode(m_scene); 59 | m_cameraModel->computeInitialView(); 60 | connect(m_cameraModel, SIGNAL(changed()), this, SLOT(update())); 61 | } 62 | 63 | Osg3dViewWithCamera::~Osg3dViewWithCamera() 64 | { 65 | } 66 | 67 | void Osg3dViewWithCamera::paintGL() 68 | { 69 | // Update the camera 70 | osg::Camera *cam = this->getCamera(); 71 | //const osg::Viewport* vp = cam->getViewport(); 72 | 73 | if (cam->getCullMask() != m_cameraModel->cullMask()) 74 | cam->setCullMask( m_cameraModel->cullMask() ); 75 | 76 | m_cameraModel->setAspect((double)width() / (double)height()); 77 | 78 | cam->setViewMatrix(m_cameraModel->getModelViewMatrix() ); 79 | cam->setProjectionMatrix(m_cameraModel->computeProjection()); 80 | 81 | // Invoke the OSG traversal pipeline 82 | frame(); 83 | 84 | emit updated(); 85 | } 86 | 87 | void Osg3dViewWithCamera::setCameraModel(osg::ref_ptr cameraControl) 88 | { 89 | disconnect(m_cameraModel.get(), SIGNAL(changed()), this, SLOT(update())); 90 | m_cameraModel = cameraControl; 91 | m_cameraModel->setBoundingNode(m_scene); 92 | connect(m_cameraModel.get(), SIGNAL(changed()), this, SLOT(update())); 93 | update(); 94 | } 95 | 96 | void Osg3dViewWithCamera::resizeGL(int width, int height) 97 | { 98 | m_osgGraphicsWindow->resized(0,0,width,height); 99 | } 100 | 101 | // Camera callback that force binds an FBO 102 | // Used to make sure the main camera writes to the default Qt FBO 103 | struct CameraPreDrawCallback : public osg::Camera::DrawCallback 104 | { 105 | CameraPreDrawCallback(int fboInt) : 106 | _fboInt(fboInt) 107 | { 108 | } 109 | 110 | virtual void operator () (const osg::Camera& /*camera*/) const 111 | { 112 | QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); 113 | f->glBindFramebuffer(GL_FRAMEBUFFER, _fboInt); 114 | } 115 | 116 | int _fboInt; 117 | }; 118 | 119 | void Osg3dViewWithCamera::initializeGL() 120 | { 121 | initializeOpenGLFunctions(); 122 | QString vendor = (const char *)glGetString(GL_VENDOR); 123 | QString renderer = (const char *)glGetString(GL_RENDERER); 124 | QString version = (const char *)glGetString(GL_VERSION); 125 | QString glsl_version = (const char *)glGetString(GL_SHADING_LANGUAGE_VERSION); 126 | 127 | QString htmlString; 128 | QTextStream htmlStream(&htmlString); 129 | htmlStream << ""; 130 | htmlStream << "" ; 132 | htmlStream << "" ; 134 | htmlStream << "" ; 136 | htmlStream << "" ; 138 | htmlStream << "
GL_VENDOR"; 131 | htmlStream << vendor << "
GL_RENDERER"; 133 | htmlStream << renderer << "
GL_VERSION "; 135 | htmlStream << version << "
GLSL_VERSION "; 137 | htmlStream << glsl_version << "
"; 139 | htmlStream.flush(); 140 | qApp->setProperty("OpenGLinfoHTML", htmlString ); 141 | 142 | 143 | QString plainString; 144 | QTextStream stream(&plainString); 145 | stream << "GL_VENDOR : "; 146 | stream << vendor << endl; 147 | stream << "GL_RENDERER : "; 148 | stream << renderer << endl; 149 | stream << "GL_VERSION : "; 150 | stream << version << endl; 151 | stream << "GLSL_VERSION : "; 152 | stream << glsl_version << endl; 153 | stream.flush(); 154 | qApp->setProperty("OpenGLinfo", plainString); 155 | 156 | // Add a callback to the main camera to make it use the default Qt framebuffer 157 | int fboInt = this->defaultFramebufferObject(); 158 | this->getCamera()->setPreDrawCallback(new CameraPreDrawCallback(fboInt)); 159 | } 160 | 161 | osg::Vec2d Osg3dViewWithCamera::getNormalizedDeviceCoords(const int ix, const int iy) 162 | { 163 | osg::Vec2d ndc; 164 | 165 | int center = width()/2; 166 | ndc[0] = ((double)ix - (double)center) / (double)center; 167 | if (ndc[0] > 1.0) ndc[0] = 1.0; 168 | 169 | center = height()/2; 170 | int invertedY = height() - iy; 171 | ndc[1] = ((double)invertedY - (double)center) / (double)center; 172 | if (ndc[1] > 1.0) ndc[1] = 1.0; 173 | 174 | return ndc; 175 | } 176 | 177 | void Osg3dViewWithCamera::addNode(osg::Node *root) 178 | { 179 | m_scene->addChild(root); 180 | } 181 | void Osg3dViewWithCamera::removeNode(osg::Node *root) 182 | { 183 | m_scene->removeChild(root); 184 | } 185 | void Osg3dViewWithCamera::clearNodes() 186 | { 187 | m_scene->removeChildren(0, m_scene->getNumChildren()); 188 | } 189 | 190 | void Osg3dViewWithCamera::setLightingTwoSided(bool tf) 191 | { 192 | osg::ref_ptr lm = new osg::LightModel; 193 | lm->setTwoSided(tf); 194 | lm->setAmbientIntensity(osg::Vec4(0.1f,0.1f,0.1f,1.0f)); 195 | 196 | osg::StateSet *ss; 197 | 198 | for (int i=0 ; i < 2 ; i++ ) { 199 | ss = ((osgViewer::Renderer *)getCamera()->getRenderer())-> 200 | getSceneView(i)->getGlobalStateSet(); 201 | 202 | ss->setAttributeAndModes(lm, osg::StateAttribute::ON); 203 | } 204 | } 205 | 206 | 207 | void Osg3dViewWithCamera::setDrawMode(osg::PolygonMode::Mode drawMode) 208 | { 209 | osg::ref_ptr ss = 210 | m_scene->getOrCreateStateSet(); 211 | 212 | osg::ref_ptr pm = 213 | dynamic_cast 214 | (ss->getAttribute(osg::StateAttribute::POLYGONMODE)); 215 | 216 | if(!pm) { 217 | pm = new osg::PolygonMode; 218 | ss->setAttribute(pm.get()); 219 | } 220 | 221 | pm->setMode(osg::PolygonMode::FRONT_AND_BACK, drawMode); 222 | 223 | switch (drawMode) { 224 | case osg::PolygonMode::LINE: 225 | case osg::PolygonMode::POINT: 226 | ss->setMode(GL_LIGHTING, osg::StateAttribute::OFF); 227 | break; 228 | default: 229 | ss->setMode(GL_LIGHTING, osg::StateAttribute::ON); 230 | break; 231 | } 232 | 233 | emit drawModeChanged(drawMode); 234 | update(); 235 | } 236 | 237 | void Osg3dViewWithCamera::setDrawModeFromQAction() 238 | { 239 | QAction *a = dynamic_cast(sender()); 240 | if (!a) 241 | return; 242 | bool ok = false; 243 | osg::PolygonMode::Mode drawMode = 244 | static_cast(a->data().toUInt(&ok)); 245 | if (ok) setDrawMode(drawMode); 246 | } 247 | 248 | void Osg3dViewWithCamera::setLineWidth(int size) 249 | { 250 | m_point->setSize((float)size); 251 | m_lineWidth->setWidth((float)size); 252 | emit lineWidthChanged(size); 253 | update(); 254 | } 255 | 256 | void printIntersectorDebugging(const int x, const int y, unsigned mask, 257 | osg::ref_ptr intersector 258 | ) 259 | { 260 | qDebug("intersectUnderCursor(%d %d %s):", x, y, qPrintable(NodeMask::maskToString(mask))); 261 | auto intersections = intersector->getIntersections(); 262 | 263 | for (auto isect=intersections.begin() ; isect != intersections.end() ; isect++) { 264 | 265 | osg::Vec3d pt = (*isect).getWorldIntersectPoint(); 266 | 267 | QString path; 268 | foreach (osg::Node *n, (*isect).nodePath) { 269 | if (n->getName().size() > 0) 270 | path.append(QString("/%1(%2)") 271 | .arg(n->getName().c_str()) 272 | .arg( NodeMask::maskToString(n->getNodeMask()) )); 273 | else 274 | path.append(QString("/%1(%2)") 275 | .arg(n->className()) 276 | .arg( NodeMask::maskToString(n->getNodeMask()) )); 277 | } 278 | 279 | qDebug(" ipt %10g %10g %10g %s", pt.x(), pt.y(), pt.z(), qPrintable(path)); 280 | 281 | } 282 | qDebug("endintersections:"); 283 | } 284 | 285 | 286 | osg::ref_ptr 287 | Osg3dViewWithCamera::intersectUnderCursor(const int x, const int y, unsigned mask) 288 | { 289 | osg::ref_ptr intersector = 290 | new osgUtil::LineSegmentIntersector(osgUtil::Intersector::WINDOW, 291 | x, height()- y); 292 | 293 | osgUtil::IntersectionVisitor intersectVisitor( intersector.get() ); 294 | intersectVisitor.setTraversalMask(mask); 295 | 296 | getCamera()->accept(intersectVisitor); 297 | 298 | return intersector; 299 | } 300 | -------------------------------------------------------------------------------- /MainWindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 668 10 | 439 11 | 12 | 13 | 14 | MainWindow 15 | 16 | 17 | 18 | 19 | 0 20 | 21 | 22 | 0 23 | 24 | 25 | 0 26 | 27 | 28 | 0 29 | 30 | 31 | 32 | 33 | Qt::Horizontal 34 | 35 | 36 | 37 | 38 | 2 39 | 40 | 41 | 0 42 | 43 | 44 | 0 45 | 46 | 47 | 0 48 | 49 | 50 | 0 51 | 52 | 53 | 54 | 55 | 56 | 0 57 | 0 58 | 59 | 60 | 61 | 62 | 128 63 | 128 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 0 73 | 0 74 | 75 | 76 | 77 | 78 | 128 79 | 128 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 192 90 | 16777215 91 | 92 | 93 | 94 | 95 | 96 | 97 | Radius: 98 | 99 | 100 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | Power: 111 | 112 | 113 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | Threshold: 127 | 128 | 129 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | ColorOnly 138 | 139 | 140 | 141 | 142 | ColorAndSSAO 143 | 144 | 145 | 146 | 147 | ssaoOnly 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | AOBlur 156 | 157 | 158 | 159 | 160 | 161 | 162 | HaloRemoval 163 | 164 | 165 | 166 | 167 | 168 | 169 | Qt::Vertical 170 | 171 | 172 | 173 | 20 174 | 40 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | SSAO Enabled 183 | 184 | 185 | true 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 0 199 | 0 200 | 668 201 | 21 202 | 203 | 204 | 205 | 206 | File 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | MouseMode 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | TopToolBarArea 227 | 228 | 229 | false 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | Open... 240 | 241 | 242 | 243 | 244 | Quit 245 | 246 | 247 | Ctrl+Q 248 | 249 | 250 | 251 | 252 | Reset 253 | 254 | 255 | 256 | 257 | Fit 258 | 259 | 260 | 261 | 262 | true 263 | 264 | 265 | Orbit 266 | 267 | 268 | 269 | 270 | true 271 | 272 | 273 | Pan 274 | 275 | 276 | 277 | 278 | true 279 | 280 | 281 | Zoom 282 | 283 | 284 | 285 | 286 | true 287 | 288 | 289 | Rotate 290 | 291 | 292 | 293 | 294 | 295 | 296 | OSGWidget 297 | QWidget 298 |
OSGWidget.h
299 | 1 300 |
301 | 302 | UiEventWidget 303 | QWidget 304 |
UiEventWidget.h
305 | 1 306 |
307 |
308 | 309 | 310 | 311 | actionQuit 312 | triggered() 313 | MainWindow 314 | close() 315 | 316 | 317 | -1 318 | -1 319 | 320 | 321 | 199 322 | 149 323 | 324 | 325 | 326 | 327 |
328 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /MainWindow.cpp: -------------------------------------------------------------------------------- 1 | #include "MainWindow.h" 2 | #include "ui_MainWindow.h" 3 | #include "SSAONode.h" 4 | #include "Osg3dSSAOView.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | MainWindow::MainWindow(QWidget *parent) 16 | : QMainWindow(parent) 17 | , ui(new Ui::MainWindow) 18 | , m_world(new osg::Group) 19 | 20 | { 21 | ui->setupUi(this); 22 | Osg3dSSAOView *ssaoView = ui->uiEventWidget->ssaoView(); 23 | 24 | applicationSetup(); 25 | connectHandlers(ssaoView); 26 | setupOSGWidget(ssaoView); 27 | setupSSAOWidget(ssaoView); 28 | setMouseModeOrbit(); 29 | 30 | m_world->addChild(buildScene()); 31 | ui->osgWidget->setScene(m_world); 32 | ui->uiEventWidget->ssaoView()->addNode(m_world); 33 | ui->uiEventWidget->ssaoView()->cameraModel()->fitToScreen(); 34 | } 35 | 36 | MainWindow::~MainWindow() 37 | { 38 | delete ui; 39 | } 40 | void MainWindow::applicationSetup() 41 | { 42 | // set up application name 43 | QFileInfo applicationFile(QApplication::applicationFilePath()); 44 | 45 | // These allow us to simply construct a "QSettings" object without arguments 46 | qApp->setOrganizationDomain("org.vissimlab"); 47 | qApp->setApplicationName(applicationFile.baseName()); 48 | qApp->setOrganizationName("LeeButler"); 49 | qApp->setApplicationVersion(__DATE__ __TIME__); 50 | 51 | 52 | QActionGroup *group = new QActionGroup(this); 53 | group->addAction(ui->actionOrbit); 54 | group->addAction(ui->actionPan); 55 | group->addAction(ui->actionZoom); 56 | group->addAction(ui->actionRotate); 57 | } 58 | 59 | void MainWindow::connectHandlers(Osg3dSSAOView *ssaoView) 60 | { 61 | connect(ui->actionOrbit, SIGNAL(triggered()), this, SLOT(setMouseModeOrbit()) ); 62 | connect(ui->actionPan, SIGNAL(triggered()), this, SLOT(setMouseModePan()) ); 63 | connect(ui->actionRotate, SIGNAL(triggered()), this, SLOT(setMouseModeRotate()) ); 64 | connect(ui->actionZoom, SIGNAL(triggered()), this, SLOT(setMouseModeZoom()) ); 65 | 66 | connect(ui->radiusEdit, SIGNAL(editingFinished()), 67 | this, SLOT(setRadius())); 68 | connect(ui->thresholdEdit, SIGNAL(editingFinished()), 69 | this, SLOT(setThreshold())); 70 | connect(ui->powerEdit, SIGNAL(editingFinished()), 71 | this, SLOT(setPower())); 72 | connect(ui->displayModeCombo, SIGNAL(currentIndexChanged(int)), 73 | this, SLOT(handleDisplayModeCombo())); 74 | connect(ui->aoBlurr, SIGNAL(toggled(bool)), 75 | this, SLOT(ssaoBlur(bool))); 76 | connect(ui->haloRemoval, SIGNAL(toggled(bool)), 77 | this, SLOT(ssaoHalo(bool))); 78 | connect(ui->ssaoCheckBox, SIGNAL(toggled(bool)), 79 | this, SLOT(setSSAOEnabled(bool))); 80 | 81 | ui->powerEdit->setText(QString("%1").arg(ssaoView->ssaoPower())); 82 | ui->radiusEdit->setText(QString("%1").arg(ssaoView->ssaoRadius() )); 83 | ui->thresholdEdit->setText(QString("%1").arg(ssaoView->ssaoHaloThreshold() )); 84 | 85 | ui->haloRemoval->setChecked(ssaoView->ssaoHaloRemovalIsEnabled()); 86 | ui->aoBlurr->setChecked(ssaoView->ssaoBlurIsEnabled()); 87 | ui->displayModeCombo->setCurrentIndex(ssaoView->ssaoDisplayMode()); 88 | 89 | } 90 | 91 | void MainWindow::setupOSGWidget(Osg3dSSAOView *ssaoView) 92 | { 93 | ui->osgWidget->setSSAOPower(ssaoView->ssaoPower()); 94 | ui->osgWidget->setSSAORadius(ssaoView->ssaoRadius()); 95 | ui->osgWidget->setSSAOHaloThreshold(ssaoView->ssaoHaloThreshold()); 96 | 97 | ui->osgWidget->setSSAOHaloRemoval(ssaoView->ssaoHaloRemovalIsEnabled()); 98 | ui->osgWidget->setSSAOBlurEnabled(ssaoView->ssaoBlurIsEnabled()); 99 | ui->osgWidget->setSSAODisplayMode((SSAONode::DisplayMode)ssaoView->ssaoDisplayMode()); 100 | ui->osgWidget->setCameraModel(ssaoView->cameraModel()); 101 | 102 | ui->osgWidget->setSSAOEnabled(true); 103 | } 104 | 105 | void MainWindow::setupSSAOWidget(Osg3dSSAOView *ssaoView) 106 | { 107 | connect(ui->haloRemoval, SIGNAL(toggled(bool)), 108 | ssaoView, SLOT(setSSAOHaloRemoval(bool))); 109 | connect(ui->aoBlurr, SIGNAL(toggled(bool)), 110 | ssaoView, SLOT(setSSAOBlurEnabled(bool))); 111 | connect(ui->ssaoCheckBox, SIGNAL(toggled(bool)), 112 | ssaoView, SLOT(setSSAOEnabled(bool))); 113 | } 114 | osg::ref_ptr MainWindow::buildAxes() 115 | { 116 | osg::ref_ptr m_axisGeom = new osg::Geometry(); 117 | // allocate verticies 118 | osg::ref_ptr m_axisVerts = new osg::Vec3Array; 119 | m_axisVerts->push_back(osg::Vec3(-1.0, 0.0, 0.0)); 120 | m_axisVerts->push_back(osg::Vec3(0.0, 0.0, 0.0)); 121 | m_axisVerts->push_back(osg::Vec3(1.0, 0.0, 0.0)); 122 | m_axisVerts->push_back(osg::Vec3(0.0, -1.0, 0.0)); 123 | m_axisVerts->push_back(osg::Vec3(0.0, 0.0, 0.0)); 124 | m_axisVerts->push_back(osg::Vec3(0.0, 1.0, 0.0)); 125 | m_axisVerts->push_back(osg::Vec3(0.0, 0.0, -1.0)); 126 | m_axisVerts->push_back(osg::Vec3(0.0, 0.0, 0.0)); 127 | m_axisVerts->push_back(osg::Vec3(0.0, 0.0, 1.0)); 128 | 129 | m_axisGeom->setVertexArray(m_axisVerts); 130 | m_axisGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, 2)); 131 | m_axisGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 1, 2)); 132 | m_axisGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 3, 2)); 133 | m_axisGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 4, 2)); 134 | m_axisGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 6, 2)); 135 | m_axisGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 7, 2)); 136 | 137 | // allocate colors 138 | osg::Vec4Array* colors = new osg::Vec4Array; 139 | colors->push_back(osg::Vec4(.35f,0.0f,0.0f,1.0f)); 140 | colors->push_back(osg::Vec4(1.0f,0.0f,0.0f,1.0f)); 141 | colors->push_back(osg::Vec4(0.0f,.35f,0.0f,1.0f)); 142 | colors->push_back(osg::Vec4(0.0f,1.0f,0.0f,1.0f)); 143 | colors->push_back(osg::Vec4(0.0f,0.0f,.35f,1.0f)); 144 | colors->push_back(osg::Vec4(0.0f,0.0f,1.0f,1.0f)); 145 | 146 | m_axisGeom->setColorArray(colors); 147 | m_axisGeom->setColorBinding(osg::Geometry::BIND_PER_PRIMITIVE_SET); 148 | m_axisGeom->setDataVariance(osg::Object::DYNAMIC); 149 | 150 | // generate the Geode 151 | osg::ref_ptr m_axisGeode = new osg::Geode(); 152 | m_axisGeode->addDrawable(m_axisGeom); 153 | //m_axisGeode->setNodeMask( MASK_AXIS ); 154 | m_axisGeode->setName("Axis"); 155 | 156 | //turn off lighting so we always see the line color 157 | osg::StateSet *ss = m_axisGeode->getOrCreateStateSet(); 158 | ss->setMode( GL_LIGHTING, osg::StateAttribute::OFF ); 159 | 160 | // Set the linewidth a little wider so we can see the thing 161 | osg::LineWidth *lineWidth = new osg::LineWidth; 162 | lineWidth->setWidth( 2.0 ); 163 | ss->setAttributeAndModes(lineWidth); 164 | 165 | return m_axisGeode; 166 | } 167 | 168 | 169 | // TODO: Remove 170 | double drand48() 171 | { 172 | return ((double)rand()/(double)RAND_MAX); 173 | } 174 | double randColor() 175 | { 176 | return drand48() * 0.875 + 0.125; 177 | } 178 | osg::ref_ptr MainWindow::buildScene() 179 | { 180 | osg::ref_ptr node = new osg::Group; 181 | 182 | osg::ref_ptr geode = buildAxes(); 183 | node->addChild(geode); 184 | #if 1 185 | geode = new osg::Geode; 186 | #define RANDCOORD ((drand48() - 0.5) * 2.0) 187 | 188 | float boxDimen = 30.0; 189 | float sphMin = 0.125; 190 | float sphMax = 4; 191 | 192 | for (int i=0 ; i < 5000 ; i++ ) { 193 | float x = boxDimen * RANDCOORD; 194 | float y = boxDimen * RANDCOORD; 195 | float z = boxDimen * RANDCOORD; 196 | float radius = sphMin + (drand48() * (sphMax-sphMin)); 197 | 198 | osg::ShapeDrawable *sd = new osg::ShapeDrawable(new osg::Box(osg::Vec3(x, y, z), radius)); 199 | sd->setColor(osg::Vec4(randColor(), randColor(), randColor(), 1.0)); 200 | geode->addDrawable(sd); 201 | } 202 | 203 | node->addChild(geode); 204 | osgDB::writeNodeFile(*node, "testScene.osg"); 205 | #endif 206 | 207 | return node; 208 | } 209 | 210 | void MainWindow::on_actionOpen_triggered() 211 | { 212 | 213 | QSettings settings; 214 | // if there is a current workFlowController, we need 215 | 216 | QString fileName = QFileDialog::getOpenFileName(this, "Select File", 217 | settings.value("currentDirectory").toString(), 218 | "OSG File (*.osg *.ive *.osgt *.osgb *.obj *.ply)"); 219 | 220 | if (fileName.isEmpty() || fileName.isNull()) 221 | return; 222 | 223 | osg::ref_ptr loaded = osgDB::readNodeFile(fileName.toStdString()); 224 | 225 | if (!loaded.valid()) return; 226 | 227 | const osg::BoundingSphere &bs = loaded->getBound(); 228 | if (bs.radius() <= 0.0) return; 229 | 230 | m_world->removeChildren(0, m_world->getNumChildren()); 231 | 232 | m_world->addChild(loaded); 233 | 234 | QFileInfo fi(fileName); 235 | settings.setValue("currentDirectory", fi.absolutePath()); 236 | 237 | ui->uiEventWidget->ssaoView()->cameraModel()->fitToScreen(); 238 | } 239 | 240 | void MainWindow::setMouseModeOrbit() 241 | { 242 | ui->actionOrbit->setChecked(true); 243 | ui->uiEventWidget->setMouseMode(UiEventWidget::MM_ORBIT); 244 | } 245 | 246 | void MainWindow::setMouseModePan() 247 | { 248 | ui->actionPan->setChecked(true); 249 | ui->uiEventWidget->setMouseMode(UiEventWidget::MM_PAN); 250 | } 251 | 252 | void MainWindow::setMouseModeRotate() 253 | { 254 | ui->actionRotate->setChecked(true); 255 | ui->uiEventWidget->setMouseMode(UiEventWidget::MM_ROTATE); 256 | } 257 | 258 | void MainWindow::setMouseModeZoom() 259 | { 260 | ui->actionZoom->setChecked(true); 261 | ui->uiEventWidget->setMouseMode(UiEventWidget::MM_ZOOM); 262 | } 263 | 264 | void MainWindow::handleDisplayModeCombo() 265 | { 266 | ui->osgWidget->setSSAODisplayMode( 267 | (SSAONode::DisplayMode) 268 | ui->displayModeCombo->currentIndex() 269 | ); 270 | ui->uiEventWidget->ssaoView()->setSSAODisplayMode( 271 | (SSAONode::DisplayMode) 272 | ui->displayModeCombo->currentIndex() 273 | ); 274 | 275 | ui->osgWidget->update(); 276 | ui->uiEventWidget->ssaoView()->update(); 277 | } 278 | 279 | void MainWindow::ssaoHalo(bool tf) 280 | { 281 | ui->osgWidget->setSSAOHaloRemoval(tf); 282 | ui->uiEventWidget->ssaoView()->setSSAOHaloRemoval(tf); 283 | ui->osgWidget->update(); 284 | ui->uiEventWidget->ssaoView()->update(); 285 | } 286 | 287 | void MainWindow::ssaoBlur(bool tf) 288 | { 289 | ui->osgWidget->setSSAOBlurEnabled(tf); 290 | ui->uiEventWidget->ssaoView()->setSSAOBlurEnabled(tf); 291 | ui->osgWidget->update(); 292 | ui->uiEventWidget->ssaoView()->update(); 293 | } 294 | 295 | void MainWindow::setSSAOEnabled(bool tf) 296 | { 297 | ui->displayModeCombo->setEnabled(tf); 298 | ui->osgWidget->setSSAOEnabled(tf); 299 | ui->uiEventWidget->ssaoView()->setSSAOEnabled(tf); 300 | } 301 | 302 | void MainWindow::setRadius() 303 | { 304 | float radius = ui->radiusEdit->text().toFloat(); 305 | 306 | ui->osgWidget->setSSAORadius(radius); 307 | ui->uiEventWidget->ssaoView()->setSSAORadius(radius); 308 | 309 | ui->osgWidget->update(); 310 | ui->uiEventWidget->ssaoView()->update(); 311 | } 312 | 313 | void MainWindow::setThreshold() 314 | { 315 | float threshold = ui->thresholdEdit->text().toFloat(); 316 | ui->osgWidget->setSSAOHaloThreshold(threshold); 317 | ui->uiEventWidget->ssaoView()->setSSAOHaloThreshold(threshold); 318 | ui->osgWidget->update(); 319 | ui->uiEventWidget->ssaoView()->update(); 320 | } 321 | 322 | void MainWindow::setPower() 323 | { 324 | float power = ui->powerEdit->text().toFloat(); 325 | ui->osgWidget->setSSAOPower(power); 326 | ui->uiEventWidget->ssaoView()->setSSAOPower(power); 327 | ui->osgWidget->update(); 328 | ui->uiEventWidget->ssaoView()->update(); 329 | } 330 | -------------------------------------------------------------------------------- /SSAONode.cpp: -------------------------------------------------------------------------------- 1 | #include "SSAONode.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // Default settings constructor 11 | SSAONode::SSAONode(int width, 12 | int height, 13 | int kernelSize, 14 | int noiseSize, 15 | int blurSize, 16 | float radius, 17 | float power) 18 | : m_kernelSize(kernelSize), 19 | m_noiseSize(noiseSize), 20 | m_blurSize(blurSize), 21 | m_ssaoRadius(radius), 22 | m_ssaoPower(power), 23 | m_blurAOEnabled(true), 24 | m_haloRemovalEnabled(true), 25 | m_haloTreshold(radius), 26 | m_width(width), 27 | m_height(height), 28 | 29 | m_kernelData(nullptr), 30 | m_noiseData(nullptr), 31 | 32 | displayType(SSAO_ColorAndAO) 33 | { 34 | Initialize(); 35 | } 36 | 37 | SSAONode::~SSAONode() { 38 | if (m_kernelData != NULL) { 39 | delete[] m_kernelData; 40 | m_kernelData = NULL; 41 | } 42 | if (m_noiseData != NULL) { 43 | delete[] m_noiseData; 44 | m_noiseData = NULL; 45 | } 46 | } 47 | 48 | 49 | void SSAONode::addKernelUniformToStateSet(osg::StateSet *stateset, int kernelLength) 50 | { 51 | // Set ssao sampling kernel as uniform 52 | osg::Uniform *ssaoKernelUniform = 53 | new osg::Uniform(osg::Uniform::FLOAT_VEC3, 54 | "ssaoKernel", 55 | kernelLength); 56 | 57 | // Create hemisphere kernel and noise texture 58 | if (m_kernelData == nullptr) { 59 | m_kernelData = new osg::Vec3f[kernelLength]; 60 | generateHemisphereSamples(m_kernelData, kernelLength); 61 | } 62 | 63 | for (int i = 0; i < kernelLength; i++) { 64 | ssaoKernelUniform->setElement(i, m_kernelData[i]); 65 | } 66 | 67 | stateset->addUniform(ssaoKernelUniform); 68 | } 69 | 70 | void SSAONode::removeAttachedCameras() 71 | { 72 | int numChildren = this->getNumChildren(); 73 | 74 | for (int i = numChildren - 1; i >= 0; i--) 75 | { 76 | osg::Camera* cam = this->getChild(i)->asCamera(); 77 | if (cam) 78 | { 79 | this->removeChild(cam); 80 | //delete &cam; 81 | } 82 | } 83 | } 84 | 85 | void SSAONode::createFirstPassCamera() 86 | { 87 | // Initialize programmable pipeline for multipass deferred rendering 88 | 89 | // ------------------------------------------------------------------------- 90 | 91 | // Create texture for deferred rendering (1st pass) - G-Buffer: Color 92 | colorTex = new osg::Texture2D(); 93 | colorTex->setTextureSize(m_width, m_height); 94 | colorTex->setInternalFormat(GL_RGB); 95 | 96 | // Create texture for deferred rendering (1st pass) - G-Buffer: Depth 97 | linearDepthTex = new osg::Texture2D(); 98 | linearDepthTex->setTextureSize(m_width, m_height); 99 | linearDepthTex->setInternalFormat(GL_DEPTH_COMPONENT24); 100 | linearDepthTex->setSourceFormat(GL_DEPTH_COMPONENT); 101 | linearDepthTex->setSourceType(GL_FLOAT); 102 | 103 | // Create texture for deferred rendering (1st pass) - G-Buffer: Normal 104 | normalTex = new osg::Texture2D; 105 | normalTex->setTextureSize(m_width, m_height); 106 | normalTex->setInternalFormat(GL_RGB); 107 | 108 | // Create camera for rendering to texture (to G-buffer) 109 | rttCamera = createRTTCameraGBuffer(osg::Camera::DEPTH_BUFFER, 110 | linearDepthTex.get(), 111 | osg::Camera::COLOR_BUFFER0, 112 | colorTex.get(), 113 | osg::Camera::COLOR_BUFFER1, 114 | normalTex.get(), 115 | false); 116 | 117 | // Load deferred phong shader for rendering scene into G-buffer 118 | // (color + view space normal + linear depth) 119 | osg::StateSet* phongState = rttCamera->getOrCreateStateSet(); 120 | 121 | osg::Program* phongProgramObject = new osg::Program; 122 | osg::Shader* phongVertexObject = new osg::Shader(osg::Shader::VERTEX); 123 | osg::Shader* phongFragmentObject = new osg::Shader(osg::Shader::FRAGMENT); 124 | phongProgramObject->addShader(phongFragmentObject); 125 | phongProgramObject->addShader(phongVertexObject); 126 | 127 | setShaderStringFromResource(phongVertexObject, ":/shaders/phong.vp"); 128 | setShaderStringFromResource(phongFragmentObject, ":/shaders/phong.fp"); 129 | 130 | phongState->setAttributeAndModes(phongProgramObject, 131 | osg::StateAttribute::ON); 132 | 133 | rttCamera->setRenderOrder(osg::Camera::PRE_RENDER, 0); 134 | this->addChild(rttCamera.get()); 135 | 136 | } 137 | 138 | void SSAONode::createSecondPassCamera(int kernelLength) 139 | { 140 | // Create texture for deferred rendering (2nd pass - blur) 141 | secondPassTex = new osg::Texture2D; 142 | secondPassTex->setTextureSize(m_width, m_height); 143 | secondPassTex->setInternalFormat(GL_RGBA); 144 | 145 | // Create ssao camera for deffered rendering (first pass) 146 | ssaoCamera = createRTTCamera(osg::Camera::COLOR_BUFFER, 147 | secondPassTex.get(), 148 | true); 149 | 150 | // Load ssao shader 151 | osg::ref_ptr ssaoProgram = new osg::Program; 152 | osg::Shader* ssaoVertexObject = new osg::Shader(osg::Shader::VERTEX); 153 | osg::Shader* ssaoFragmentObject = new osg::Shader(osg::Shader::FRAGMENT); 154 | ssaoProgram->addShader(ssaoFragmentObject); 155 | ssaoProgram->addShader(ssaoVertexObject); 156 | setShaderStringFromResource(ssaoVertexObject, ":/shaders/ssao.vp"); 157 | setShaderStringFromResource(ssaoFragmentObject, ":/shaders/ssao.fp"); 158 | 159 | osg::StateSet* stateset = ssaoCamera->getOrCreateStateSet(); 160 | 161 | stateset->setTextureAttributeAndModes(0, colorTex.get()); 162 | stateset->setTextureAttributeAndModes(1, createTexture2D(m_noiseSize, 163 | m_noiseSize, 164 | m_noiseData)); 165 | stateset->setTextureAttributeAndModes(2, linearDepthTex.get()); 166 | stateset->setTextureAttributeAndModes(3, normalTex.get()); 167 | 168 | stateset->setAttributeAndModes(ssaoProgram.get()); 169 | stateset->addUniform(new osg::Uniform("colorTexture", 0)); 170 | stateset->addUniform(new osg::Uniform("noiseTexture", 1)); 171 | stateset->addUniform(new osg::Uniform("linearDepthTexture", 2)); 172 | stateset->addUniform(new osg::Uniform("normalTexture", 3)); 173 | 174 | radiusUniform = new osg::Uniform("ssaoRadius", m_ssaoRadius); 175 | stateset->addUniform(radiusUniform); 176 | 177 | powerUniform = new osg::Uniform("ssaoPower", m_ssaoPower); 178 | stateset->addUniform(powerUniform); 179 | 180 | 181 | stateset->addUniform(new osg::Uniform("kernelSize", kernelLength)); 182 | 183 | noiseTextureRcpUniform = 184 | new osg::Uniform("noiseTextureRcp", 185 | osg::Vec2f(float(m_width) / float(m_noiseSize), 186 | (float(m_height)/float(m_noiseSize)))); 187 | stateset->addUniform(noiseTextureRcpUniform); 188 | 189 | projMatUniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "projMatrix", 1); 190 | stateset->addUniform(projMatUniform); 191 | 192 | invProjMatrixUniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "invProjMatrix", 1); 193 | stateset->addUniform(invProjMatrixUniform); 194 | 195 | addKernelUniformToStateSet(stateset, kernelLength); 196 | 197 | ssaoCamera->setRenderOrder(osg::Camera::PRE_RENDER, 1); 198 | this->addChild(ssaoCamera.get()); 199 | 200 | } 201 | 202 | void SSAONode::createThirdPassCamera() 203 | { 204 | // Create blur camera for deffered rendering (second pass) 205 | blurCamera = createHUDCamera(0.0, 1.0, 0.0, 1.0); 206 | blurCamera->addChild(createScreenQuad(1.0f, 1.0f)); 207 | 208 | // Load blur shader 209 | osg::ref_ptr blurProgram = new osg::Program; 210 | osg::Shader* blurVertexObject = new osg::Shader(osg::Shader::VERTEX); 211 | osg::Shader* blurFragmentObject = new osg::Shader(osg::Shader::FRAGMENT); 212 | blurProgram->addShader(blurFragmentObject); 213 | blurProgram->addShader(blurVertexObject); 214 | setShaderStringFromResource(blurVertexObject, ":/shaders/blur.vp"); 215 | setShaderStringFromResource(blurFragmentObject, ":/shaders/blur.fp"); 216 | 217 | // Set blur shader to blur camera 218 | osg::StateSet* statesetBlur = blurCamera->getOrCreateStateSet(); 219 | statesetBlur->setTextureAttributeAndModes(0, secondPassTex.get()); // Set screen texture from first pass to texture channel 0 220 | statesetBlur->setTextureAttributeAndModes(1, linearDepthTex.get()); 221 | statesetBlur->setAttributeAndModes(blurProgram.get()); 222 | 223 | statesetBlur->addUniform(new osg::Uniform("sceneTex", 0)); 224 | statesetBlur->addUniform(new osg::Uniform("linearDepthTexture", 1)); 225 | statesetBlur->addUniform(new osg::Uniform("uBlurSize", int(m_blurSize))); 226 | 227 | haloRemovalUniform = new osg::Uniform("haloRemoval", m_haloRemovalEnabled ? 1 : 0); 228 | haloTresholdUniform = new osg::Uniform("haloTreshold", m_haloTreshold); 229 | blurAOUniform = new osg::Uniform("blurAOEnable", m_blurAOEnabled ? 1 : 0); 230 | 231 | statesetBlur->addUniform(haloRemovalUniform); 232 | statesetBlur->addUniform(haloTresholdUniform); 233 | statesetBlur->addUniform(blurAOUniform); 234 | 235 | blurProjMatrixUniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "projMatrix", 1); 236 | statesetBlur->addUniform(blurProjMatrixUniform); 237 | 238 | sceneSizeUniform = new osg::Uniform("sceneSize", osg::Vec2f(m_width, m_height)); 239 | statesetBlur->addUniform(sceneSizeUniform); 240 | 241 | displayTypeUniform = new osg::Uniform("displayType", displayType); 242 | statesetBlur->addUniform(displayTypeUniform); 243 | 244 | // Ensure rendering order 245 | blurCamera->setRenderOrder(osg::Camera::POST_RENDER, 0); 246 | 247 | this->addChild(blurCamera.get()); 248 | } 249 | 250 | void SSAONode::Initialize() 251 | { 252 | int kernelLength = m_kernelSize * m_kernelSize; 253 | 254 | if (m_noiseData == NULL) 255 | { 256 | long nsquared = m_noiseSize * m_noiseSize; 257 | m_noiseData = new osg::Vec3f[nsquared]; 258 | generateNoise(m_noiseData, nsquared); 259 | } 260 | 261 | removeAttachedCameras(); 262 | 263 | createFirstPassCamera(); 264 | 265 | createSecondPassCamera(kernelLength); 266 | 267 | createThirdPassCamera(); 268 | // ------------------------------------------------------------------------------------------------------------------------ 269 | 270 | // ------------------------------------------------------------------------------------------------------------------------ 271 | 272 | // Set user definable uniforms 273 | setUniforms(); 274 | 275 | // Create ssao group 276 | } 277 | 278 | bool SSAONode::IsHaloRemovalEnabled() { 279 | return this->m_haloRemovalEnabled; 280 | } 281 | 282 | bool SSAONode::IsAOBlurEnabled() { 283 | return this->m_blurAOEnabled; 284 | } 285 | 286 | void SSAONode::SetHaloTreshold(float treshold) { 287 | this->m_haloTreshold = treshold; 288 | } 289 | 290 | float SSAONode::GetHaloTreshold() { 291 | return this->m_haloTreshold; 292 | } 293 | 294 | void SSAONode::addNode(osg::Node* node) 295 | { 296 | rttCamera->addChild(node); 297 | } 298 | 299 | void SSAONode::Resize(int width, int height) 300 | { 301 | if (width == this->m_width && height == this->m_height) return; 302 | 303 | m_width = width; 304 | m_height = height; 305 | 306 | // Remember nodes attached to ssao 307 | unsigned int nodeNum = rttCamera->getNumChildren(); 308 | 309 | std::vector nodes; 310 | 311 | for (unsigned int i = 0; i < nodeNum; ++i){ 312 | nodes.push_back(rttCamera->getChild(i)); 313 | } 314 | 315 | // Initialize again 316 | Initialize(); 317 | 318 | // Add nodes back 319 | for (unsigned int i = 0; i < nodeNum; ++i){ 320 | addNode(nodes[i]); 321 | } 322 | 323 | sceneSizeUniform->set(osg::Vec2f(width, height)); 324 | noiseTextureRcpUniform->set(osg::Vec2f(float(width) / float(m_noiseSize), (float(height) / float(m_noiseSize)))); 325 | } 326 | 327 | void SSAONode::SetSSAORadius(float radius) { 328 | this->m_ssaoRadius = radius; 329 | setUniforms(); 330 | } 331 | 332 | void SSAONode::SetSSAOPower(float power) { 333 | this->m_ssaoPower = power; 334 | setUniforms(); 335 | } 336 | 337 | void SSAONode::SetDisplayMode(SSAONode::DisplayMode mode) 338 | { 339 | this->displayType = mode; 340 | setUniforms(); 341 | } 342 | 343 | float SSAONode::GetSSAORadius() { 344 | return this->m_ssaoRadius; 345 | } 346 | 347 | float SSAONode::GetSSAOPower() { 348 | return this->m_ssaoPower; 349 | } 350 | 351 | SSAONode::DisplayMode SSAONode::GetDisplayMode() { 352 | return this->displayType; 353 | } 354 | 355 | void SSAONode::setProjectionMatrixUniforms() 356 | { 357 | projMatUniform->set(projMatrix); 358 | invProjMatrixUniform->set(osg::Matrixd::inverse(projMatrix)); 359 | blurProjMatrixUniform->set(projMatrix); 360 | } 361 | 362 | void SSAONode::setUniforms() 363 | { 364 | setProjectionMatrixUniforms(); 365 | radiusUniform->set(m_ssaoRadius); 366 | powerUniform->set(m_ssaoPower); 367 | displayTypeUniform->set((int) displayType); 368 | haloRemovalUniform->set(m_haloRemovalEnabled ? 1 : 0); 369 | haloTresholdUniform->set(m_haloTreshold); 370 | blurAOUniform->set(m_blurAOEnabled ? 1 : 0); 371 | } 372 | 373 | void SSAONode::updateProjectionMatrix(osg::Matrixd projMatrix) 374 | { 375 | this->projMatrix = projMatrix; 376 | setProjectionMatrixUniforms(); 377 | } 378 | 379 | // Random number generator 380 | unsigned int SSAONode::xorshift32() { 381 | static unsigned int x = 1424447641; 382 | x ^= x << 13; 383 | x ^= x >> 17; 384 | x ^= x << 5; 385 | return x; 386 | } 387 | 388 | float SSAONode::random(float min, float max) { 389 | return min + static_cast (xorshift32()) / 390 | ( static_cast (0xFFFFFFFF/(max-min)) ); 391 | } 392 | 393 | float SSAONode::lerp(float min, float max, float t) { 394 | return min * (1.0f - t) + max * t; 395 | } 396 | 397 | 398 | void SSAONode::generateHemisphereSamples(osg::Vec3f* kernel, size_t kernelSize) 399 | { 400 | QFile f("kernel.obj"); 401 | f.open(QIODevice::WriteOnly); 402 | QTextStream fs(&f); 403 | fs << "o kernel" << "\n"; 404 | for (size_t i = 0; i < kernelSize; ++i) { 405 | 406 | // Actually, do not generate hemisphere, 407 | // but a cone to minimize self-intersections 408 | kernel[i] = osg::Vec3f( 409 | random(-0.95f, 0.95f), 410 | random(-0.95f, 0.95f), 411 | random(0.0f, 1.0f)); 412 | 413 | kernel[i].normalize(); 414 | fs << "v " 415 | << kernel[i].x() 416 | << " " 417 | << kernel[i].y() 418 | << " " 419 | << kernel[i].z() 420 | << "\n"; 421 | 422 | #if 1 423 | // XXX why are we scaling by the index into the kernel? 424 | float scale = float(i) / float(kernelSize); 425 | scale = lerp(0.1f, 1.0f, scale * scale); 426 | kernel[i] *= scale; 427 | #endif 428 | fs << "f " << i << "\n"; 429 | } 430 | fs.flush(); 431 | f.flush(); 432 | f.close(); 433 | } 434 | 435 | void SSAONode::generateNoise(osg::Vec3f* noise, size_t noiseSize) { 436 | 437 | for (size_t i = 0; i < noiseSize; ++i) { 438 | noise[i] = osg::Vec3f( 439 | random(-1.0f, 1.0f), 440 | random(-1.0f, 1.0f), 441 | 0.0f 442 | ); 443 | 444 | noise[i].normalize(); 445 | 446 | // Scale to <0; 1> - it will be stored in RGB24 447 | noise[i] = noise[i] + osg::Vec3f(1.0f, 1.0f, 1.0f); 448 | noise[i] = noise[i] / 2.0f; 449 | } 450 | } 451 | 452 | 453 | 454 | osg::Camera* SSAONode::createRTTCamera(osg::Camera::BufferComponent buffer, osg::Texture* tex, bool isAbsolute) 455 | { 456 | osg::ref_ptr camera = new osg::Camera; 457 | camera->setClearColor( osg::Vec4() ); 458 | camera->setClearMask( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT ); 459 | camera->setRenderTargetImplementation( osg::Camera::FRAME_BUFFER_OBJECT ); 460 | camera->setRenderOrder( osg::Camera::PRE_RENDER ); 461 | if ( tex ) 462 | { 463 | tex->setFilter( osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR ); 464 | tex->setFilter( osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR ); 465 | camera->setViewport( 0, 0, tex->getTextureWidth(), tex->getTextureHeight() ); 466 | camera->attach( buffer, tex ); 467 | } 468 | 469 | if ( isAbsolute ) 470 | { 471 | camera->setReferenceFrame( osg::Transform::ABSOLUTE_RF ); 472 | camera->setProjectionMatrix( osg::Matrix::ortho2D(0.0, 1.0, 0.0, 1.0) ); 473 | camera->setViewMatrix( osg::Matrix::identity() ); 474 | camera->addChild( createScreenQuad(1.0f, 1.0f) ); 475 | } 476 | return camera.release(); 477 | } 478 | 479 | osg::Camera* SSAONode::createRTTCameraGBuffer( osg::Camera::BufferComponent buffer1, 480 | osg::Texture* tex1, 481 | osg::Camera::BufferComponent buffer2, 482 | osg::Texture* tex2, 483 | osg::Camera::BufferComponent buffer3, 484 | osg::Texture* tex3, 485 | bool isAbsolute ) 486 | { 487 | osg::ref_ptr camera = new osg::Camera; 488 | camera->setClearColor( osg::Vec4(0.2, 0.2, 0.4, 1.0) ); 489 | camera->setClearMask( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT ); 490 | camera->setRenderTargetImplementation( osg::Camera::FRAME_BUFFER_OBJECT ); 491 | camera->setRenderOrder( osg::Camera::PRE_RENDER ); 492 | if ( tex1 ) 493 | { 494 | tex1->setFilter( osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR ); 495 | tex1->setFilter( osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR ); 496 | camera->setViewport( 0, 0, tex1->getTextureWidth(), tex1->getTextureHeight() ); 497 | camera->attach( buffer1, tex1 ); 498 | } 499 | 500 | if ( tex2 ) 501 | { 502 | tex2->setFilter( osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR ); 503 | tex2->setFilter( osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR ); 504 | camera->setViewport( 0, 0, tex2->getTextureWidth(), tex2->getTextureHeight() ); 505 | camera->attach( buffer2, tex2 ); 506 | } 507 | 508 | if ( tex3 ) 509 | { 510 | tex3->setFilter( osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR ); 511 | tex3->setFilter( osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR ); 512 | camera->setViewport( 0, 0, tex3->getTextureWidth(), tex3->getTextureHeight() ); 513 | camera->attach( buffer3, tex3 ); 514 | } 515 | 516 | if ( isAbsolute ) 517 | { 518 | camera->setReferenceFrame( osg::Transform::ABSOLUTE_RF ); 519 | camera->setProjectionMatrix( osg::Matrix::ortho2D(0.0, 1.0, 0.0, 1.0) ); 520 | camera->setViewMatrix( osg::Matrix::identity() ); 521 | camera->addChild(createScreenQuad(1.0f, 1.0f) ); 522 | } 523 | return camera.release(); 524 | } 525 | 526 | osg::Texture* SSAONode::createTexture2D(int width, int height, osg::Vec3f* data) 527 | { 528 | osg::ref_ptr texture = new osg::Texture2D; 529 | osg::Image* image = new osg::Image; 530 | 531 | image->setImage(width, height, 1, GL_RGB, GL_RGB, GL_FLOAT, (unsigned char*) data, osg::Image::NO_DELETE); 532 | 533 | texture->setImage( image ); 534 | texture->setWrap( osg::Texture::WRAP_S, osg::Texture::REPEAT ); 535 | texture->setWrap( osg::Texture::WRAP_T, osg::Texture::REPEAT ); 536 | texture->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR ); 537 | texture->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR ); 538 | return texture.release(); 539 | } 540 | 541 | osg::Geode* SSAONode::createScreenQuad( float width, float height, float scale ) 542 | { 543 | osg::Geometry* geom = 544 | osg::createTexturedQuadGeometry( 545 | osg::Vec3(), 546 | osg::Vec3(width,0.0f,0.0f), 547 | osg::Vec3(0.0f,height,0.0f), 548 | 0.0f, 549 | 0.0f, 550 | width*scale, 551 | height*scale ); 552 | 553 | osg::ref_ptr quad = new osg::Geode; 554 | quad->addDrawable( geom ); 555 | 556 | int values = osg::StateAttribute::OFF|osg::StateAttribute::PROTECTED; 557 | quad->getOrCreateStateSet()->setAttribute( 558 | new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, 559 | osg::PolygonMode::FILL), 560 | values ); 561 | quad->getOrCreateStateSet()->setMode( GL_LIGHTING, values ); 562 | return quad.release(); 563 | } 564 | 565 | osg::Camera* SSAONode::createHUDCamera( double left, double right, 566 | double bottom, double top ) 567 | { 568 | osg::ref_ptr camera = new osg::Camera; 569 | camera->setReferenceFrame( osg::Transform::ABSOLUTE_RF ); 570 | camera->setClearMask( GL_DEPTH_BUFFER_BIT ); 571 | camera->setRenderOrder( osg::Camera::POST_RENDER ); 572 | camera->setAllowEventFocus( false ); 573 | camera->setProjectionMatrix( osg::Matrix::ortho2D(left, right, bottom, top) ); 574 | camera->getOrCreateStateSet()->setMode( GL_LIGHTING, osg::StateAttribute::OFF ); 575 | return camera.release(); 576 | } 577 | 578 | #include 579 | #include 580 | bool SSAONode::setShaderStringFromResource(osg::Shader* shader, 581 | const std::string resourceName) 582 | { 583 | QFile file( QString::fromStdString(resourceName) ); 584 | if (!file.open(QIODevice::ReadOnly)) { 585 | qDebug("%s does ont exist", resourceName.c_str()); 586 | return false; 587 | } 588 | 589 | QString str = file.readAll(); 590 | file.close(); 591 | shader->setShaderSource(str.toStdString()); 592 | return true; 593 | } 594 | -------------------------------------------------------------------------------- /CameraModel.cpp: -------------------------------------------------------------------------------- 1 | #include "CameraModel.h" 2 | #include 3 | #include 4 | #include 5 | 6 | CameraModel::CameraModel(QObject *parent) 7 | : osg::Object() 8 | , QObject(parent) 9 | , m_viewUp(osg::Vec3d(0., 0., 1.)) 10 | , m_viewDir(osg::Vec3d(0., 1., 0.)) 11 | , m_viewCenter(osg::Vec3d( 0., 0., 0.)) 12 | , m_fovY(30.0) 13 | , m_viewDistance(20.0) 14 | , m_aspect(1.0) 15 | , m_ortho(false) 16 | , m_orthoBottom(0.0) 17 | , m_orthoTop(0.0) 18 | 19 | , m_fovYScaleFactor(1.1) 20 | , m_clampFovyRange( osg::Vec2d( 5.0, 160.0 ) ) 21 | , m_shouldClampFovYScale(true) 22 | , m_trackballRollSensitivity(1.3) 23 | , m_dollyCanChangeCenter(true) 24 | , m_dollyCenterChangeThreshold(10) 25 | , m_dollyCurrentPressure(0) 26 | , m_panPlane( osg::Vec4d(0.0, 0.0, 0.0, 0.0) ) 27 | , m_viewChangeInProgress(false) 28 | , m_viewChangeMatrix(osg::Matrixd::identity()) 29 | , m_startingNDC( osg::Vec2d( 0.0, 0.0) ) 30 | , m_stashedView("") 31 | , m_cullMask(~0) 32 | { 33 | 34 | } 35 | 36 | CameraModel::CameraModel(const CameraModel &rhs, const osg::CopyOp ©op) 37 | : osg::Object(rhs, copyop) 38 | , QObject() 39 | , m_viewUp(rhs.m_viewUp) 40 | , m_viewDir(rhs.m_viewDir) 41 | , m_viewCenter(rhs.m_viewCenter) 42 | , m_fovY(rhs.m_fovY) 43 | , m_viewDistance(rhs.m_viewDistance) 44 | , m_aspect(rhs.m_aspect) 45 | , m_ortho(rhs.m_ortho) 46 | , m_orthoBottom(rhs.m_orthoBottom) 47 | , m_orthoTop(rhs.m_orthoTop) 48 | , m_fovYScaleFactor(rhs.m_fovYScaleFactor) 49 | , m_clampFovyRange(rhs.m_clampFovyRange) 50 | , m_shouldClampFovYScale(rhs.m_shouldClampFovYScale) 51 | , m_trackballRollSensitivity(rhs.m_trackballRollSensitivity) 52 | , m_dollyCanChangeCenter(rhs.m_dollyCanChangeCenter) 53 | , m_dollyCenterChangeThreshold(rhs.m_dollyCenterChangeThreshold) 54 | , m_dollyCurrentPressure(0) 55 | , m_panPlane(rhs.m_panPlane) 56 | , m_viewChangeInProgress(false) 57 | , m_stashedView(rhs.m_stashedView) 58 | , m_startingNDC(rhs.m_startingNDC) 59 | , m_cullMask(rhs.m_cullMask) 60 | 61 | { 62 | 63 | } 64 | 65 | void CameraModel::computeInitialView() 66 | { 67 | m_viewUp = osg::Vec3d(0., 0., 1.); 68 | m_viewDir = osg::Vec3d(0., 1., 0.); 69 | 70 | fitToScreen(); 71 | } 72 | 73 | double CameraModel::getFovyRadians() const 74 | { 75 | return( osg::DegreesToRadians( m_fovY ) ); 76 | } 77 | #include "VectorFunctions.h" 78 | osg::Vec3d CameraModel::getAzElTwist() const 79 | { 80 | #if 0 81 | 82 | const double twoPi( 2. * osg::PI ); 83 | osg::Vec3d xAxis(1.0, 0.0, 0.0); 84 | osg::Vec3d yAxis(0.0, 1.0, 0.0); 85 | osg::Vec3d zAxis(0.0, 0.0, 1.0); 86 | 87 | osg::Vec3d rightHandDirection = m_viewDir ^ zAxis; 88 | 89 | osg::Vec3d viewDirInYXPlane = zAxis ^ rightHandDirection; 90 | viewDirInYXPlane.normalize(); 91 | 92 | double cosAngleRadiansInPlane = viewDirInYXPlane * xAxis; 93 | double azimuthInRadians = acos(cosAngleRadiansInPlane); 94 | 95 | bool pointsTowardsNegativeYAxis = (ViewDirInXYPlane * yAxis) < 0.0; 96 | if ( pointsTowardsNegativeYAxis ) 97 | azimuthInRadians = twoPi - azimuthInRadians; 98 | 99 | 100 | double cosAngleRadiansOfElevation = m_viewDir * viewDirInYXPlane; 101 | double elevationInRadians = acos(cosAngleRadiansOfElevation); 102 | 103 | 104 | return osg::Vec3d(osg::RadiansToDegrees( azimuthInRadians ), 105 | osg::RadiansToDegrees(elevationInRadians), 106 | 0.0); 107 | #else 108 | abort(); 109 | #endif 110 | } 111 | 112 | osg::Vec3d CameraModel::getYawPitchRoll() const 113 | { 114 | osg::Vec3d xAxis(1.0, 0.0, 0.0); 115 | osg::Vec3d yAxis(0.0, 1.0, 0.0); 116 | osg::Vec3d zAxis(0.0, 0.0, 1.0); 117 | const osg::Vec3d viewDirXBaseUp( m_viewDir ^ zAxis ); 118 | const double twoPi( 2. * osg::PI ); 119 | 120 | 121 | // Yaw 122 | 123 | // Compute view direction, projected into plane defined by base up. 124 | // TBD what if _viewDir and _baseUp are coincident? 125 | osg::Vec3d projectedDir = zAxis ^ viewDirXBaseUp; 126 | projectedDir.normalize(); 127 | // Is the vector pointing to the left of north, or to the right? 128 | //osg::Vec3d right = xAxis ^ zAxis; 129 | const double dotDirRight = projectedDir * -yAxis; 130 | // Dot product of two unit vectors is the cosine of the angle between them. 131 | const double dotDirNorth = preAcosClamp(projectedDir * xAxis, -1.0, 1.0); 132 | double yawRad = acos( dotDirNorth ); 133 | if( dotDirRight > 0. ) 134 | yawRad = osg::PI + ( osg::PI - yawRad ); 135 | 136 | if( yawRad == twoPi ) 137 | yawRad = 0.; 138 | double yaw = osg::RadiansToDegrees( yawRad ); 139 | 140 | 141 | // Pitch 142 | 143 | const double dotDirUp = m_viewDir * zAxis; 144 | const double dotUpUp = preAcosClamp(m_viewUp * zAxis, -1.0, 1.0); 145 | double pitchRad = acos( osg::absolute< double >( dotUpUp ) ); 146 | if( dotDirUp < 0. ) 147 | pitchRad *= -1.; 148 | double pitch = osg::RadiansToDegrees( pitchRad ); 149 | 150 | 151 | // Roll 152 | 153 | // Compute base up projected onto plane defined by view direction. 154 | // TBD what if _viewDir and _baseUp are coincident? 155 | osg::Vec3d projectedBaseUp = viewDirXBaseUp ^ m_viewDir; 156 | projectedBaseUp.normalize(); 157 | // Is the view up vector pointing to the left of the projected base up, or to the right? 158 | osg::Vec3d right = m_viewDir ^ projectedBaseUp; 159 | const double dotUpRight = m_viewUp * right; 160 | // Dot product of two unit vectors is the cosine of the angle between them. 161 | const double dotUp = preAcosClamp(projectedBaseUp * m_viewUp, -1.0, 1.0); 162 | double rollRad = acos( dotUp ); 163 | if( dotUpRight > 0. ) 164 | rollRad = osg::PI + ( osg::PI - rollRad ); 165 | 166 | if( rollRad == twoPi ) 167 | rollRad = 0.; 168 | double roll = osg::RadiansToDegrees( rollRad ); 169 | 170 | return osg::Vec3d(yaw, pitch, roll); 171 | } 172 | 173 | osg::Matrixd CameraModel::computeProjection() const 174 | { 175 | if( !( m_boundingNode.valid() ) ) { 176 | osg::notify( osg::WARN ) << "CameraModel::computeProjection: _scene == NULL." << std::endl; 177 | return( osg::Matrixd::identity() ); 178 | } 179 | 180 | // TBD do we really want eyeToCenter to be a vector 181 | // to the *bound* center, or to the *view* center? 182 | const osg::BoundingSphere& bs = m_boundingNode->getBound(); 183 | 184 | const osg::Vec3d eyeToCenter( bs._center - getEyePosition() ); 185 | if( m_ortho ) { 186 | double zNear = eyeToCenter.length() - bs._radius; 187 | double zFar = eyeToCenter.length() + bs._radius; 188 | 189 | const double xRange = m_aspect * ( m_orthoTop - m_orthoBottom ); 190 | const double right = xRange * .5; 191 | 192 | return( osg::Matrixd::ortho( -right, right, m_orthoBottom, m_orthoTop, zNear, zFar ) ); 193 | } else { 194 | double zNear = eyeToCenter.length() - bs._radius; 195 | double zFar = zNear + ( bs._radius * 2. ); 196 | if( zNear < 0. ) { 197 | zNear = zFar / 2000.; // Default z ratio. 198 | } 199 | return( osg::Matrixd::perspective( m_fovY, m_aspect, zNear, zFar ) ); 200 | } 201 | } 202 | 203 | osg::Matrixd CameraModel::getModelViewMatrix() const 204 | { 205 | osg::Matrixd m; 206 | m.invert( getMatrix() ); 207 | return( m ); 208 | } 209 | 210 | osg::Matrixd CameraModel::getMatrix() const 211 | { 212 | const osg::Vec3d& d = m_viewDir; 213 | const osg::Vec3d& u = m_viewUp; 214 | osg::Vec3d r = d ^ u; 215 | const osg::Vec3d p = getEyePosition(); 216 | 217 | osg::Matrixd m = osg::Matrixd( 218 | r[0], r[1], r[2], 0.0, 219 | u[0], u[1], u[2], 0.0, 220 | -d[0], -d[1], -d[2], 0.0, 221 | p[0], p[1], p[2], 1.0 ); 222 | 223 | return( m ); 224 | } 225 | 226 | osg::Vec3d CameraModel::getEyePosition() const 227 | { 228 | return( m_viewCenter - ( m_viewDir * m_viewDistance ) ); 229 | } 230 | 231 | 232 | void CameraModel::setEyePosition(osg::Vec3d p) 233 | { 234 | osg::Vec3d dir = p - m_viewCenter; 235 | 236 | double viewDistance = dir.normalize(); 237 | 238 | if (viewDistance == m_viewDistance && dir == m_viewDir) 239 | return; 240 | 241 | m_viewDistance = viewDistance; 242 | m_viewDir = dir; 243 | 244 | emit changed(); 245 | } 246 | 247 | void CameraModel::setAspect(double a) 248 | { 249 | if (m_aspect == a) return; 250 | m_aspect = a; 251 | emit changed(); 252 | } 253 | 254 | void CameraModel::setOrtho(bool tf) 255 | { 256 | if (tf == m_ortho) return; 257 | 258 | m_ortho = tf; 259 | emit changed(); 260 | } 261 | void CameraModel::setOrthoFromQAction() 262 | { 263 | QAction *a = dynamic_cast(sender()); 264 | if (a) 265 | setOrtho( a->data().toBool() ); 266 | } 267 | void CameraModel::fovYScaleUp() 268 | { 269 | m_fovY *= m_fovYScaleFactor; 270 | if( m_shouldClampFovYScale ) { 271 | m_fovY = osg::clampBelow< double >( m_fovY, m_clampFovyRange.y() ); 272 | } 273 | 274 | m_orthoBottom *= m_fovYScaleFactor; 275 | m_orthoTop *= m_fovYScaleFactor; 276 | 277 | emit changed(); 278 | } 279 | 280 | void CameraModel::fovYScaleDown() 281 | { 282 | const double factor( 1.0 / m_fovYScaleFactor ); 283 | m_fovY *= factor; 284 | if( m_shouldClampFovYScale ) { 285 | m_fovY = osg::clampAbove< double >( m_fovY, m_clampFovyRange.x() ); 286 | } 287 | 288 | m_orthoBottom *= factor; 289 | m_orthoTop *= factor; 290 | emit changed(); 291 | } 292 | 293 | void CameraModel::setClampFovyScale(bool clamp, osg::Vec2d range) 294 | { 295 | m_shouldClampFovYScale = clamp; 296 | m_clampFovyRange = range; 297 | 298 | if (m_shouldClampFovYScale) { 299 | m_fovY = osg::clampBetween< double >( m_fovY, 300 | m_clampFovyRange.x(), 301 | m_clampFovyRange.y() ); 302 | } 303 | emit changed(); 304 | } 305 | 306 | 307 | void CameraModel::setViewDirFromAzEl(osg::Vec2d aet) 308 | { 309 | osg::Vec3d xAxis(1.0, 0.0, 0.0); 310 | osg::Vec3d yAxis(0.0, 1.0, 0.0); 311 | osg::Vec3d zAxis(0.0, 0.0, 1.0); 312 | 313 | osg::Matrixd mat; 314 | mat.makeRotate(osg::DegreesToRadians(aet[1]), yAxis); 315 | m_viewUp = zAxis * mat; 316 | m_viewDir = xAxis * mat; 317 | 318 | mat.makeRotate(osg::DegreesToRadians(aet[0]+180.0), zAxis); 319 | m_viewUp = m_viewUp * mat; 320 | m_viewDir = m_viewDir * mat; 321 | 322 | emit changed(); 323 | } 324 | 325 | void CameraModel::saveView(std::stringstream &stream) 326 | { 327 | stream << m_viewUp.x() << " " 328 | << m_viewUp.y() << " " 329 | << m_viewUp.z() << " " 330 | << m_viewDir.x() << " " 331 | << m_viewDir.y() << " " 332 | << m_viewDir.z() << " " 333 | << m_viewCenter.x() << " " 334 | << m_viewCenter.y() << " " 335 | << m_viewCenter.z() << " "; 336 | stream << m_viewDistance << " "; 337 | stream << m_fovY << " "; 338 | 339 | stream << (m_ortho?"true ":"false "); 340 | 341 | stream << m_aspect << " " 342 | << m_orthoBottom << " " 343 | << m_orthoTop << " "; 344 | } 345 | 346 | void CameraModel::loadView(std::stringstream &stream) 347 | { 348 | stream >> m_viewUp.x() >> m_viewUp.y() >> m_viewUp.z(); 349 | stream >> m_viewDir.x() >> m_viewDir.y() >> m_viewDir.z(); 350 | stream >> m_viewCenter.x() >> m_viewCenter.y() >> m_viewCenter.z(); 351 | stream >> m_viewDistance; 352 | stream >> m_fovY; 353 | std::string orthoString; 354 | stream >> orthoString; 355 | m_ortho = (orthoString == "true"); 356 | stream >> m_aspect; 357 | stream >> m_orthoBottom; 358 | stream >> m_orthoTop; 359 | } 360 | 361 | 362 | 363 | void CameraModel::stashView() 364 | { 365 | std::stringstream ss; 366 | saveView(ss); 367 | ss.flush(); 368 | 369 | m_stashedView = ss.str(); 370 | } 371 | 372 | void CameraModel::restoreView() 373 | { 374 | if (m_stashedView.size() < 0) return; 375 | 376 | std::stringstream ss(m_stashedView); 377 | loadView(ss); 378 | } 379 | 380 | void CameraModel::startOrbit(osg::Vec2d startingNDC) 381 | { 382 | m_startingNDC = startingNDC; 383 | m_viewChangeInProgress = true; 384 | } 385 | 386 | void CameraModel::orbit(osg::Vec2d currentNDC) 387 | { 388 | osg::Vec2d deltaNDC = currentNDC - m_startingNDC; 389 | 390 | const osg::Matrixd orientMat = getOrientationMatrix(); 391 | 392 | // Take the spin direction 'dir' and rotate it 90 degrees 393 | // to get our base axis (still in the window plane). 394 | // Simultaneously convert to current view space. 395 | osg::Vec2d screenAxis( -deltaNDC[ 1 ], deltaNDC[ 0 ] ); 396 | const osg::Vec3d baseAxis = osg::Vec3d( screenAxis[ 0 ], screenAxis[ 1 ], 0. ) * orientMat; 397 | osg::Vec3d dir3 = osg::Vec3d( deltaNDC[ 0 ], deltaNDC[ 1 ], 0. ) * orientMat; 398 | dir3.normalize(); 399 | 400 | // The distance from center, along with the roll sensitivity, 401 | // tells us how much to rotate the baseAxis (ballTouchAngle) to get 402 | // the actual ballAxis. 403 | const double distance = m_startingNDC.length(); 404 | const double rotationDir( ( screenAxis * m_startingNDC > 0. ) ? -1. : 1. ); 405 | const double ballTouchAngle = rotationDir * m_trackballRollSensitivity * distance; 406 | osg::Vec3d ballAxis = baseAxis * osg::Matrixd::rotate( ballTouchAngle, dir3 ); 407 | ballAxis.normalize(); 408 | 409 | osg::Matrixd m = osg::Matrixd::rotate( -( deltaNDC.length() ), ballAxis ); 410 | 411 | // Re-orient the basis. 412 | m_viewDir = m_viewDir * m; 413 | m_viewUp = m_viewUp * m; 414 | 415 | orthoNormalize(); 416 | 417 | m_startingNDC = currentNDC; 418 | 419 | emit changed(); 420 | } 421 | 422 | void CameraModel::finishOrbit(osg::Vec2d currentNDC) 423 | { 424 | m_viewChangeInProgress = false; 425 | orbit(currentNDC); 426 | } 427 | 428 | 429 | osg::Matrixd CameraModel::getOrientationMatrix() const 430 | { 431 | const osg::Vec3d& d = m_viewDir; 432 | const osg::Vec3d& u = m_viewUp; 433 | osg::Vec3d r = d ^ u; 434 | 435 | osg::Matrixd m = osg::Matrixd( 436 | r[0], r[1], r[2], 0.0, 437 | u[0], u[1], u[2], 0.0, 438 | -d[0], -d[1], -d[2], 0.0, 439 | 0.0, 0.0, 0.0, 1.0 ); 440 | return( m ); 441 | } 442 | 443 | 444 | void CameraModel::startRotate(osg::Vec2d startingNDC) 445 | { 446 | m_startingNDC = startingNDC; 447 | m_viewChangeInProgress = true; 448 | } 449 | 450 | void CameraModel::rotate(osg::Vec2d currentNDC) 451 | { 452 | osg::Vec2d deltaNDC = currentNDC - m_startingNDC; 453 | 454 | 455 | // Compute m_viewChangeMatrix here 456 | // Position is constant in 1st person view. Obtain it (for later use) 457 | // *before* we alter the _viewDir. 458 | const osg::Vec3d position = getEyePosition(); 459 | 460 | // Compute rotation matrix. 461 | osg::Vec3d cross = m_viewDir ^ m_viewUp; 462 | osg::Matrix m = osg::Matrix::rotate( deltaNDC[ 0 ], m_viewUp ) * 463 | osg::Matrix::rotate( -deltaNDC[ 1 ], cross ); 464 | 465 | // Re-orient the basis. 466 | m_viewDir = m_viewDir * m; 467 | m_viewUp = m_viewUp * m; 468 | orthoNormalize(); 469 | 470 | // Compute the new view center. 471 | m_viewCenter = position + ( m_viewDir * m_viewDistance ); 472 | 473 | m_startingNDC = currentNDC; 474 | emit changed(); 475 | } 476 | 477 | void CameraModel::finishRotate(osg::Vec2d currentNDC) 478 | { 479 | rotate(currentNDC); 480 | 481 | m_viewChangeInProgress = false; 482 | emit changed(); 483 | } 484 | 485 | void CameraModel::getZNearZFarProj(double &zNear, double &zFar, const osg::Matrixd &projMat) 486 | { 487 | if( m_ortho ) { 488 | double l, r, b, t; 489 | projMat.getOrtho( l, r, b, t, zNear, zFar ); 490 | } else { 491 | double fovy, aspect; 492 | projMat.getPerspective( fovy, aspect, zNear, zFar ); 493 | } 494 | } 495 | 496 | bool CameraModel::intersectPlaneRay( osg::Vec3d& result, const osg::Vec4d& plane, const osg::Vec3d& p0, const osg::Vec3d& p1 ) 497 | { 498 | osg::Vec3d planeNormal = osg::Vec3d( plane[ 0 ], plane[ 1 ], plane[ 2 ] ); 499 | 500 | osg::notify( osg::DEBUG_FP ) << " p0 " << p0 << std::endl; 501 | osg::notify( osg::DEBUG_FP ) << " p1 " << p1 << std::endl; 502 | const osg::Vec3d vDir = p1 - p0; 503 | const double dotVd = vDir * planeNormal; 504 | osg::notify( osg::DEBUG_FP ) << " dotVd " << dotVd << std::endl; 505 | if( dotVd == 0. ) { 506 | osg::notify( osg::WARN ) << "ViewingCore::intersectPlaneRay: No plane intersection." << std::endl; 507 | return( false ); 508 | } 509 | double length = -( planeNormal * p0 + plane[ 3 ] ) / dotVd; 510 | osg::notify( osg::DEBUG_FP ) << " length " << length << std::endl; 511 | result = p0 + ( vDir * length ); 512 | osg::notify( osg::DEBUG_FP ) << " intersection point " << result << std::endl; 513 | return( true ); 514 | } 515 | void CameraModel::startPan(osg::Vec2d startingNDC, osg::Vec4d panPlane) 516 | { 517 | m_startingNDC = startingNDC; 518 | m_viewChangeInProgress = true; 519 | m_panPlane = panPlane; 520 | } 521 | void CameraModel::pan(osg::Vec2d currentNDC) 522 | { 523 | // Get the view volume far plane value, and the distance from 524 | // the near to far plane. 525 | double zNear, zFar; 526 | osg::Vec2d deltaNDC = m_startingNDC - currentNDC; 527 | 528 | osg::Matrixd p = computeProjection(); 529 | 530 | getZNearZFarProj(zNear, zFar, p); 531 | 532 | const double distance = zFar - zNear; 533 | 534 | // Create two points, both in NDC space, and lying on the far plane at the back 535 | // of the view volume. One is the xy origin, the other with the passed xy parameters. 536 | osg::Vec4d farPoint0 = osg::Vec4d( 0., 0., 1., 1. ); 537 | osg::Vec4d farPoint1 = osg::Vec4d( deltaNDC.x(), deltaNDC.y(), 1., 1. ); 538 | if( !m_ortho ) { 539 | // Not ortho, so w != 1.0. Multiply by the far plane distance. 540 | // This yields values in clip coordinates. 541 | farPoint0 *= zFar; 542 | farPoint1 *= zFar; 543 | } 544 | 545 | // Get inverse view & proj matrices to back-transform the 546 | // two clip coord far points into world space. 547 | osg::Matrixd v = getMatrix(); 548 | 549 | osg::Matrixd invProjMat; 550 | invProjMat.invert( p ); 551 | 552 | osg::Vec4d wc0 = farPoint0 * invProjMat * v; 553 | osg::Vec4d wc1 = farPoint1 * invProjMat * v; 554 | 555 | // Intersect the two world coord points with the pan plane. 556 | osg::Vec3d result0, result1; 557 | osg::Vec3d p1( wc0.x(), wc0.y(), wc0.z() ); 558 | osg::Vec3d p0 = m_ortho ? p1 - ( m_viewDir * distance ) : getEyePosition(); 559 | intersectPlaneRay( result0, m_panPlane, p0, p1 ); 560 | p1 = osg::Vec3d( wc1.x(), wc1.y(), wc1.z() ); 561 | p0 = m_ortho ? p1 - ( m_viewDir * distance ) : getEyePosition(); 562 | intersectPlaneRay( result1, m_panPlane, p0, p1 ); 563 | 564 | // Subtract the two plane intersection points to get the delta world coord 565 | // motion and move the view center accordingly. 566 | osg::Vec3d delta = result1 - result0; 567 | osg::notify( osg::DEBUG_FP ) << " delta " << delta << std::endl; 568 | m_viewCenter += delta; 569 | 570 | m_startingNDC = currentNDC; 571 | // m_viewChangeMatrix.makeTranslate(delta); 572 | 573 | emit changed(); 574 | } 575 | 576 | void CameraModel::finishPan(osg::Vec2d currentNDC) 577 | { 578 | m_viewChangeInProgress = false; 579 | pan(currentNDC); 580 | } 581 | 582 | void CameraModel::startZoom(osg::Vec2d startingNDC) 583 | { 584 | m_startingNDC = startingNDC; 585 | m_viewChangeInProgress = true; 586 | } 587 | 588 | void CameraModel::zoom(osg::Vec2d currentNDC) 589 | { 590 | if (m_startingNDC.y() == currentNDC.y()) 591 | return; 592 | 593 | if (currentNDC.y() > m_startingNDC.y()) { 594 | fovYScaleUp(); 595 | } else { 596 | fovYScaleDown(); 597 | } 598 | m_startingNDC = currentNDC; 599 | emit changed(); 600 | } 601 | 602 | void CameraModel::finishZoom(osg::Vec2d currentNDC) 603 | { 604 | m_viewChangeInProgress = false; 605 | zoom(currentNDC); 606 | } 607 | 608 | void CameraModel::startDolly(osg::Vec2d startingNDC) 609 | { 610 | m_startingNDC = startingNDC; 611 | m_viewChangeInProgress = true; 612 | m_dollyCurrentPressure = 0; 613 | } 614 | void CameraModel::dolly(const double deltaMovement) 615 | { 616 | if( m_ortho || ! m_boundingNode.valid() ) 617 | // No dolly in ortho mode 618 | return; 619 | 620 | // Scale based on model size. TBD this should be under 621 | // app control so that it can be disabled if desired. 622 | const osg::BoundingSphere& bs = m_boundingNode->getBound(); 623 | double scale( bs._radius * .5 ); 624 | if( m_viewDistance > bs._radius ) 625 | scale *= ( m_viewDistance / bs._radius ); 626 | 627 | double newViewDistance = m_viewDistance + ( deltaMovement * scale ); 628 | 629 | if (newViewDistance >= 1.0) { 630 | if (newViewDistance > m_viewDistance) 631 | m_dollyCurrentPressure = 0; 632 | 633 | m_viewDistance = newViewDistance; 634 | 635 | } else if (m_dollyCanChangeCenter) { 636 | m_dollyCurrentPressure++; 637 | if (m_dollyCurrentPressure > m_dollyCenterChangeThreshold) { 638 | double centerDistanceToMove = 1 - newViewDistance; 639 | m_viewCenter = m_viewCenter + (m_viewDir * centerDistanceToMove); 640 | m_viewDistance = 1.; 641 | } 642 | } 643 | emit changed(); 644 | } 645 | 646 | 647 | void CameraModel::dolly(osg::Vec2d currentNDC) 648 | { 649 | if (currentNDC.y() > m_startingNDC.y()) { 650 | dolly(0.5); 651 | } else if (currentNDC.y() < m_startingNDC.y()) { 652 | dolly (-0.5); 653 | } else { 654 | emit changed(); // Odd but needed in case of m_ViewChangeInProgress 655 | } 656 | m_startingNDC = currentNDC; 657 | } 658 | 659 | void CameraModel::finishDolly(osg::Vec2d currentNDC) 660 | { 661 | m_viewChangeInProgress = false; 662 | dolly(currentNDC); 663 | } 664 | 665 | 666 | void CameraModel::orthoNormalize() 667 | { 668 | osg::Vec3d cross = m_viewDir ^ m_viewUp; 669 | m_viewUp = cross ^ m_viewDir; 670 | m_viewDir.normalize(); 671 | m_viewUp.normalize(); 672 | } 673 | 674 | void CameraModel::fitToScreen() 675 | { 676 | if (!m_boundingNode.valid()) { 677 | qDebug("CameraModel::fitToScreen() invalid boundingNode"); 678 | return; 679 | } 680 | const osg::BoundingSphere &bs = m_boundingNode->getBound(); 681 | m_viewCenter = bs.center(); 682 | 683 | double sceneRadius = bs.radius(); 684 | if (sceneRadius <= 0.0) sceneRadius = 10.0; 685 | 686 | // tan( fovy/2. ) = bs.radius / distance 687 | // Solve for distance: 688 | // distance = bs.radius / tan( fovy/2. ) 689 | m_fovY = 30; 690 | float distance = sceneRadius / 691 | tan( osg::DegreesToRadians( m_fovY/2. ) ); 692 | 693 | m_viewDistance = distance; 694 | 695 | m_orthoTop = tan( getFovyRadians() * 0.5 ) * m_viewDistance; 696 | m_orthoBottom = -m_orthoTop; 697 | 698 | emit changed(); 699 | } 700 | 701 | 702 | void CameraModel::setUpAndDir(osg::Vec3d up, osg::Vec3d dir) 703 | { 704 | m_viewDir = dir; 705 | m_viewUp = up; 706 | orthoNormalize(); 707 | emit changed(); 708 | } 709 | 710 | void CameraModel::setViewUp(osg::Vec3d v) 711 | { 712 | osg::Matrixd mat; 713 | mat.makeRotate(m_viewUp, v); 714 | 715 | m_viewDir = m_viewDir * mat; 716 | m_viewUp = v; 717 | emit changed(); 718 | } 719 | 720 | void CameraModel::setViewDir(osg::Vec3d v) 721 | { 722 | osg::Matrixd mat; 723 | mat.makeRotate(m_viewDir, v); 724 | 725 | m_viewUp = m_viewUp * mat; 726 | m_viewDir = v; 727 | emit changed(); 728 | } 729 | 730 | void CameraModel::setViewCenter(osg::Vec3d newCenter) 731 | { 732 | const osg::Vec3d lastEyePosition = getEyePosition(); 733 | 734 | m_viewCenter = newCenter; 735 | m_viewDistance = (lastEyePosition - m_viewCenter).length(); 736 | 737 | emit changed(); 738 | } 739 | 740 | void CameraModel::setViewDistance(double distance) 741 | { 742 | if (m_viewDistance == distance) return; 743 | m_viewDistance = distance; 744 | emit changed(); 745 | } 746 | 747 | 748 | --------------------------------------------------------------------------------