/*! Updates the axis aligned bounding box in world space. */ SLAABBox& SLNode::updateAABBRec() { if (_isAABBUpToDate) return _aabb; // empty the AABB (= max negative AABB) if (_meshes.size() > 0 || _children.size() > 0) { _aabb.minWS(SLVec3f( FLT_MAX, FLT_MAX, FLT_MAX)); _aabb.maxWS(SLVec3f(-FLT_MAX,-FLT_MAX,-FLT_MAX)); } // Build or update AABB of meshes & merge them to the nodes aabb in WS for (auto mesh : _meshes) { SLAABBox aabbMesh; mesh->buildAABB(aabbMesh, updateAndGetWM()); _aabb.mergeWS(aabbMesh); } // Merge children in WS for (auto child : _children) if (typeid(*child)!=typeid(SLCamera)) _aabb.mergeWS(child->updateAABBRec()); // We need min & max also in OS for the uniform grid intersection in OS _aabb.fromWStoOS(_aabb.minWS(), _aabb.maxWS(), updateAndGetWMI()); // For visualizing the nodes orientation we finally update the axis in WS _aabb.updateAxisWS(updateAndGetWM()); _isAABBUpToDate = true; return _aabb; }
/*! Intersects the nodes meshes with the given ray. The intersection test is only done if the AABB is intersected. The ray-mesh intersection is done in the nodes object space. The rays origin and direction is therefore transformed into the object space. */ bool SLNode::hitRec(SLRay* ray) { assert(ray != 0); // Do not test hidden nodes if (_drawBits.get(SL_DB_HIDDEN)) return false; // Do not test origin node for shadow rays if (this==ray->srcNode && ray->type==SHADOW) return false; // Check first AABB for intersection if (!_aabb.isHitInWS(ray)) return false; SLbool wasHit = false; // Transform ray to object space for non-groups if (_meshes.size() > 0) { // transform origin position to object space ray->originOS.set(updateAndGetWMI().multVec(ray->origin)); // transform the direction only with the linear sub matrix ray->setDirOS(_wmI.mat3() * ray->dir); // test all meshes for (auto mesh : _meshes) { if (mesh->hit(ray, this) && !wasHit) wasHit = true; if (ray->isShaded()) return true; } } // Test children nodes for (auto child : _children) { if (child->hitRec(ray) && !wasHit) wasHit = true; if (ray->isShaded()) return true; } return wasHit; }
/*! Applies the view transform to the modelview matrix depending on the eye: eye=-1 for left, eye=1 for right */ void SLCamera::setView(SLSceneView* sv, const SLEye eye) { SLScene* s = SLScene::current; SLMat4f vm = updateAndGetWMI(); if (eye == centerEye) { _stateGL->modelViewMatrix.identity(); _stateGL->viewMatrix.setMatrix(vm); } else // stereo viewing { if (_projection == stereoSideBySideD) { // half interpupilar disqtance //_eyeSeparation = s->oculus()->interpupillaryDistance(); update old rift code SLfloat halfIPD = (SLfloat)eye * _eyeSeparation * -0.5f; SLMat4f trackingPos; if (_useDeviceRot) { // get the oculus or mobile device orientation SLQuat4f rotation; if (s->oculus()->isConnected()) { rotation = s->oculus()->orientation(eye); trackingPos.translate(-s->oculus()->position(eye)); } else rotation = sv->deviceRotation(); SLfloat rotX, rotY, rotZ; rotation.toMat4().toEulerAnglesZYX(rotZ, rotY, rotX); //SL_LOG("rotx : %3.1f, roty: %3.1f, rotz: %3.1f\n", rotX*SL_RAD2DEG, rotY*SL_RAD2DEG, rotZ*SL_RAD2DEG); SLVec3f viewAdjust = s->oculus()->viewAdjust(eye) * _unitScaling; SLMat4f vmEye(SLMat4f(viewAdjust.x, viewAdjust.y, viewAdjust.z) * rotation.inverted().toMat4() * trackingPos * vm); _stateGL->modelViewMatrix = vmEye; _stateGL->viewMatrix = vmEye; } else { SLMat4f vmEye(SLMat4f(halfIPD, 0.0f, 0.f) * vm); _stateGL->modelViewMatrix = vmEye; _stateGL->viewMatrix = vmEye; } } else { // Get central camera vectors eye, lookAt, lookUp out of the view matrix vm SLVec3f EYE, LA, LU, LR; vm.lookAt(&EYE, &LA, &LU, &LR); // Shorten LR to half of the eye dist (eye=-1 for left, eye=1 for right) LR *= _eyeSeparation * 0.5f * (SLfloat)eye; // Set the OpenGL view matrix for the left eye SLMat4f vmEye; vmEye.lookAt(EYE+LR, EYE + _focalDist*LA+LR, LU); _stateGL->modelViewMatrix = vmEye; _stateGL->viewMatrix = vmEye; } } }
/*! Sets the projection transformation matrix, the viewport transformation and the drawing buffer. In case of a stereographic projection it additionally sets the stereo splitting parameters such as the color masks and the color filter matrix for stereo color anaglyphs. */ void SLCamera::setProjection(SLSceneView* sv, const SLEye eye) { //////////////////// // Set Projection // //////////////////// const SLMat4f& vm = updateAndGetWMI(); _stateGL->stereoEye = eye; _stateGL->projection = _projection; SLVec3f pos(vm.translation()); SLfloat top, bottom, left, right, d; // frustum paramters _scrW = sv->scrW(); _scrH = sv->scrH(); _aspect = sv->scrWdivH(); switch (_projection) { case monoPerspective: _stateGL->projectionMatrix.perspective(_fov, sv->scrWdivH(), _clipNear, _clipFar); break; case monoOrthographic: top = tan(SL_DEG2RAD*_fov*0.5f) * pos.length(); bottom = -top; left = -sv->scrWdivH()*top; right = -left; // The ortographic projection should have its near clip plane behind the camera // rather than slightly in front of it. Else we will see cross sections of scenes if // we zoom in close _stateGL->projectionMatrix.ortho(left,right,bottom,top, -_clipNear, _clipFar); break; case stereoSideBySideD: _stateGL->projectionMatrix = SLScene::current->oculus()->projection(eye); break; // all other stereo projections default: // assymetric frustum shift d (see chapter stereo projection) d = (SLfloat)eye * 0.5f * _eyeSeparation * _clipNear / _focalDist; top = tan(SL_DEG2RAD*_fov/2) * _clipNear; bottom = -top; left = -sv->scrWdivH()*top - d; right = sv->scrWdivH()*top - d; _stateGL->projectionMatrix.frustum(left,right,bottom,top,_clipNear,_clipFar); } ////////////////// // Set Viewport // ////////////////// SLint w = sv->scrW(); SLint h = sv->scrH(); SLint w2 = sv->scrWdiv2(); SLint h2 = sv->scrHdiv2(); SLint h4 = h2 >> 1; if (_projection == stereoSideBySideD) { SLint fbW2 = sv->oculusFB()->halfWidth(); SLint fbH = sv->oculusFB()->height(); if (eye==leftEye) _stateGL->viewport( 0, 0, fbW2, fbH); else _stateGL->viewport(fbW2, 0, fbW2, fbH); } else if (_projection == stereoSideBySide) { if (eye==leftEye) _stateGL->viewport( 0, 0, w2, h); else _stateGL->viewport(w2, 0, w2, h); } else if (_projection == stereoSideBySideP) { if (eye==leftEye) _stateGL->viewport( 0, h4, w2, h2); else _stateGL->viewport(w2, h4, w2, h2); } else _stateGL->viewport(0, 0, w, h); /////////////////// // Clear Buffers // /////////////////// if (eye==rightEye) //&& _projection >= stereoColorRC) // Do not clear color on right eye because it contains the color of the // left eye. The right eye must be drawn after the left into the same buffer _stateGL->clearDepthBuffer(); // Set Color Mask and Filter if (_projection >= stereoColorRC) { if (eye==leftEye) { switch (_projection) { case stereoColorRC: _stateGL->colorMask(1, 0, 0, 1); break; case stereoColorRB: _stateGL->colorMask(1, 0, 0, 1); break; case stereoColorRG: _stateGL->colorMask(1, 0, 0, 1); break; case stereoColorYB: _stateGL->colorMask(1, 1, 0, 1); break; default: break; } } else { switch (_projection) { case stereoColorRC: _stateGL->colorMask(0, 1, 1, 1); break; case stereoColorRB: _stateGL->colorMask(0, 0, 1, 1); break; case stereoColorRG: _stateGL->colorMask(0, 1, 0, 1); break; case stereoColorYB: _stateGL->colorMask(0, 0, 1, 1); break; default: break; } } // Set color filter matrix for red-cyan and yello-blue (ColorCode3D) switch (_projection) { case stereoColorRC: _stateGL->stereoColorFilter.setMatrix(0.29f, 0.59f, 0.12f, 0.00f, 1.00f, 0.00f, 0.00f, 0.00f, 1.00f); break; case stereoColorYB: _stateGL->stereoColorFilter.setMatrix(1.00f, 0.00f, 0.00f, 0.00f, 1.00f, 0.00f, 0.15f, 0.15f, 0.70f); break; default: break; } } GET_GL_ERROR; }
/*! Only draws the frustum lines without lighting when the camera is not the active one. This means that it can be seen from the active view point. */ void SLCamera::drawMeshes(SLSceneView* sv) { if (sv->camera()!=this) { if (_projection == monoOrthographic) { const SLMat4f& vm = updateAndGetWMI(); SLVec3f P[17*2]; SLuint i=0; SLVec3f pos(vm.translation()); SLfloat t = tan(SL_DEG2RAD*_fov*0.5f) * pos.length(); SLfloat b = -t; SLfloat l = -sv->scrWdivH() * t; SLfloat r = -l; // small line in view direction P[i++].set(0,0,0); P[i++].set(0,0,_clipNear*4); // frustum pyramid lines P[i++].set(r,t,_clipNear); P[i++].set(r,t,-_clipFar); P[i++].set(l,t,_clipNear); P[i++].set(l,t,-_clipFar); P[i++].set(l,b,_clipNear); P[i++].set(l,b,-_clipFar); P[i++].set(r,b,_clipNear); P[i++].set(r,b,-_clipFar); // around far clipping plane P[i++].set(r,t,-_clipFar); P[i++].set(r,b,-_clipFar); P[i++].set(r,b,-_clipFar); P[i++].set(l,b,-_clipFar); P[i++].set(l,b,-_clipFar); P[i++].set(l,t,-_clipFar); P[i++].set(l,t,-_clipFar); P[i++].set(r,t,-_clipFar); // around projection plane at focal distance P[i++].set(r,t,-_focalDist); P[i++].set(r,b,-_focalDist); P[i++].set(r,b,-_focalDist); P[i++].set(l,b,-_focalDist); P[i++].set(l,b,-_focalDist); P[i++].set(l,t,-_focalDist); P[i++].set(l,t,-_focalDist); P[i++].set(r,t,-_focalDist); // around near clipping plane P[i++].set(r,t,_clipNear); P[i++].set(r,b,_clipNear); P[i++].set(r,b,_clipNear); P[i++].set(l,b,_clipNear); P[i++].set(l,b,_clipNear); P[i++].set(l,t,_clipNear); P[i++].set(l,t,_clipNear); P[i++].set(r,t,_clipNear); _bufP.generate(P, i, 3); } else { SLVec3f P[17*2]; SLuint i=0; SLfloat aspect = sv->scrWdivH(); SLfloat tanFov = tan(_fov*SL_DEG2RAD*0.5f); SLfloat tF = tanFov * _clipFar; //top far SLfloat rF = tF * aspect; //right far SLfloat lF = -rF; //left far SLfloat tP = tanFov * _focalDist; //top projection at focal distance SLfloat rP = tP * aspect; //right projection at focal distance SLfloat lP = -tP * aspect; //left projection at focal distance SLfloat tN = tanFov * _clipNear; //top near SLfloat rN = tN * aspect; //right near SLfloat lN = -tN * aspect; //left near // small line in view direction P[i++].set(0,0,0); P[i++].set(0,0,_clipNear*4); // frustum pyramid lines P[i++].set(0,0,0); P[i++].set(rF, tF,-_clipFar); P[i++].set(0,0,0); P[i++].set(lF, tF,-_clipFar); P[i++].set(0,0,0); P[i++].set(lF,-tF,-_clipFar); P[i++].set(0,0,0); P[i++].set(rF,-tF,-_clipFar); // around far clipping plane P[i++].set(rF, tF,-_clipFar); P[i++].set(rF,-tF,-_clipFar); P[i++].set(rF,-tF,-_clipFar); P[i++].set(lF,-tF,-_clipFar); P[i++].set(lF,-tF,-_clipFar); P[i++].set(lF, tF,-_clipFar); P[i++].set(lF, tF,-_clipFar); P[i++].set(rF, tF,-_clipFar); // around projection plane at focal distance P[i++].set(rP, tP,-_focalDist); P[i++].set(rP,-tP,-_focalDist); P[i++].set(rP,-tP,-_focalDist); P[i++].set(lP,-tP,-_focalDist); P[i++].set(lP,-tP,-_focalDist); P[i++].set(lP, tP,-_focalDist); P[i++].set(lP, tP,-_focalDist); P[i++].set(rP, tP,-_focalDist); // around near clipping plane P[i++].set(rN, tN,-_clipNear); P[i++].set(rN,-tN,-_clipNear); P[i++].set(rN,-tN,-_clipNear); P[i++].set(lN,-tN,-_clipNear); P[i++].set(lN,-tN,-_clipNear); P[i++].set(lN, tN,-_clipNear); P[i++].set(lN, tN,-_clipNear); P[i++].set(rN, tN,-_clipNear); _bufP.generate(P, i, 3); } _bufP.drawArrayAsConstantColorLines(SLCol3f::WHITE*0.7f); } }