/* * Stellarium * Copyright (C) 2010 Fabien Chereau * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "StelViewportEffect.hpp" #include "StelApp.hpp" #include "StelCore.hpp" #include "StelPainter.hpp" #include "SphericMirrorCalculator.hpp" #include "StelFileMgr.hpp" #include "StelMovementMgr.hpp" #include #include #include void StelViewportEffect::paintViewportBuffer(const QGLFramebufferObject* buf) const { StelPainter sPainter(StelApp::getInstance().getCore()->getProjection2d()); sPainter.setColor(1,1,1); sPainter.enableTexture2d(true); glBindTexture(GL_TEXTURE_2D, buf->texture()); sPainter.drawRect2d(0, 0, buf->size().width(), buf->size().height()); } struct VertexPoint { Vec2f ver_xy; Vec4f color; double h; }; StelViewportDistorterFisheyeToSphericMirror::StelViewportDistorterFisheyeToSphericMirror(int screen_w,int screen_h) : screen_w(screen_w), screen_h(screen_h), originalProjectorParams(StelApp::getInstance().getCore()->getCurrentStelProjectorParams()), texture_point_array(NULL) { QSettings& conf = *StelApp::getInstance().getSettings(); StelCore* core = StelApp::getInstance().getCore(); // initialize viewport parameters and texture size: // maximum FOV value of the not yet distorted image double distorter_max_fov = conf.value("spheric_mirror/distorter_max_fov",175.f).toFloat(); if (distorter_max_fov > 240.f) distorter_max_fov = 240.f; else if (distorter_max_fov < 120.f) distorter_max_fov = 120.f; if (distorter_max_fov > core->getMovementMgr()->getMaxFov()) distorter_max_fov = core->getMovementMgr()->getMaxFov(); StelProjectorP prj = core->getProjection(StelCore::FrameJ2000); core->getMovementMgr()->setMaxFov(distorter_max_fov); // width of the not yet distorted image newProjectorParams.viewportXywh[2] = conf.value("spheric_mirror/newProjectorParams.viewportXywh[2]idth", originalProjectorParams.viewportXywh[2]).toInt(); if (newProjectorParams.viewportXywh[2] <= 0) { newProjectorParams.viewportXywh[2] = originalProjectorParams.viewportXywh[2]; } else if (newProjectorParams.viewportXywh[2] > screen_w) { newProjectorParams.viewportXywh[2] = screen_w; } // height of the not yet distorted image newProjectorParams.viewportXywh[3] = conf.value("spheric_mirror/newProjectorParams.viewportXywh[3]eight", originalProjectorParams.viewportXywh[3]).toInt(); if (newProjectorParams.viewportXywh[3] <= 0) { newProjectorParams.viewportXywh[3] = originalProjectorParams.viewportXywh[3]; } else if (newProjectorParams.viewportXywh[3] > screen_h) { newProjectorParams.viewportXywh[3] = screen_h; } // center of the FOV-disk in the not yet distorted image newProjectorParams.viewportCenter[0] = conf.value("spheric_mirror/viewportCenterX", 0.5*newProjectorParams.viewportXywh[2]).toFloat(); newProjectorParams.viewportCenter[1] = conf.value("spheric_mirror/viewportCenterY", 0.5*newProjectorParams.viewportXywh[3]).toFloat(); // diameter of the FOV-disk in pixels newProjectorParams.viewportFovDiameter = conf.value("spheric_mirror/viewport_fov_diameter", qMin(newProjectorParams.viewportXywh[2],newProjectorParams.viewportXywh[3])).toFloat(); texture_wh = 1; while (texture_wh < newProjectorParams.viewportXywh[2] || texture_wh < newProjectorParams.viewportXywh[3]) texture_wh <<= 1; viewport_texture_offset[0] = (texture_wh-newProjectorParams.viewportXywh[2])>>1; viewport_texture_offset[1] = (texture_wh-newProjectorParams.viewportXywh[3])>>1; newProjectorParams.viewportXywh[0] = (screen_w-newProjectorParams.viewportXywh[2]) >> 1; newProjectorParams.viewportXywh[1] = (screen_h-newProjectorParams.viewportXywh[3]) >> 1; StelApp::getInstance().getCore()->setCurrentStelProjectorParams(newProjectorParams); // init transformation VertexPoint *vertex_point_array = 0; const QString custom_distortion_file = conf.value("spheric_mirror/custom_distortion_file","").toString(); if (custom_distortion_file.isEmpty()) { float texture_triangle_base_length = conf.value("spheric_mirror/texture_triangle_base_length",16.f).toFloat(); if (texture_triangle_base_length > 256.f) texture_triangle_base_length = 256.f; else if (texture_triangle_base_length < 2.f) texture_triangle_base_length = 2.f; max_x = (int)trunc(0.5 + screen_w/texture_triangle_base_length); step_x = screen_w / (double)(max_x-0.5); max_y = (int)trunc(screen_h/(texture_triangle_base_length*0.5*sqrt(3.0))); step_y = screen_h/ (double)max_y; double gamma = conf.value("spheric_mirror/projector_gamma",0.45).toDouble(); if (gamma < 0.0) gamma = 0.0; const float view_scaling_factor = 0.5 * newProjectorParams.viewportFovDiameter / prj->fovToViewScalingFactor(distorter_max_fov*(M_PI/360.0)); texture_point_array = new Vec2f[(max_x+1)*(max_y+1)]; vertex_point_array = new VertexPoint[(max_x+1)*(max_y+1)]; double max_h = 0; SphericMirrorCalculator calc(conf); for (int j=0;j<=max_y;j++) { for (int i=0;i<=max_x;i++) { VertexPoint &vertex_point(vertex_point_array[(j*(max_x+1)+i)]); Vec2f &texture_point(texture_point_array[(j*(max_x+1)+i)]); vertex_point.ver_xy[0] = ((i == 0) ? 0.f : (i == max_x) ? screen_w : (i-0.5f*(j&1))*step_x); vertex_point.ver_xy[1] = j*step_y; Vec3f v,vX,vY; bool rc = calc.retransform( (vertex_point.ver_xy[0]-0.5f*screen_w) / screen_h, (vertex_point.ver_xy[1]-0.5f*screen_h) / screen_h, v,vX,vY); rc &= prj->forward(v); const float x = newProjectorParams.viewportCenter[0] + v[0] * view_scaling_factor; const float y = newProjectorParams.viewportCenter[1] + v[1] * view_scaling_factor; vertex_point.h = rc ? (vX^vY).length() : 0.0; // sharp image up to the border of the fisheye image, at the cost of // accepting clamping artefacts. You can get rid of the clamping // artefacts by specifying a viewport size a little less then // (1< newProjectorParams.viewportXywh[2]) {x=newProjectorParams.viewportXywh[2];vertex_point.h=0;} // if (y < 0.f) {y=0.f;vertex_point.h=0;} // else if (y > newProjectorParams.viewportXywh[3]) {y=newProjectorParams.viewportXywh[3];vertex_point.h=0;} texture_point[0] = (viewport_texture_offset[0]+x)/texture_wh; texture_point[1] = (viewport_texture_offset[1]+y)/texture_wh; if (vertex_point.h > max_h) max_h = vertex_point.h; } } for (int j=0;j<=max_y;j++) { for (int i=0;i<=max_x;i++) { VertexPoint &vertex_point(vertex_point_array[(j*(max_x+1)+i)]); vertex_point.color[0] = vertex_point.color[1] = vertex_point.color[2] = (vertex_point.h<=0.0) ? 0.0 : exp(gamma*log(vertex_point.h/max_h)); vertex_point.color[3] = 1.0f; } } } else { QFile file; QDataStream in; in.setVersion(QDataStream::Qt_4_5); try { file.setFileName(StelFileMgr::findFile(custom_distortion_file)); file.open(QIODevice::ReadOnly); if (file.error() != QFile::NoError) throw("failed to open file"); in.setDevice(&file); } catch (std::runtime_error& e) { qWarning() << "WARNING: could not open custom_distortion_file:" << custom_distortion_file << e.what(); } Q_ASSERT(file.error()!=QFile::NoError); in >> max_x >> max_y; Q_ASSERT(in.status()==QDataStream::Ok && max_x>0 && max_y>0); step_x = screen_w / (double)(max_x-0.5); step_y = screen_h/ (double)max_y; texture_point_array = new Vec2f[(max_x+1)*(max_y+1)]; vertex_point_array = new VertexPoint[(max_x+1)*(max_y+1)]; for (int j=0;j<=max_y;j++) { for (int i=0;i<=max_x;i++) { VertexPoint &vertex_point(vertex_point_array[(j*(max_x+1)+i)]); Vec2f &texture_point(texture_point_array[(j*(max_x+1)+i)]); vertex_point.ver_xy[0] = ((i == 0) ? 0.f : (i == max_x) ? screen_w : (i-0.5f*(j&1))*step_x); vertex_point.ver_xy[1] = j*step_y; float x,y; in >> x >> y >> vertex_point.color[0] >> vertex_point.color[1] >> vertex_point.color[2]; vertex_point.color[3] = 1.0f; Q_ASSERT(in.status()!=QDataStream::Ok); texture_point[0] = (viewport_texture_offset[0]+x)/texture_wh; texture_point[1] = (viewport_texture_offset[1]+y)/texture_wh; } } } // initialize the display list displayVertexList.clear(); for (int j=0;jcolor << v1->color; displayTexCoordList << *t0 << *t1; displayVertexList << v0->ver_xy << v1->ver_xy; } } delete[] vertex_point_array; } StelViewportDistorterFisheyeToSphericMirror::~StelViewportDistorterFisheyeToSphericMirror(void) { if (texture_point_array) delete[] texture_point_array; // TODO repair // prj->setMaxFov(original_max_fov); // prj->setViewport(original_viewport[0],original_viewport[1], // original_viewport[2],original_viewport[3], // original_viewportCenter[0],original_viewportCenter[1], // original_viewportFovDiameter); } void StelViewportDistorterFisheyeToSphericMirror::distortXY(float& x, float& y) const { float texture_x,texture_y; // find the triangle and interpolate accordingly: float dy = y / step_y; const int j = (int)floorf(dy); dy -= j; if (j&1) { float dx = x / step_x + 0.5f*(1.f-dy); const int i = (int)floorf(dx); dx -= i; const Vec2f *const t = texture_point_array + (j*(max_x+1)+i); if (dx + dy <= 1.f) { if (i == 0) { dx -= 0.5f*(1.f-dy); dx *= 2.f; } texture_x = t[0][0] + dx * (t[1][0]-t[0][0]) + dy * (t[max_x+1][0]-t[0][0]); texture_y = t[0][1] + dx * (t[1][1]-t[0][1]) + dy * (t[max_x+1][1]-t[0][1]); } else { if (i == max_x-1) { dx -= 0.5f*(1.f-dy); dx *= 2.f; } texture_x = t[max_x+2][0] + (1.f-dy) * (t[1][0]-t[max_x+2][0]) + (1.f-dx) * (t[max_x+1][0]-t[max_x+2][0]); texture_y = t[max_x+2][1] + (1.f-dy) * (t[1][1]-t[max_x+2][1]) + (1.f-dx) * (t[max_x+1][1]-t[max_x+2][1]); } } else { float dx = x / step_x + 0.5f*dy; const int i = (int)floorf(dx); dx -= i; const Vec2f *const t = texture_point_array + (j*(max_x+1)+i); if (dx >= dy) { if (i == max_x-1) { dx -= 0.5f*dy; dx *= 2.f; } texture_x = t[1][0] + (1.f-dx) * (t[0][0]-t[1][0]) + dy * (t[max_x+2][0]-t[1][0]); texture_y = t[1][1] + (1.f-dx) * (t[0][1]-t[1][1]) + dy * (t[max_x+2][1]-t[1][1]); } else { if (i == 0) { dx -= 0.5f*dy; dx *= 2.f; } texture_x = t[max_x+1][0] + (1.f-dy) * (t[0][0]-t[max_x+1][0]) + dx * (t[max_x+2][0]-t[max_x+1][0]); texture_y = t[max_x+1][1] + (1.f-dy) * (t[0][1]-t[max_x+1][1]) + dx * (t[max_x+2][1]-t[max_x+1][1]); } } x = texture_wh*texture_x - viewport_texture_offset[0] + newProjectorParams.viewportXywh[0]; y = texture_wh*texture_y - viewport_texture_offset[1] + newProjectorParams.viewportXywh[1]; } void StelViewportDistorterFisheyeToSphericMirror::paintViewportBuffer(const QGLFramebufferObject* buf) const { StelPainter sPainter(StelApp::getInstance().getCore()->getProjection2d()); sPainter.enableTexture2d(true); glBindTexture(GL_TEXTURE_2D, buf->texture()); glDisable(GL_BLEND); sPainter.enableClientStates(true, true, true); sPainter.setColorPointer(4, GL_FLOAT, displayColorList.constData()); sPainter.setVertexPointer(2, GL_FLOAT, displayVertexList.constData()); sPainter.setTexCoordPointer(2, GL_FLOAT, displayTexCoordList.constData()); for (int j=0;j