void KX_Camera::ExtractFrustumSphere() { if (m_set_frustum_center) return; // compute sphere for the general case and not only symmetric frustum: // the mirror code in ImageRender can use very asymmetric frustum. // We will put the sphere center on the line that goes from origin to the center of the far clipping plane // This is the optimal position if the frustum is symmetric or very asymmetric and probably close // to optimal for the general case. The sphere center position is computed so that the distance to // the near and far extreme frustum points are equal. // get the transformation matrix from device coordinate to camera coordinate MT_Matrix4x4 clip_camcs_matrix = m_projection_matrix; clip_camcs_matrix.invert(); if (m_projection_matrix[3][3] == MT_Scalar(0.0)) { // frustrum projection // detect which of the corner of the far clipping plane is the farthest to the origin MT_Vector4 nfar; // far point in device normalized coordinate MT_Point3 farpoint; // most extreme far point in camera coordinate MT_Point3 nearpoint;// most extreme near point in camera coordinate MT_Point3 farcenter(0.0, 0.0, 0.0);// center of far cliping plane in camera coordinate MT_Scalar F=-1.0, N; // square distance of far and near point to origin MT_Scalar f, n; // distance of far and near point to z axis. f is always > 0 but n can be < 0 MT_Scalar e, s; // far and near clipping distance (<0) MT_Scalar c; // slope of center line = distance of far clipping center to z axis / far clipping distance MT_Scalar z; // projection of sphere center on z axis (<0) // tmp value MT_Vector4 npoint(1.0, 1.0, 1.0, 1.0); MT_Vector4 hpoint; MT_Point3 point; MT_Scalar len; for (int i=0; i<4; i++) { hpoint = clip_camcs_matrix*npoint; point.setValue(hpoint[0]/hpoint[3], hpoint[1]/hpoint[3], hpoint[2]/hpoint[3]); len = point.dot(point); if (len > F) { nfar = npoint; farpoint = point; F = len; } // rotate by 90 degree along the z axis to walk through the 4 extreme points of the far clipping plane len = npoint[0]; npoint[0] = -npoint[1]; npoint[1] = len; farcenter += point; } // the far center is the average of the far clipping points farcenter *= 0.25; // the extreme near point is the opposite point on the near clipping plane nfar.setValue(-nfar[0], -nfar[1], -1.0, 1.0); nfar = clip_camcs_matrix*nfar; nearpoint.setValue(nfar[0]/nfar[3], nfar[1]/nfar[3], nfar[2]/nfar[3]); // this is a frustrum projection N = nearpoint.dot(nearpoint); e = farpoint[2]; s = nearpoint[2]; // projection on XY plane for distance to axis computation MT_Point2 farxy(farpoint[0], farpoint[1]); // f is forced positive by construction f = farxy.length(); // get corresponding point on the near plane farxy *= s/e; // this formula preserve the sign of n n = f*s/e - MT_Point2(nearpoint[0]-farxy[0], nearpoint[1]-farxy[1]).length(); c = MT_Point2(farcenter[0], farcenter[1]).length()/e; // the big formula, it simplifies to (F-N)/(2(e-s)) for the symmetric case z = (F-N)/(2.0*(e-s+c*(f-n))); m_frustum_center = MT_Point3(farcenter[0]*z/e, farcenter[1]*z/e, z); m_frustum_radius = m_frustum_center.distance(farpoint); } else { // orthographic projection // The most extreme points on the near and far plane. (normalized device coords) MT_Vector4 hnear(1.0, 1.0, 1.0, 1.0), hfar(-1.0, -1.0, -1.0, 1.0); // Transform to hom camera local space hnear = clip_camcs_matrix*hnear; hfar = clip_camcs_matrix*hfar; // Tranform to 3d camera local space. MT_Point3 nearpoint(hnear[0]/hnear[3], hnear[1]/hnear[3], hnear[2]/hnear[3]); MT_Point3 farpoint(hfar[0]/hfar[3], hfar[1]/hfar[3], hfar[2]/hfar[3]); // just use mediant point m_frustum_center = (farpoint + nearpoint)*0.5; m_frustum_radius = m_frustum_center.distance(farpoint); } // Transform to world space. m_frustum_center = GetCameraToWorld()(m_frustum_center); m_frustum_radius /= fabs(NodeGetWorldScaling()[NodeGetWorldScaling().closestAxis()]); m_set_frustum_center = true; }
// render the scene void render() { double delta,idle; double elev,dist; double coef; double light; miniv3d lightdir; if (winwidth<=0 || winheight<=0) return; // update eye point: cam->move_back(-speed/params->fps); elev=cam->get_elev(); dist=cam->get_dist(); // update eye movement: speed+=accel*(topspeed-speed); if (dabs(speed)<minspeed) speed=0.0; cam->rotate_right(accel*turn); turn*=(1.0-accel); cam->rotate_up(-accel*incline); incline*=(1.0-accel); cam->rotate_limit(-90.0,90.0); coef=dist/hover-1.0; if (coef>1.0) coef=1.0; else if (coef<-1.0) coef=-1.0; aez=-coef*gravity; aez*=dmax(1.0-dabs(dez/maxspeed),0.0); dez+=aez/params->fps; dez*=pow(1.0/(1.0+damp),1.0/params->fps); if (elev<0.0) dez*=pow(1.0/(1.0+water),1.0/params->fps); cam->move_down(-dez/params->fps); dist=cam->get_dist(); if (dist<hover) { dez=-dez; dez*=1.0/(1.0+bounce); cam->move_down(dist-hover); } // check for eye movement: if (dabs(speed)>VIEWER_MINDIFF || dabs(turn)>VIEWER_MINDIFF || dabs(incline)>VIEWER_MINDIFF || dabs(dez)>VIEWER_MINDIFF) wakeup=TRUE; // change orientation of signposts: tparams->signpostturn=cam->get_angle(); tparams->signpostincline=cam->get_pitch(); if (eparams->usewaypoints) viewer->getearth()->getterrain()->propagate(); // move eye point into projection center of panoramic waypoint: minipointdata *nearest=viewer->getearth()->getterrain()->getnearestpoint(cam->get_eye(),minipointopts::OPTION_TYPE_FREE); if (nearest!=NULL) if (nearest->opts!=NULL) if (nearest->opts->dataswitch==0) { miniv3d nearpoint(nearest->x,nearest->y,nearest->height+nearest->offset); minicoord nearcoord=viewer->map_l2g(minicoord(nearpoint)); miniv3d nearvec=(nearcoord-cam->get_eye()).vec; double neardist=nearvec.normalize(); double nearrad=3.0*viewer->len_l2g(nearest->size/2.0f); double nearspeed=0.1*neardist; if (neardist<nearrad) { cam->move(nearspeed*nearvec); if (nearspeed>VIEWER_MINDIFF) wakeup=TRUE; } } // update earth lighting if (eparams->usediffuse) { light=2*PI*VIEWER_ROTATION*viewer->time(); lightdir=miniv3d(-cos(light),sin(light),0.0); eparams->lightdir=lightdir; viewer->propagate(); wakeup=TRUE; } // render scene if (sw_stereo==0) viewer->render_geometry(); else viewer->render_geometry(sbase,sw_anaglyph); // render the head-up display renderhud(); // swap buffers and wait for next frame: glutSwapBuffers(); // get time spent delta=viewer->gettimer(); idle=1.0/params->fps-delta; // idle for the remainder of the frame viewer->idle(delta); // update quality parameters viewer->adapt(delta); // update statistics: accu_delta+=delta; if (idle>0.0) accu_idle+=idle; if (++avg_count>params->fps) { avg_delta=accu_delta/avg_count; avg_idle=accu_idle/avg_count; accu_delta=0.0; accu_idle=0.0f; avg_count=0; } }