X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;ds=sidebyside;f=src%2FMain%2FCameraGroup.cxx;h=78f83fde7f88ba9d9cccffe1d9e4527069d4931e;hb=4fc7105675225aa0ac9563063de3b600d6a17e09;hp=4c00ab6db4e1509e9af91a313b7bcc1babca0c73;hpb=fe83ba71107986bb1a11f61080915d255c47bf88;p=flightgear.git diff --git a/src/Main/CameraGroup.cxx b/src/Main/CameraGroup.cxx index 4c00ab6db..78f83fde7 100644 --- a/src/Main/CameraGroup.cxx +++ b/src/Main/CameraGroup.cxx @@ -18,10 +18,13 @@ #include "globals.hxx" #include "renderer.hxx" -#include "FGManipulator.hxx" +#include "FGEventHandler.hxx" #include "WindowBuilder.hxx" #include "WindowSystemAdapter.hxx" #include +#include +#include +#include #include #include @@ -38,6 +41,7 @@ #include #include +#include namespace flightgear { @@ -53,83 +57,247 @@ CameraGroup::CameraGroup(osgViewer::Viewer* viewer) : { } +} + +namespace +{ +using namespace osg; + +// Given a projection matrix, return a new one with the same frustum +// sides and new near / far values. + +void makeNewProjMat(Matrixd& oldProj, double znear, + double zfar, Matrixd& projection) +{ + projection = oldProj; + // Slightly inflate the near & far planes to avoid objects at the + // extremes being clipped out. + znear *= 0.999; + zfar *= 1.001; + + // Clamp the projection matrix z values to the range (near, far) + double epsilon = 1.0e-6; + if (fabs(projection(0,3)) < epsilon && + fabs(projection(1,3)) < epsilon && + fabs(projection(2,3)) < epsilon) { + // Projection is Orthographic + epsilon = -1.0/(zfar - znear); // Used as a temp variable + projection(2,2) = 2.0*epsilon; + projection(3,2) = (zfar + znear)*epsilon; + } else { + // Projection is Perspective + double trans_near = (-znear*projection(2,2) + projection(3,2)) / + (-znear*projection(2,3) + projection(3,3)); + double trans_far = (-zfar*projection(2,2) + projection(3,2)) / + (-zfar*projection(2,3) + projection(3,3)); + double ratio = fabs(2.0/(trans_near - trans_far)); + double center = -0.5*(trans_near + trans_far); + + projection.postMult(osg::Matrixd(1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, ratio, 0.0, + 0.0, 0.0, center*ratio, 1.0)); + } +} + +void installCullVisitor(Camera* camera) +{ + osgViewer::Renderer* renderer + = static_cast(camera->getRenderer()); + for (int i = 0; i < 2; ++i) { + osgUtil::SceneView* sceneView = renderer->getSceneView(i); + sceneView->setCullVisitor(new simgear::EffectCullVisitor); + } +} +} + +namespace flightgear +{ +void updateCameras(const CameraInfo* info) +{ + if (info->camera.valid()) + info->camera->getViewport()->setViewport(info->x, info->y, + info->width, info->height); + if (info->farCamera.valid()) + info->farCamera->getViewport()->setViewport(info->x, info->y, + info->width, info->height); +} + CameraInfo* CameraGroup::addCamera(unsigned flags, Camera* camera, const Matrix& view, const Matrix& projection, bool useMasterSceneData) { - if ((flags & (VIEW_ABSOLUTE | PROJECTION_ABSOLUTE)) != 0) - camera->setReferenceFrame(Transform::ABSOLUTE_RF); - else - camera->setReferenceFrame(Transform::RELATIVE_RF); - CameraInfo* info = new CameraInfo(flags, camera); - _cameras.push_back(info); + CameraInfo* info = new CameraInfo(flags); + // The camera group will always update the camera + camera->setReferenceFrame(Transform::ABSOLUTE_RF); + + Camera* farCamera = 0; + if ((flags & (GUI | ORTHO)) == 0) { + farCamera = new Camera; + farCamera->setAllowEventFocus(camera->getAllowEventFocus()); + farCamera->setGraphicsContext(camera->getGraphicsContext()); + farCamera->setCullingMode(camera->getCullingMode()); + farCamera->setInheritanceMask(camera->getInheritanceMask()); + farCamera->setReferenceFrame(Transform::ABSOLUTE_RF); + // Each camera's viewport is written when the window is + // resized; if the the viewport isn't copied here, it gets updated + // twice and ends up with the wrong value. + farCamera->setViewport(simgear::clone(camera->getViewport())); + _viewer->addSlave(farCamera, view, projection, useMasterSceneData); + installCullVisitor(farCamera); + info->farCamera = farCamera; + info->farSlaveIndex = _viewer->getNumSlaves() - 1; + farCamera->setRenderOrder(Camera::POST_RENDER, info->farSlaveIndex); + camera->setCullMask(camera->getCullMask() & ~simgear::BACKGROUND_BIT); + camera->setClearMask(GL_DEPTH_BUFFER_BIT); + } _viewer->addSlave(camera, view, projection, useMasterSceneData); + installCullVisitor(camera); + info->camera = camera; info->slaveIndex = _viewer->getNumSlaves() - 1; + camera->setRenderOrder(Camera::POST_RENDER, info->slaveIndex); + _cameras.push_back(info); return info; } void CameraGroup::update(const osg::Vec3d& position, const osg::Quat& orientation) { - FGManipulator *manipulator - = dynamic_cast(_viewer->getCameraManipulator()); - if (!manipulator) - return; - manipulator->setPosition(position); - manipulator->setAttitude(orientation); - const Matrix masterView(manipulator->getInverseMatrix()); + const Matrix masterView(osg::Matrix::translate(-position) + * osg::Matrix::rotate(orientation.inverse())); + _viewer->getCamera()->setViewMatrix(masterView); const Matrix& masterProj = _viewer->getCamera()->getProjectionMatrix(); for (CameraList::iterator i = _cameras.begin(); i != _cameras.end(); ++i) { const CameraInfo* info = i->get(); - if ((info->flags & (VIEW_ABSOLUTE | PROJECTION_ABSOLUTE)) == 0) { - // Camera has relative reference frame and is updated by - // osg::View. - continue; - } const View::Slave& slave = _viewer->getSlave(info->slaveIndex); + // refreshes camera viewports (for now) + updateCameras(info); Camera* camera = info->camera.get(); + Matrix viewMatrix; if ((info->flags & VIEW_ABSOLUTE) != 0) - camera->setViewMatrix(slave._viewOffset); + viewMatrix = slave._viewOffset; else - camera->setViewMatrix(masterView * slave._viewOffset); + viewMatrix = masterView * slave._viewOffset; + camera->setViewMatrix(viewMatrix); + Matrix projectionMatrix; if ((info->flags & PROJECTION_ABSOLUTE) != 0) - camera->setProjectionMatrix(slave._projectionOffset); + projectionMatrix = slave._projectionOffset; else - camera->setViewMatrix(masterProj * slave._projectionOffset); + projectionMatrix = masterProj * slave._projectionOffset; + + if (!info->farCamera.valid()) { + camera->setProjectionMatrix(projectionMatrix); + } else { + Camera* farCamera = info->farCamera.get(); + farCamera->setViewMatrix(viewMatrix); + double left, right, bottom, top, parentNear, parentFar; + projectionMatrix.getFrustum(left, right, bottom, top, + parentNear, parentFar); + if (parentFar < _nearField || _nearField == 0.0f) { + camera->setProjectionMatrix(projectionMatrix); + camera->setCullMask(camera->getCullMask() + | simgear::BACKGROUND_BIT); + camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + farCamera->setNodeMask(0); + } else { + Matrix nearProj, farProj; + makeNewProjMat(projectionMatrix, parentNear, _nearField, + nearProj); + makeNewProjMat(projectionMatrix, _nearField, parentFar, + farProj); + camera->setProjectionMatrix(nearProj); + camera->setCullMask(camera->getCullMask() + & ~simgear::BACKGROUND_BIT); + camera->setClearMask(GL_DEPTH_BUFFER_BIT); + farCamera->setProjectionMatrix(farProj); + farCamera->setNodeMask(camera->getNodeMask()); + } + } } } void CameraGroup::setCameraParameters(float vfov, float aspectRatio) { - const float zNear = .1f; - const float zFar = 120000.0f; - _viewer->getCamera()->setProjectionMatrixAsPerspective(vfov, - 1.0f / aspectRatio, - zNear, zFar); + if (vfov != 0.0f && aspectRatio != 0.0f) + _viewer->getCamera() + ->setProjectionMatrixAsPerspective(vfov, + 1.0f / aspectRatio, + _zNear, _zFar); } } namespace { -osg::Viewport* buildViewport(const SGPropertyNode* viewportNode) +// A raw value for property nodes that references a class member via +// an osg::ref_ptr. +template +class RefMember : public SGRawValue +{ +public: + RefMember (C *obj, T C::*ptr) + : _obj(obj), _ptr(ptr) {} + virtual ~RefMember () {} + virtual T getValue () const + { + return _obj.get()->*_ptr; + } + virtual bool setValue (T value) + { + _obj.get()->*_ptr = value; + return true; + } + virtual SGRawValue * clone () const + { + return new RefMember(_obj.get(), _ptr); + } +private: + ref_ptr _obj; + T C::* const _ptr; +}; + +template +RefMember makeRefMember(C *obj, T C::*ptr) +{ + return RefMember(obj, ptr); +} + +template +void bindMemberToNode(SGPropertyNode* parent, const char* childName, + C* obj, T C::*ptr, T value) { - double x = viewportNode->getDoubleValue("x", 0.0); - double y = viewportNode->getDoubleValue("y", 0.0); - double width = viewportNode->getDoubleValue("width", 0.0); - double height = viewportNode->getDoubleValue("height", 0.0); - return new osg::Viewport(x, y, width, height); + SGPropertyNode* valNode = parent->getNode(childName); + RefMember refMember = makeRefMember(obj, ptr); + if (!valNode) { + valNode = parent->getNode(childName, true); + valNode->tie(refMember, false); + setValue(valNode, value); + } else { + valNode->tie(refMember, true); + } +} + +void buildViewport(flightgear::CameraInfo* info, SGPropertyNode* viewportNode, + const osg::GraphicsContext::Traits *traits) +{ + using namespace flightgear; + bindMemberToNode(viewportNode, "x", info, &CameraInfo::x, 0.0); + bindMemberToNode(viewportNode, "y", info, &CameraInfo::y, 0.0); + bindMemberToNode(viewportNode, "width", info, &CameraInfo::width, + static_cast(traits->width)); + bindMemberToNode(viewportNode, "height", info, &CameraInfo::height, + static_cast(traits->height)); } } namespace flightgear { -CameraInfo* CameraGroup::buildCamera(const SGPropertyNode* cameraNode) + +CameraInfo* CameraGroup::buildCamera(SGPropertyNode* cameraNode) { WindowBuilder *wBuild = WindowBuilder::getWindowBuilder(); const SGPropertyNode* windowNode = cameraNode->getNode("window"); GraphicsWindow* window = 0; - static int cameraNum = 0; int cameraFlags = DO_INTERSECTION_TEST; if (windowNode) { // New style window declaration / definition @@ -144,18 +312,14 @@ CameraInfo* CameraGroup::buildCamera(const SGPropertyNode* cameraNode) Camera* camera = new Camera; camera->setAllowEventFocus(false); camera->setGraphicsContext(window->gc.get()); - // If a viewport isn't set on the camera, then it's hard to dig it - // out of the SceneView objects in the viewer, and the coordinates - // of mouse events are somewhat bizzare. - const SGPropertyNode* viewportNode = cameraNode->getNode("viewport"); - Viewport* viewport = 0; - if (viewportNode) { - viewport = buildViewport(viewportNode); - } else { - const GraphicsContext::Traits *traits = window->gc->getTraits(); - viewport = new Viewport(0, 0, traits->width, traits->height); - } - camera->setViewport(viewport); + camera->setViewport(new Viewport); + camera->setCullingMode(CullSettings::SMALL_FEATURE_CULLING + | CullSettings::VIEW_FRUSTUM_CULLING); + camera->setInheritanceMask(CullSettings::ALL_VARIABLES + & ~(CullSettings::CULL_MASK + | CullSettings::CULLING_MODE + | CullSettings::CLEAR_MASK)); + osg::Matrix pOff; osg::Matrix vOff; const SGPropertyNode* viewNode = cameraNode->getNode("view"); @@ -219,17 +383,23 @@ CameraInfo* CameraGroup::buildCamera(const SGPropertyNode* cameraNode) double sheary = cameraNode->getDoubleValue("shear-y", 0); pOff.makeTranslate(-shearx, -sheary, 0); } - return addCamera(cameraFlags, camera, pOff, vOff); + CameraInfo* info = addCamera(cameraFlags, camera, pOff, vOff); + // If a viewport isn't set on the camera, then it's hard to dig it + // out of the SceneView objects in the viewer, and the coordinates + // of mouse events are somewhat bizzare. + SGPropertyNode* viewportNode = cameraNode->getNode("viewport", true); + buildViewport(info, viewportNode, window->gc->getTraits()); + updateCameras(info); + return info; } -CameraInfo* CameraGroup::buildGUICamera(const SGPropertyNode* cameraNode, +CameraInfo* CameraGroup::buildGUICamera(SGPropertyNode* cameraNode, GraphicsWindow* window) { WindowBuilder *wBuild = WindowBuilder::getWindowBuilder(); const SGPropertyNode* windowNode = (cameraNode ? cameraNode->getNode("window") : 0); - static int cameraNum = 0; if (!window) { if (windowNode) { // New style window declaration / definition @@ -242,24 +412,15 @@ CameraInfo* CameraGroup::buildGUICamera(const SGPropertyNode* cameraNode, Camera* camera = new Camera; camera->setAllowEventFocus(false); camera->setGraphicsContext(window->gc.get()); - const SGPropertyNode* viewportNode = (cameraNode - ? cameraNode->getNode("viewport") - : 0); - Viewport* viewport = 0; - if (viewportNode) { - viewport = buildViewport(viewportNode); - } else { - const GraphicsContext::Traits *traits = window->gc->getTraits(); - viewport = new Viewport(0, 0, traits->width, traits->height); - } - camera->setViewport(viewport); + camera->setViewport(new Viewport); // XXX Camera needs to be drawn last; eventually the render order // should be assigned by a camera manager. camera->setRenderOrder(osg::Camera::POST_RENDER, 100); camera->setClearMask(0); camera->setInheritanceMask(CullSettings::ALL_VARIABLES & ~(CullSettings::COMPUTE_NEAR_FAR_MODE - | CullSettings::CULLING_MODE)); + | CullSettings::CULLING_MODE + | CullSettings::CLEAR_MASK)); camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR); camera->setCullingMode(osg::CullSettings::NO_CULLING); camera->setProjectionResizePolicy(Camera::FIXED); @@ -267,17 +428,21 @@ CameraInfo* CameraGroup::buildGUICamera(const SGPropertyNode* cameraNode, const int cameraFlags = GUI; CameraInfo* result = addCamera(cameraFlags, camera, Matrixd::identity(), Matrixd::identity(), false); + SGPropertyNode* viewportNode = cameraNode->getNode("viewport", true); + buildViewport(result, viewportNode, window->gc->getTraits()); + // Disable statistics for the GUI camera. result->camera->setStats(0); + updateCameras(result); return result; } CameraGroup* CameraGroup::buildCameraGroup(osgViewer::Viewer* viewer, - const SGPropertyNode* gnode) + SGPropertyNode* gnode) { CameraGroup* cgroup = new CameraGroup(viewer); for (int i = 0; i < gnode->nChildren(); ++i) { - const SGPropertyNode* pNode = gnode->getChild(i); + SGPropertyNode* pNode = gnode->getChild(i); const char* name = pNode->getName(); if (!strcmp(name, "camera")) { cgroup->buildCamera(pNode); @@ -287,9 +452,40 @@ CameraGroup* CameraGroup::buildCameraGroup(osgViewer::Viewer* viewer, cgroup->buildGUICamera(pNode); } } + bindMemberToNode(gnode, "znear", cgroup, &CameraGroup::_zNear, .1f); + bindMemberToNode(gnode, "zfar", cgroup, &CameraGroup::_zFar, 120000.0f); + bindMemberToNode(gnode, "near-field", cgroup, &CameraGroup::_nearField, + 100.0f); return cgroup; } +void CameraGroup::setCameraCullMasks(Node::NodeMask nm) +{ + for (CameraIterator i = camerasBegin(), e = camerasEnd(); i != e; ++i) { + CameraInfo* info = i->get(); + if (info->flags & GUI) + continue; + if (info->farCamera.valid() && info->farCamera->getNodeMask() != 0) { + info->camera->setCullMask(nm & ~simgear::BACKGROUND_BIT); + info->farCamera->setCullMask(nm); + } else { + info->camera->setCullMask(nm); + } + } +} + +void CameraGroup::resized() +{ + for (CameraIterator i = camerasBegin(), e = camerasEnd(); i != e; ++i) { + CameraInfo *info = i->get(); + const Viewport* viewport = info->camera->getViewport(); + info->x = viewport->x(); + info->y = viewport->y(); + info->width = viewport->width(); + info->height = viewport->height(); + } +} + Camera* getGUICamera(CameraGroup* cgroup) { CameraGroup::CameraIterator end = cgroup->camerasEnd(); @@ -328,9 +524,25 @@ bool computeIntersections(const CameraGroup* cgroup, && y >= viewport->y() - epsilon && y < viewport->y() + viewport->height() -1.0 + epsilon)) continue; - LineSegmentIntersector::CoordinateFrame cf = Intersector::WINDOW; + Vec4d start(x, y, 0.0, 1.0); + Vec4d end(x, y, 1.0, 1.0); + Matrix windowMat = viewport->computeWindowMatrix(); + Matrix startPtMat = Matrix::inverse(camera->getProjectionMatrix() + * windowMat); + Matrix endPtMat; + if (!cinfo->farCamera.valid() || cinfo->farCamera->getNodeMask() == 0) + endPtMat = startPtMat; + else + endPtMat = Matrix::inverse(cinfo->farCamera->getProjectionMatrix() + * windowMat); + start = start * startPtMat; + start /= start.w(); + end = end * endPtMat; + end /= end.w(); ref_ptr picker - = new LineSegmentIntersector(cf, x, y); + = new LineSegmentIntersector(Intersector::VIEW, + Vec3d(start.x(), start.y(), start.z()), + Vec3d(end.x(), end.y(), end.z())); osgUtil::IntersectionVisitor iv(picker.get()); const_cast(camera)->accept(iv); if (picker->containsIntersections()) { @@ -355,7 +567,7 @@ void warpGUIPointer(CameraGroup* cgroup, int x, int y) = dynamic_cast(guiCamera->getGraphicsContext()); if (!gw) return; - globals->get_renderer()->getManipulator()->setMouseWarped(); + globals->get_renderer()->getEventHandler()->setMouseWarped(); // Translate the warp request into the viewport of the GUI camera, // send the request to the window, then transform the coordinates // for the Viewer's event queue.