// Camera class // celes@tecgraf.puc-rio.br // Jan 2003 #include "camera.h" #include "frustum.h" #include "util.h" #include "defines.h" #include #ifdef _WIN32 #include #endif #include #include #define EYE_HALF_DIST 0.035f // zoom stack implementation VglCamera::ZoomStack::ZoomStack () : m_top(-1) { } void VglCamera::ZoomStack::Clear () { m_top = -1; m_array.Resize(0); } bool VglCamera::ZoomStack::Undo () { if (m_top >= 0) { m_top--; return true; } else return false; } bool VglCamera::ZoomStack::Redo () { if (m_top < m_array.Size()-1) { m_top++; return true; } else return false; } void VglCamera::ZoomStack::Zoom (ZoomData& data) { if (m_top != m_array.Size()-1) m_array.Resize(m_top+1); m_top++; m_array[m_top].tx = data.tx; m_array[m_top].ty = data.ty; m_array[m_top].factor = data.factor; } bool VglCamera::ZoomStack::IsEmpty () { return m_top == -1; } VglCamera::ZoomStack::ZoomData* VglCamera::ZoomStack::Top () { if (!IsEmpty()) return &m_array[m_top]; else return NULL; } // camera implementation VglCamera::VglCamera () : m_updated(false), m_ortho(false), m_autofit(false), m_box_set(false), m_stdist(false), m_curreye(0), m_headlight(0), m_angle(45.0f), m_aspect(1.0f), m_znear(1.0f), m_zfar(1000.0f), m_fitfactor(1.0f), m_shiftx(0.0f), m_shifty(0.0f), m_pmin(-1.5f), m_pmax(1.5f), m_width(1.0f), m_distance(1.0f), m_zps(-1.0f), m_tc(0.0f), m_hit(0.0f), m_pos(0.0f,0.0f,0.0f), m_target(0.0f,0.0f,-1.0f), m_up(0.0f,1.0f,0.0f), m_dop(0.0f,0.0f,-1.0f), m_center(0.0f,0.0f,0.0f), m_stack(), m_tx(0.0f),m_ty(0.0f), m_validate(0),m_vdata(0) { m_rotation.Identity(); } VglCamera::~VglCamera () { } void VglCamera::SetOrtho (bool flag) { m_ortho = flag; m_updated = false; } bool VglCamera::GetOrtho () { return m_ortho; } void VglCamera::SetPosition (float x, float y, float z) { m_pos.Set(x,y,z); m_updated = false; } void VglCamera::SetTarget (float x, float y, float z) { m_target.Set(x,y,z); m_updated = false; } void VglCamera::SetUp (float nx, float ny, float nz) { m_up.Set(nx,ny,nz); m_up.Normalize(); m_updated = false; } void VglCamera::SetAspect (float aspect) { if (aspect != m_aspect) // avoid unnecessary out-of-date status { m_aspect = aspect; m_updated = false; } } void VglCamera::SetAngle (float fovy) { if (fovy != m_angle) // avoid unnecessary out-of-date status { m_angle = fovy; m_updated = false; } } void VglCamera::SetZPlanes (float znear, float zfar) { if (znear != m_znear || zfar != m_zfar) { m_znear = znear; m_zfar = zfar; m_updated = false; } } void VglCamera::SetRotation (const AlgMatrix& rotation) { m_rotation = rotation; } void VglCamera::SetRotation (float angle, float x, float y, float z) { m_rotation.Identity(); m_rotation.Rotate(angle,x,y,z); } void VglCamera::SetShift (float shiftx, float shifty) { if (shiftx!=m_shiftx || shifty!=m_shifty) // avoid out-of-date status { m_shiftx = shiftx; m_shifty = shifty; m_updated = false; } } void VglCamera::SetBox (float xmin, float xmax, float ymin, float ymax, float zmin, float zmax) { if (m_box_set && xmin == m_box[0].x && ymin == m_box[0].y && zmin == m_box[0].z && xmax == m_box[6].x && ymax == m_box[6].y && zmax == m_box[6].z) return; m_center.Set((xmin+xmax)/2,(ymin+ymax)/2,(zmin+zmax)/2); m_box[0].Set(xmin,ymin,zmin); m_box[1].Set(xmax,ymin,zmin); m_box[2].Set(xmax,ymax,zmin); m_box[3].Set(xmin,ymax,zmin); m_box[4].Set(xmin,ymin,zmax); m_box[5].Set(xmax,ymin,zmax); m_box[6].Set(xmax,ymax,zmax); m_box[7].Set(xmin,ymax,zmax); m_box_set = true; Reset(); } void VglCamera::GetBox (float* xmin, float* xmax, float* ymin, float* ymax, float* zmin, float* zmax) { *xmin = m_box[0].x; *ymin = m_box[0].y; *zmin = m_box[0].z; *xmax = m_box[6].x; *ymax = m_box[6].y; *zmax = m_box[6].z; } const AlgVector* VglCamera::GetBox () { return m_box; } // autofit methods void VglCamera::SetAutoFit (bool flag) { if (m_autofit != flag) { m_autofit = flag; m_updated = false; if (!flag) { m_znear = m_vmin.z; m_zfar = m_vmax.z; } } } void VglCamera::SetDOP (float x, float y, float z) { if (x != 0.0f || y != 0.0f || z != 0.0f) { m_dop.Set(x,y,z); m_dop.Normalize(); m_updated = false; } } void VglCamera::SetFitFactor (float factor) { if (factor != 0.0f && factor != m_fitfactor) { m_fitfactor = factor; m_updated = false; } } // stereo methods void VglCamera::SetCurrEye (int eye) { m_curreye = eye; } int VglCamera::GetCurrEye () { return m_curreye; } void VglCamera::SetParallax (float pmin, float pmax) { m_pmin = pmin; m_pmax = pmax; m_updated = false; } void VglCamera::SetZeroParallax (float zps) { m_zps = zps; m_updated = false; } void VglCamera::SetStereoDistortion(bool avoid) { m_stdist = avoid; m_updated = false; } void VglCamera::SetPhysicalScreen (float w, float d) { m_width = w; m_distance = d; } void VglCamera::SetHeadlight (int lightid) { m_headlight = lightid; } float VglCamera::GetZCenter () { return m_zcenter; } void VglCamera::SetViewVolume (float xmin, float ymin, float zmin, float xmax, float ymax, float zmax) { m_vmin.x = xmin; m_vmin.y = ymin; m_vmin.z = zmin; m_vmax.x = xmax; m_vmax.y = ymax; m_vmax.z = zmax; m_updated = false; } void VglCamera::GetViewVolume (float* xmin, float* ymin, float* zmin, float* xmax, float* ymax, float* zmax) { if (!m_updated) ComputeFrustum(); *xmin = m_vmin.x; *ymin = m_vmin.y; *zmin = m_vmin.z; *xmax = m_vmax.x; *ymax = m_vmax.y; *zmax = m_vmax.z; } void VglCamera::CenterView () { AlgMatrix m = GetModelview(); float xmin=0.0f, xmax=0.0f, ymin=0.0f, ymax=0.0f, zmin=0.0f, zmax=0.0f; for (int i=0; i<8; ++i) { AlgVector p = m.Transform(m_box[i]); if (i==0) { xmin = xmax = p.x; ymin = ymax = p.y; zmin = zmax = p.z; } else { if (p.xxmax) xmax = p.x; if (p.yymax) ymax = p.y; if (p.zzmax) zmax = p.z; } } Translate((xmin+xmax)/2,(ymin+ymax)/2,m_zcenter+(zmin+zmax)/2); } void VglCamera::FitView () { CenterView(); ResetZoom(); // Compute bounding sphere AlgMatrix m = GetModelview(); AlgMatrix p = GetProjection(); AlgVector center(0.0f,0.0f,-m_zcenter); AlgVector min = m.Transform(m_box[0]); AlgVector max = m.Transform(m_box[6]); AlgVector diag = max-min; float radius = diag.Length()/2; //radius *= float(sqrt(2.0/3.0)); // to fit as in 2D // Compute minimum distance from center to frustum planes VglFrustum f(p.GetMatrix()); AlgPlane l = f.GetPlane(VGL_PLANE_LEFT); l.Normalize(); AlgPlane r = f.GetPlane(VGL_PLANE_RIGHT); r.Normalize(); AlgPlane b = f.GetPlane(VGL_PLANE_BOTTOM); b.Normalize(); AlgPlane t = f.GetPlane(VGL_PLANE_TOP); t.Normalize(); float dist = l.Distance(center); float rdist = r.Distance(center); float bdist = b.Distance(center); float tdist = t.Distance(center); if (rdist= 0.9f*EYE_HALF_DIST) phy_pmax = 0.9f*EYE_HALF_DIST; float pmin = phy_pmin/m_width*(m_vmax.x-m_vmin.x)*m_znear/m_vmin.z; float pmax = phy_pmax/m_width*(m_vmax.x-m_vmin.x)*m_znear/m_vmin.z; if (m_zps>=0.0f) // if explicitly given { zps = m_znear + m_zps*(m_zfar-m_znear); if (fabs(zps-m_znear) < VGL_TOL) // pmin must be zero { tc = m_zfar*pmax/(m_zfar-m_znear); pmin = 0.0f; } else { tc = (zps*pmin)/(zps-m_znear); float p = tc*(m_zfar-m_znear)/m_zfar - pmin; if (p > pmax) { // use pmax and find pmin p = pmax / (zps/m_zfar*(m_zfar-m_znear)/(zps-m_znear) - 1.0f); tc = (zps*p)/(zps-m_znear); pmin = tc*(zps-m_znear)/zps; // pmin based on pmax value } else { pmax = p; // pmax based on pmin value } } } else { tc = m_zfar*(pmin+pmax)/(m_zfar-m_znear); zps = tc*m_znear/(tc-pmin); } // printf("tc_wrl before distortion correction: %f\n",tc); // printf("tc_phy before distortion correction: %f\n",tc*m_width/((m_vmax.x-m_vmin.x)*m_znear/m_vmin.z)); if (m_stdist) m_tc = AvoidStereoDistortion(tc,pmin,pmax); else m_tc = tc; if (m_tc != 0.0f) // to avoid division by zero (/zps) m_hit = m_tc*m_znear/zps * m_vmin.z/m_znear; else m_hit = 0.0f; m_vzps = zps; } float VglCamera::AvoidStereoDistortion(float tc, float pmin, float pmax) { float dmin, dmax; bool update = false; float factor = m_width/((m_vmax.x-m_vmin.x)*m_znear/m_vmin.z); // from virtual to physical world float phy_pmin = pmin*factor; // pmin to physical world float phy_pmax = pmax*factor; // pmax to physical world // printf("phypmin,phypmax: %f,%f\n",phy_pmin,phy_pmax); dmin = (phy_pmin*m_distance)/(EYE_HALF_DIST+phy_pmin); dmax = (phy_pmax*m_distance)/(EYE_HALF_DIST-phy_pmax); dmin /= factor; // dmin to virtual world dmax /= factor; // dmax to virtual world if (m_zps>=0.0f) // if explicitly given { float zps = m_znear + m_zps*(m_zfar-m_znear); if (dmin > (zps-m_znear)) { dmin = zps-m_znear; update = true; } if (dmax > (m_zfar-zps)) { dmax = m_zfar-zps; update = true; } } else { // printf("d=%f, z=%f\n",(dmin+dmax) , (m_zfar-m_znear)); if ((dmin+dmax) > (m_zfar-m_znear)) { float d = (m_zfar-m_znear)-(dmin+dmax); dmin -= d/2; dmax -= d/2; update = true; } } if (update) { float tc1 = pmax*(m_znear+dmax)/dmax; float tc2 = pmin*(m_znear-dmin)/dmin; return (tc1 < tc2 ? tc1 : tc2); } else return tc; } float VglCamera::GetComputedZPS () { return m_vzps; } void VglCamera::Load () { glMatrixMode(GL_PROJECTION); glLoadMatrixf(GetProjection().GetMatrix()); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); if (m_headlight) { glEnable(GL_LIGHTING); glEnable(m_headlight); if (m_ortho) { float pos[] = {0,0,1,0}; glLightfv(m_headlight,GL_POSITION,pos); } else { float pos[] = {0,0,0,1}; glLightfv(m_headlight,GL_POSITION,pos); } } glTranslatef(-m_curreye*m_tc,0.0f,0.0f); // use the mono view matrix because eye shift has already been applied int curr_eye = GetCurrEye(); SetCurrEye(VGL_EYE_MONO); glMultMatrixf(GetModelview().GetMatrix()); SetCurrEye(curr_eye); } void VglCamera::PushMatrix () { m_stack.Duplicate(); m_currmatrix = m_stack.Top(); m_lasttransf.Identity(); } void VglCamera::PopMatrix () { m_stack.Pop(); m_currmatrix = m_stack.Top(); m_lasttransf.Identity(); m_updated = false; } void VglCamera::LoadIdentity () { m_currmatrix = m_stack.Top(); m_lasttransf.Identity(); m_stack.Top().Identity(); m_updated = false; } void VglCamera::Translate (float x, float y, float z) { AlgMatrix t; t.Identity(); t.Translate(-x,-y,-z); ApplyTransform(t); } void VglCamera::TranslateW (float x, float y, float z) { AlgMatrix lookat = AlgMatrix::GetIdentity(); lookat.LookAt(m_pos,m_target,m_up); AlgMatrix lookatinv = lookat.Inverse(); AlgMatrix t = lookat; t.Translate(-x,-y,-z); t.Accum(lookatinv); ApplyTransform(t); } void VglCamera::Rotate (float angle, float x, float y, float z) { AlgMatrix t; t.Identity(); t.Rotate(-angle,x,y,z); ApplyTransform(t); } void VglCamera::TranslateView (float x, float y, float z) { AlgMatrix t; t.Identity(); AlgVector d = m_vmax - m_vmin; float factor = m_ortho ? 1.0f : m_zcenter/m_vmin.z; t.Translate(factor*x*d.x,factor*y*d.y,z*d.z); ApplyTransform(t); } void VglCamera::TranslateZoom (float x, float y) { if (m_zoomstack.IsEmpty()) { TranslateView(x,y,0.0f); } else { ZoomStack::ZoomData* d = m_zoomstack.Top(); d->tx -= x/d->factor; d->ty -= y/d->factor; } m_updated = false; } void VglCamera::RotateView (float angle, float x, float y, float z) { AlgMatrix t; t.Identity(); t.Rotate(angle,x,y,z,0.0f,0.0f,-m_zcenter); ApplyTransform(t); } void VglCamera::ScaleView (float sx, float sy, float sz) { AlgMatrix t; t.Identity(); t.Scale(sx,sy,sz,0.0f,0.0f,-m_zcenter); ApplyTransform(t); } void VglCamera::ScaleZoom (float s) { if (m_zoomstack.IsEmpty()) ScaleView(s,s,s); else { ZoomStack::ZoomData* d = m_zoomstack.Top(); d->factor *= s; m_updated = false; } } void VglCamera::UndoTransform () { m_stack.Top() = m_currmatrix; } void VglCamera::RedoTransform () { ApplyTransform(m_lasttransf); } void VglCamera::SetValidateFunc (int (*func)(void*), void* data) { m_validate = func; m_vdata = data; } void VglCamera::ApplyTransform (AlgMatrix& t) { AlgMatrix curr = m_stack.Top(); m_stack.Top().PreAccum(t); if (m_validate && m_validate(m_vdata)==0) { m_stack.Top() = curr; // reject tranform (restore top) } else { m_currmatrix = curr; // accept transform m_lasttransf = t; m_updated = false; } } void VglCamera::Zoom (float factor, float x, float y) { ZoomStack::ZoomData d(factor,x-0.5f,y-0.5f); // accumulate previous params if (!m_zoomstack.IsEmpty()) { ZoomStack::ZoomData* z = m_zoomstack.Top(); d.factor *= z->factor; d.tx = d.tx/z->factor + z->tx; d.ty = d.ty/z->factor + z->ty; } m_zoomstack.Zoom(d); m_updated = false; } int VglCamera::UndoZoom () { if (!m_zoomstack.IsEmpty()) { m_zoomstack.Undo(); m_updated = false; return 1; } return 0; } int VglCamera::RedoZoom () { if (m_zoomstack.Redo() != 0) { m_updated = false; return 1; } else return 0; } void VglCamera::ResetZoom () { m_zoomstack.Clear(); m_tx = m_ty = 0.0f; m_updated = false; } // Internal methods void VglCamera::ComputeFrustum () { if (m_autofit) { AlgVector size; if (!m_box_set) printf("VGL: The box must be set in order to compute the fitting correctly.\n"); #if 0 // old autofit code (box mostly) size = ComputeBoxSize()/m_fitfactor; m_znear = size.y/2.0f/(float)tan(m_angle/360.0f*VGL_PI); m_zfar = m_znear + size.z; m_target = m_center; m_pos = m_target - m_dop*(m_znear+size.z/2); m_zcenter = (m_znear+m_zfar)/2; #else // calc box bounding sphere radius float radius = (m_box[6]-m_box[0]).Length()/2/m_fitfactor; // calc distance from the observer to the box center using the fovy float disttocenter; if (m_aspect >= 1.0) disttocenter = radius/sin(m_angle/360.0f*VGL_PI); else disttocenter = radius/sin(m_angle*m_aspect*VGL_PI/360.0f); // initial znear is the distance from the observer to the sphere, zfar=znear+2*radius m_znear = disttocenter-radius; m_zfar = disttocenter+radius; // target and position in world coordinates m_target = m_center; m_pos = m_target - m_dop*disttocenter; m_zcenter = (m_znear+m_zfar)/2; // calc frustum size. if (m_aspect >= 1.0) { if (m_ortho) size.y = 2*radius; else size.y = 2*m_znear*(float)tan(m_angle/360.0f*VGL_PI); size.x = size.y*m_aspect; } else { if (m_ortho) size.x = 2*radius; else size.x = 2*m_znear*(float)tan(m_angle*m_aspect*VGL_PI/360.0f); size.y = size.x/m_aspect; } size.z = 2*radius; #endif // enlarge z range if below ratio limit, then adjust x and y sizes float znear = m_znear; float zfar = m_zfar; float limit = 500.0f; if (!m_ortho) { size.x /= znear; size.y /= znear; } if (zfar/znear < limit) { #if 0 float delta = (limit*znear - zfar)/(1+limit); znear -= delta; zfar += delta; #else float delta = (float)sqrt(limit*znear / zfar); znear /= delta; zfar *= delta; #endif } if (!m_ortho) { size.x *= znear; size.y *= znear; } m_vmin.Set(-size.x/2,-size.y/2,znear); m_vmax.Set(size.x/2,size.y/2,zfar); } else { if (!m_ortho) { float dy = 2.0f*m_znear*(float)tan(m_angle/360.0f*VGL_PI); float dx = dy*m_aspect; m_vmin.Set(-dx/2,-dy/2,m_znear); m_vmax.Set(dx/2,dy/2,m_zfar); } // else the view volume must be set explicitly AlgVector zl = m_target-m_pos; zl.Normalize(); AlgVector v = m_center-m_pos; m_zcenter = zl.Dot(v); } // apply zoom and shift float dx = (m_vmax.x-m_vmin.x); float dy = (m_vmax.y-m_vmin.y); if (!m_zoomstack.IsEmpty()) { // apply zoom ZoomStack::ZoomData* z = m_zoomstack.Top(); float xc = (m_vmin.x+m_vmax.x)/2 + dx*z->tx; float yc = (m_vmin.y+m_vmax.y)/2 + dy*z->ty; m_vmin.x = xc-dx/z->factor/2; m_vmax.x = xc+dx/z->factor/2; m_vmin.y = yc-dy/z->factor/2; m_vmax.y = yc+dy/z->factor/2; } else { // apply zoom translation m_vmin.x += dx*m_tx; m_vmax.x += dx*m_tx; m_vmin.y += dy*m_ty; m_vmax.y += dy*m_ty; } // apply canvas shift m_vmin.x += m_shiftx*dx; m_vmax.x += m_shiftx*dx; m_vmin.y += m_shifty*dy; m_vmax.y += m_shifty*dy; } void VglCamera::FindMinMax (AlgVector& axis, float* min, float* max) { int i; AlgVector v; v = m_box[0]-m_center; *min = *max = v.Dot(axis); for (i=1; i<8; ++i) { v = m_box[i]-m_center; float d = v.Dot(axis); if (d < *min) *min = d; else if (d > *max) *max = d; } } AlgVector VglCamera::ComputeBoxSize () { AlgVector size; AlgVector min, max; // find eye axis AlgVector xl, yl, zl(-m_dop); xl.Cross(m_up,zl); yl.Cross(zl,xl); // find min and max box x, y and z given the eye axis FindMinMax(xl,&min.x,&max.x); FindMinMax(yl,&min.y,&max.y); FindMinMax(zl,&min.z,&max.z); // center view size.x = fabs(min.x) > fabs(max.x) ? 2*(float)fabs(min.x) : 2*(float)fabs(max.x); size.y = fabs(min.y) > fabs(max.y) ? 2*(float)fabs(min.y) : 2*(float)fabs(max.y); size.z = fabs(min.z) > fabs(max.z) ? 2*(float)fabs(min.z) : 2*(float)fabs(max.z); // avoid distortion if (size.x/size.y < m_aspect) size.x = size.y*m_aspect; else size.y = size.x/m_aspect; return size; } void VglCamera::Reset () { LoadIdentity (); ResetZoom (); } AlgMatrix VglCamera::GetModelview () { AlgMatrix m; if (!m_updated) ComputeFrustum(); m.Identity(); if (m_curreye != 0) m.Translate(-m_curreye*m_tc,0.0f,0.0f); if (!m_rotation.IsIdentity()) m.Accum(m_rotation); if (!m_stack.Top().IsIdentity()) m.Accum(m_stack.Top()); AlgMatrix lookat = AlgMatrix::GetIdentity(); lookat.LookAt(m_pos,m_target,m_up); m.Accum(lookat); return m; } AlgMatrix VglCamera::GetProjection () { AlgMatrix m; if (!m_updated) ComputeFrustum(); if (m_ortho) { m.Identity(); m.Ortho(m_vmin.x,m_vmax.x,m_vmin.y,m_vmax.y,m_vmin.z,m_vmax.z); } else { // compute stereo params if (!m_updated) ComputeStereo(); m.Identity(); m.Frustum( m_vmin.x-m_curreye*m_hit,m_vmax.x-m_curreye*m_hit, m_vmin.y,m_vmax.y, m_vmin.z,m_vmax.z); } m_updated = true; return m; } // io methods void VglCamera::Write (UtlWriter* writer) { if (!m_updated) ComputeFrustum(); const float* rot = m_rotation.GetMatrix(); const float* mat = m_stack.Top().GetMatrix(); writer->WriteNewBlock("vgl.Camera"); writer->WriteBool("ortho",m_ortho); writer->WriteInt("curreye",m_curreye); writer->WriteInt("headlight",m_headlight); writer->WriteFloat("angle",m_angle); writer->WriteFloat("aspect",m_aspect); writer->WriteFloat("znear",m_znear); writer->WriteFloat("zfar",m_zfar); writer->WriteFloat("shiftx",m_shiftx); writer->WriteFloat("shifty",m_shifty); writer->WriteFloat("pmin",m_pmin); writer->WriteFloat("pmax",m_pmax); writer->WriteFloat("width",m_width); writer->WriteFloat("distance",m_distance); writer->WriteFloat("zps",m_zps); writer->WriteFloat("tc",m_tc); writer->WriteFloat("hit",m_hit); writer->WriteFloatArray("pos",3,m_pos.x,m_pos.y,m_pos.z); writer->WriteFloatArray("target",3,m_target.x,m_target.y,m_target.z); writer->WriteFloatArray("up",3,m_up.x,m_up.y,m_up.z); writer->WriteBool("autofit",m_autofit); if (m_autofit) { writer->WriteFloatArray("dop",3,m_dop.x,m_dop.y,m_dop.z); writer->WriteFloat("fitfactor",m_fitfactor); } writer->WriteFloatArray("matrix",16, mat[0],mat[1],mat[2],mat[3], mat[4],mat[5],mat[6],mat[7], mat[8],mat[9],mat[10],mat[11], mat[12],mat[13],mat[14],mat[15]); writer->WriteFloatArray("rotation",16, rot[0],rot[1],rot[2],rot[3], rot[4],rot[5],rot[6],rot[7], rot[8],rot[9],rot[10],rot[11], rot[12],rot[13],rot[14],rot[15]); // write box if (m_box_set) writer->WriteFloatArray("box",6,m_box[0].x,m_box[6].x,m_box[0].y,m_box[6].y,m_box[0].z,m_box[6].z); if (!m_zoomstack.IsEmpty()) { ZoomStack::ZoomData* z = m_zoomstack.Top(); writer->WriteBool("zoom",true); writer->WriteFloatArray("zoomdata",3,z->factor,z->tx,z->ty); } else writer->WriteBool("zoom",false); writer->WriteCloseBlock(); } bool VglCamera::Write (FILE* fp) { UtlLua3FileWriter writer; writer.SetFile(fp); writer.SetIndent(1); Write(&writer); return !writer.GetError(); } bool VglCamera::Write (char* filename) { UtlLua3FileWriter writer; if (!writer.Open(filename)) return false; writer.SetIndent(1); Write(&writer); return writer.Close() && !writer.GetError(); } bool VglCamera::Skip (FILE* fp) { int n=1; fscanf(fp,"vgl.Camera{"); while (n!=0) { int c = fgetc(fp); if (c==EOF) return false; else if (c=='{') n++; else if (c=='}') n--; } return true; } bool VglCamera::Read (FILE* fp) { int ortho, autofit; float rot[16]; float mat[16]; if (fscanf(fp, "vgl.Camera{\n" " ortho = %d,\n" " curreye = %d,\n" " headlight = %d,\n" " angle = %g,\n" " aspect = %g,\n" " znear = %g,\n" " zfar = %g,\n" " shiftx = %g,\n" " shifty = %g,\n" " pmin = %g,\n" " pmax = %g,\n" " width = %g,\n" " distance = %g,\n" " zps = %g,\n" " tc = %g,\n" " hit = %g,\n" " pos = {%g ,%g ,%g, },\n" " target = {%g ,%g ,%g, },\n" " up = {%g ,%g ,%g, },\n" " autofit = %d,\n", &ortho,&m_curreye,&m_headlight,&m_angle,&m_aspect, &m_znear,&m_zfar,&m_shiftx,&m_shifty,&m_pmin,&m_pmax, &m_width,&m_distance,&m_zps,&m_tc,&m_hit, &m_pos.x,&m_pos.y,&m_pos.z, &m_target.x,&m_target.y,&m_target.z, &m_up.x,&m_up.y,&m_up.z, &autofit) != 26) { return false; } m_ortho = ortho!=0; m_autofit = autofit!=0; if (m_autofit) { if (fscanf(fp, " dop = {%g ,%g ,%g, },\n" " fitfactor = %g,\n", &m_dop.x,&m_dop.y,&m_dop.z,&m_fitfactor) != 4) { return false; } } if (fscanf(fp, " matrix = {%g ,%g ,%g ,%g ,%g ,%g ,%g ,%g ,%g ,%g ,%g ,%g ,%g ,%g ,%g ,%g, },\n" " rotation = {%g ,%g ,%g ,%g ,%g ,%g ,%g ,%g ,%g ,%g ,%g ,%g ,%g ,%g ,%g ,%g, },\n", &mat[0],&mat[1],&mat[2],&mat[3], &mat[4],&mat[5],&mat[6],&mat[7], &mat[8],&mat[9],&mat[10],&mat[11], &mat[12],&mat[13],&mat[14],&mat[15], &rot[0],&rot[1],&rot[2],&rot[3], &rot[4],&rot[5],&rot[6],&rot[7], &rot[8],&rot[9],&rot[10],&rot[11], &rot[12],&rot[13],&rot[14],&rot[15]) != 32) { return false; } // read box float box_limits[6]; m_box_set = false; if (fscanf( fp, " box = {%g,%g,%g,%g,%g,%g,},\n", &box_limits[0], &box_limits[1], &box_limits[2], &box_limits[3], &box_limits[4], &box_limits[5]) == 6) { SetBox(box_limits[0],box_limits[1],box_limits[2],box_limits[3],box_limits[4],box_limits[5]); } // read matrices m_stack.Top().SetMatrix(mat); m_rotation.SetMatrix(rot); ResetZoom(); int zoom; fscanf(fp," zoom = %d,",&zoom); if (zoom) { float factor,tx,ty; fscanf(fp, " zoomdata = {%g ,%g ,%g, },\n",&factor,&tx,&ty); ZoomStack::ZoomData zdata(factor,tx,ty); m_zoomstack.Zoom(zdata); } fscanf(fp," }\n\n"); m_updated = false; return true; } bool VglCamera::Read (char* filename) { FILE* fp = fopen(filename,"rt"); if (!fp) return false; bool ret = Read(fp); fclose(fp); return ret; }