/*! SLCamera::calcMinMax calculates the axis alligned minimum and maximum point of the camera position and the 4 near clipping plane points. */ void SLCamera::calcMinMax(SLVec3f &minV, SLVec3f &maxV) { SLScene* s = SLScene::current; SLSceneView* sv = s->activeSV(); SLVec3f P[5]; SLfloat aspect = (float)sv->scrW() / (float)sv->scrH(); SLfloat tanFov = tan(_fov*SL_DEG2RAD*0.5f); SLfloat tN = tanFov * _clipNear; //top near SLfloat rN = tN * aspect; //right near // frustum pyramid lines P[0].set(0,0,0); // around near clipping plane P[1].set( rN, tN,-_clipNear); P[2].set( rN,-tN,-_clipNear); P[3].set(-rN,-tN,-_clipNear); P[4].set(-rN, tN,-_clipNear); // init min & max points minV.set( FLT_MAX, FLT_MAX, FLT_MAX); maxV.set(-FLT_MAX, -FLT_MAX, -FLT_MAX); // calc min and max point of all vertices for (SLuint i=0; i<5; ++i) { if (P[i].x < minV.x) minV.x = P[i].x; if (P[i].x > maxV.x) maxV.x = P[i].x; if (P[i].y < minV.y) minV.y = P[i].y; if (P[i].y > maxV.y) maxV.y = P[i].y; if (P[i].z < minV.z) minV.z = P[i].z; if (P[i].z > maxV.z) maxV.z = P[i].z; } }
/*! SLCamera::onDoubleTouch gets called whenever two fingers touch a handheld screen. */ SLbool SLCamera::onTouch2Down(const SLint x1, const SLint y1, const SLint x2, const SLint y2) { SLScene* s = SLScene::current; SLSceneView* sv = s->activeSV(); // Determine the lookAt point by ray cast eyeToPixelRay((SLfloat)sv->scrWdiv2(), (SLfloat)sv->scrHdiv2(), &_lookAtRay); s->root3D()->hit(&_lookAtRay); _oldTouchPos1.set((SLfloat)x1, (SLfloat)y1); _oldTouchPos2.set((SLfloat)x2, (SLfloat)y2); return true; }
/*! SLCamera::onMouseWheel event handler moves camera forwards or backwards */ SLbool SLCamera::onMouseWheel(const SLint delta, const SLKey mod) { SLScene* s = SLScene::current; SLSceneView* sv = s->activeSV(); SLfloat sign = (SLfloat)SL_sign(delta); if (_camAnim==turntableYUp || _camAnim==turntableZUp) //.................... { if (mod==KeyNone) { // Determine the lookAt point by ray cast eyeToPixelRay((SLfloat)sv->scrWdiv2(), (SLfloat)sv->scrHdiv2(), &_lookAtRay); s->root3D()->hit(&_lookAtRay); if (_lookAtRay.length < FLT_MAX) _lookAtRay.hitPoint = _lookAtRay.origin + _lookAtRay.dir*_lookAtRay.length; // Scale the mouse delta by the lookAt distance SLfloat lookAtDist; if (_lookAtRay.length < FLT_MAX && _lookAtRay.hitShape) { lookAtDist = _lookAtRay.length; } else lookAtDist = _focalDist; _vm.translation(_vm.m(12),_vm.m(13),_vm.m(14) + sign*lookAtDist*_dPos); _lookAtRay.length = FLT_MAX; setWMandState(); } if (mod==KeyCtrl) { _eyeSeparation *= (1.0f + sign*0.1f); } if (mod==KeyAlt) { _fov += sign*5.0f; currentFOV = _fov; } if (mod==KeyShift) { _focalDist *= (1.0f + sign*0.05f); } return true; } else if (_camAnim==walkingYUp || _camAnim==walkingZUp) //................... { _speedLimit *= (1.0f + sign*0.1f); } return false; }
//----------------------------------------------------------------------------- //! Gets called whenever a mouse button gets pressed. SLbool SLCamera::onMouseDown(const SLMouseButton button, const SLint x, const SLint y, const SLKey mod) { SLScene* s = SLScene::current; SLSceneView* sv = s->activeSV(); // Determine the lookAt point by ray cast eyeToPixelRay((SLfloat)sv->scrWdiv2(), (SLfloat)sv->scrHdiv2(), &_lookAtRay); s->root3D()->hit(&_lookAtRay); // Init both position in case that the second finger came with delay _oldTouchPos1.set((SLfloat)x, (SLfloat)y); _oldTouchPos2.set((SLfloat)x, (SLfloat)y); return false; }
/*! SLMaterial::activate applies the material parameter to the global render state and activates the attached shader */ void SLMaterial::activate(SLGLState* state, SLShape* shape) { SLScene* s = SLScene::current; SLSceneView* sv = s->activeSV(); // Deactivate shader program of the current active material if (current && current->shaderProg()) current->shaderProg()->endShader(); // Set this material as the current material current = this; // If no shader program is attached add the default shader program if (!_shaderProg) { if (_textures.size()>0) shaderProg(s->shaderProgs(PerVrtBlinnTex)); else shaderProg(s->shaderProgs(PerVrtBlinn)); } // Check if shader had compile error and the error texture should be shown if (_shaderProg && _shaderProg->name().find("ErrorTex")!=string::npos) { _textures.clear(); _textures.push_back(new SLGLTexture("CompileError.png")); } // Set material in the state state->matAmbient = _ambient; state->matDiffuse = _diffuse; state->matSpecular = _specular; state->matEmissive = _emission; state->matShininess = _shininess; // Determine use of shaders & textures SLbool useTexture = !(sv->drawBits()->get(SL_DB_TEXOFF) || shape->drawBits()->get(SL_DB_TEXOFF)); // Enable or disable texturing if (useTexture && _textures.size()>0) { for (SLuint i=0; i<_textures.size(); ++i) _textures[i]->bindActive(i); } // Activate the shader program now shaderProg()->beginUse(this); }
/*! This method is used for object picking. The calculation is the same as for primary rays in Ray Tracing. */ void SLCamera::eyeToPixelRay(SLfloat x, SLfloat y, SLRay* ray) { SLScene* s = SLScene::current; SLSceneView* sv = s->activeSV(); SLfloat hw, hh, pixel; SLVec3f dir, EYE, LA, LU, LR, C, TL; // calculate half window width & height in world coords hh = tan(SL_DEG2RAD*_fov/2); hw = hh * sv->scrWdivH(); // calculate the size of a pixel in world coords. // height & width must be equal because perspective is undistorted. pixel = hw * 2 / sv->scrW(); // get camera vectors _vm.lookAt(&EYE, &LA, &LU, &LR); // calculate a vector to the center (C) of the top left (TL) pixel C = LA; TL = C - hw*LR + hh*LU + pixel/2*LR - pixel/2*LU; // Calculate direction of ray dir = TL + pixel*x*LR - pixel*y*LU; // Fill in ray parameters dir.normalize(); ray->origin.set(EYE); ray->setDir(dir); ray->length = FLT_MAX; ray->depth = 1; ray->contrib = 1.0f; ray->type = PRIMARY; ray->x = x; ray->y = y; ray->hitTriangle = 0; ray->hitMat = 0; ray->hitNormal.set(SLVec3f::ZERO); ray->hitPoint.set(SLVec3f::ZERO); ray->originMat = &SLMaterial::AIR; ray->originTria = 0; }
/*! SLCamera::onTouch2Move gets called whenever two fingers move on a handheld screen. */ SLbool SLCamera::onTouch2Move(const SLint x1, const SLint y1, const SLint x2, const SLint y2) { SLScene* s = SLScene::current; SLSceneView* sv = s->activeSV(); SLVec2f now1((SLfloat)x1, (SLfloat)y1); SLVec2f now2((SLfloat)x2, (SLfloat)y2); SLVec2f delta1(now1-_oldTouchPos1); SLVec2f delta2(now2-_oldTouchPos2); // Average out the deltas over the last 4 events for correct 1 pixel moves static SLuint cnt=0; static SLVec2f d1[4]; static SLVec2f d2[4]; d1[cnt%4] = delta1; d2[cnt%4] = delta2; SLVec2f avgDelta1(d1[0].x+d1[1].x+d1[2].x+d1[3].x, d1[0].y+d1[1].y+d1[2].y+d1[3].y); SLVec2f avgDelta2(d2[0].x+d2[1].x+d2[2].x+d2[3].x, d2[0].y+d2[1].y+d2[2].y+d2[3].y); avgDelta1 /= 4.0f; avgDelta2 /= 4.0f; cnt++; SLfloat r1, phi1, r2, phi2; avgDelta1.toPolar(r1, phi1); avgDelta2.toPolar(r2, phi2); // Scale the mouse delta by the lookAt distance SLfloat lookAtDist; if (_lookAtRay.length < FLT_MAX) lookAtDist = _lookAtRay.length; else lookAtDist = _focalDist; // scale factor depending on the space sice at focal dist SLfloat spaceH = tan(SL_DEG2RAD*_fov/2) * lookAtDist * 2.0f; SLfloat spaceW = spaceH * sv->scrWdivH(); //SL_LOG("avgDelta1: (%05.2f,%05.2f), dPhi=%05.2f\n", avgDelta1.x, avgDelta1.y, SL_abs(phi1-phi2)); // if fingers move parallel slide camera vertically or horizontally if (SL_abs(phi1-phi2) < 0.2f) { // Calculate center between finger points SLVec2f nowCenter((now1+now2)*0.5f); SLVec2f oldCenter((_oldTouchPos1+_oldTouchPos2)*0.5f); // For first move set oldCenter = nowCenter if (oldCenter == SLVec2f::ZERO) oldCenter = nowCenter; SLVec2f delta(nowCenter - oldCenter); // scale to 0-1 delta.x /= (SLfloat)sv->scrW(); delta.y /= (SLfloat)sv->scrH(); // scale to space size delta.x *= spaceW; delta.y *= spaceH; if (_camAnim==turntableYUp || _camAnim==turntableZUp) { // apply delta to x- and y-position _vm.translation(_vm.m(12) + delta.x, _vm.m(13) - delta.y, _vm.m(14)); setWMandState(); } else if (_camAnim == walkingYUp || _camAnim == walkingZUp) { _maxSpeed.x = delta.x * 100.0f, _maxSpeed.z = delta.y * 100.0f; } } else // Two finger pinch { // Calculate vector between fingers SLVec2f nowDist(now2 - now1); SLVec2f oldDist(_oldTouchPos2-_oldTouchPos1); // For first move set oldDist = nowDist if (oldDist == SLVec2f::ZERO) oldDist = nowDist; SLfloat delta = oldDist.length() - nowDist.length(); if (_camAnim==turntableYUp) { // scale to 0-1 delta /= (SLfloat)sv->scrH(); // scale to space height delta *= spaceH*2; // apply delta to the z-position _vm.translation(_vm.m(12), _vm.m(13), _vm.m(14) - delta); setWMandState(); } else if (_camAnim == walkingYUp) { // change field of view _fov += SL_sign(delta) * 0.5f; currentFOV = _fov; } } _oldTouchPos1.set((SLfloat)x1, (SLfloat)y1); _oldTouchPos2.set((SLfloat)x2, (SLfloat)y2); return true; }
//----------------------------------------------------------------------------- //! Gets called whenever the mouse is moved. SLbool SLCamera::onMouseMove(const SLMouseButton button, const SLint x, const SLint y, const SLKey mod) { SLScene* s = SLScene::current; SLSceneView* sv = s->activeSV(); if (button == ButtonLeft) //================================================ { // Get camera vectors: eye pos., lookAt, lookUp, lookRight SLVec3f eye, LA, LU, LR; _vm.lookAt(&eye, &LA, &LU, &LR); // The lookAt and lookUp point in VS SLVec3f laP = eye + _focalDist * LA; // Determine rotation point as the center of the AABB of the hitShape SLVec3f rtP; if (_lookAtRay.length < FLT_MAX && _lookAtRay.hitShape) rtP = _lookAtRay.hitShape->aabb()->centerWS(); else rtP = laP; // Determine rot angles around x- & y-axis SLfloat dY = (_oldTouchPos1.y-y) * _rotFactor; SLfloat dX = (_oldTouchPos1.x-x) * _rotFactor; if (_camAnim==turntableYUp) //....................................... { // Apply rotation around the lookAt point SLMat4f rot; rot.translate(rtP); rot.rotate(-dY, LR); rot.rotate(-dX, SLVec3f(0,1,0)); rot.translate(-rtP); _vm *= rot; } else if (_camAnim==turntableZUp) //.................................. { // Apply rotation around the lookAt point SLMat4f rot; rot.translate(rtP); rot.rotate(-dY, LR); rot.rotate(-dX, SLVec3f(0,0,1)); rot.translate(-rtP); _vm *= rot; } else if (_camAnim==walkingYUp) //.................................... { dY *= 0.5f; dX *= 0.5f; // Apply rotation around the lookRight and the Y-axis SLMat4f rot; rot.rotate(-dY, LR); rot.rotate(-dX, SLVec3f(0,1,0)); // rotate eye position LA.set(rot*LA); _vm.lookAt(eye, eye+LA*_focalDist, SLVec3f(0,1,0)); } else if (_camAnim==walkingZUp) //.................................... { dY *= 0.5f; dX *= 0.5f; // Apply rotation around the lookRight and the Z-axis SLMat4f rot; rot.rotate(-dY, LR); rot.rotate(-dX, SLVec3f(0,0,1)); // rotate eye position LA.set(rot*LA); _vm.lookAt(eye, eye+LA*_focalDist, SLVec3f(0,0,1)); } setWMandState(); _oldTouchPos1.set((SLfloat)x,(SLfloat)y); return true; } else if (button == ButtonMiddle) //============================================== { if (_camAnim==turntableYUp || _camAnim==turntableZUp) { // Calculate the fraction delta of the mouse movement SLVec2f dMouse(x-_oldTouchPos1.x, _oldTouchPos1.y-y); dMouse.x /= (SLfloat)sv->scrW(); dMouse.y /= (SLfloat)sv->scrH(); // Scale the mouse delta by the lookAt distance SLfloat lookAtDist; if (_lookAtRay.length < FLT_MAX) lookAtDist = _lookAtRay.length; else lookAtDist = _focalDist; // scale factor depending on the space sice at focal dist SLfloat spaceH = tan(SL_DEG2RAD*_fov/2) * lookAtDist * 2.0f; SLfloat spaceW = spaceH * sv->scrWdivH(); dMouse.x *= spaceW; dMouse.y *= spaceH; if (mod==KeyCtrl) { _vm.translation(_vm.m(12) + dMouse.x, _vm.m(13), _vm.m(14) + dMouse.y); } else { _vm.translation(_vm.m(12) + dMouse.x, _vm.m(13) + dMouse.y, _vm.m(14)); } setWMandState(); _oldTouchPos1.set((SLfloat)x,(SLfloat)y); return true; } } //======================================================================== return false; }