nuclear@78: #include nuclear@95: #include nuclear@85: #include nuclear@82: #include "opengl.h" nuclear@76: #include nuclear@76: #include nuclear@73: #include "goatview.h" nuclear@74: #include "goat3d.h" nuclear@74: nuclear@90: static void draw_grid(); nuclear@90: static void draw_grid(float sz, int nlines, float alpha = 1.0f); nuclear@78: static void draw_node(goat3d_node *node); nuclear@82: static void draw_mesh(goat3d_mesh *mesh); nuclear@90: static int next_pow2(int x); nuclear@78: nuclear@74: goat3d *scene; nuclear@85: static SceneModel *sdata; nuclear@88: static GoatViewport *glview; nuclear@75: nuclear@75: static long anim_time; nuclear@90: static float cam_theta, cam_phi = 25, cam_dist = 8; nuclear@76: static float fov = 60.0; nuclear@82: static bool use_nodes = true; nuclear@83: static bool use_lighting = true; nuclear@92: static bool use_textures = true; nuclear@75: nuclear@88: void post_redisplay() nuclear@88: { nuclear@90: if(glview) { nuclear@90: glview->updateGL(); nuclear@90: } nuclear@88: } nuclear@88: nuclear@82: nuclear@82: GoatView::GoatView() nuclear@82: { nuclear@82: glview = 0; nuclear@85: scene_model = 0; nuclear@82: nuclear@92: QGLFormat glfmt = QGLFormat::defaultFormat(); nuclear@92: glfmt.setSampleBuffers(true); nuclear@92: QGLFormat::setDefaultFormat(glfmt); nuclear@92: nuclear@84: QSettings settings; nuclear@84: resize(settings.value("main/size", QSize(1024, 768)).toSize()); nuclear@84: move(settings.value("main/pos", QPoint(100, 100)).toPoint()); nuclear@84: use_nodes = settings.value("use_nodes", true).toBool(); nuclear@84: use_lighting = settings.value("use_lighting", true).toBool(); nuclear@92: use_textures = settings.value("use_textures", true).toBool(); nuclear@83: nuclear@83: make_center(); // must be first nuclear@82: make_menu(); nuclear@82: make_dock(); nuclear@82: nuclear@82: statusBar(); nuclear@82: nuclear@82: setWindowTitle("GoatView"); nuclear@82: } nuclear@82: nuclear@82: GoatView::~GoatView() nuclear@82: { nuclear@85: delete scene_model; nuclear@85: sdata = 0; nuclear@82: } nuclear@82: nuclear@83: void GoatView::closeEvent(QCloseEvent *ev) nuclear@83: { nuclear@84: QSettings settings; nuclear@84: settings.setValue("main/size", size()); nuclear@84: settings.setValue("main/pos", pos()); nuclear@84: settings.setValue("use_nodes", use_nodes); nuclear@84: settings.setValue("use_lighting", use_lighting); nuclear@83: } nuclear@83: nuclear@83: nuclear@82: bool GoatView::load_scene(const char *fname) nuclear@76: { nuclear@76: if(scene) { nuclear@98: close_scene(); nuclear@76: } nuclear@76: if(!(scene = goat3d_create()) || goat3d_load(scene, fname) == -1) { nuclear@95: QMessageBox::critical(this, "Error", "Failed to load scene file: " + QString(fname)); nuclear@76: return false; nuclear@76: } nuclear@76: nuclear@76: float bmin[3], bmax[3]; nuclear@82: if(goat3d_get_bounds(scene, bmin, bmax) != -1) { nuclear@82: float bsize = (Vector3(bmax[0], bmax[1], bmax[2]) - Vector3(bmin[0], bmin[1], bmin[2])).length(); nuclear@83: cam_dist = bsize / tan(DEG_TO_RAD(fov) / 2.0); nuclear@82: printf("bounds size: %f, cam_dist: %f\n", bsize, cam_dist); nuclear@82: } nuclear@76: nuclear@85: scene_model->set_scene(scene); nuclear@85: treeview->expandAll(); nuclear@85: treeview->resizeColumnToContents(0); nuclear@85: nuclear@85: sdata = scene_model; // set the global sdata ptr nuclear@93: post_redisplay(); nuclear@76: return true; nuclear@76: } nuclear@73: nuclear@95: bool GoatView::load_anim(const char *fname) nuclear@95: { nuclear@95: if(!scene) { nuclear@95: QMessageBox::critical(this, "Error", "You must load a scene before loading any animations!"); nuclear@95: return false; nuclear@95: } nuclear@95: nuclear@95: if(goat3d_load_anim(scene, fname) == -1) { nuclear@95: QMessageBox::critical(this, "Error", QString("Failed to load animation: ") + QString(fname)); nuclear@95: return false; nuclear@95: } nuclear@95: nuclear@95: nuclear@95: long tstart = LONG_MAX, tend = LONG_MIN; nuclear@95: int num_nodes = goat3d_get_node_count(scene); nuclear@95: for(int i=0; i tend) tend = t1; nuclear@95: } nuclear@95: } nuclear@95: nuclear@95: if(tstart != LONG_MAX) { nuclear@97: grp_anim_ctl->setDisabled(false); nuclear@97: grp_anim_time->setDisabled(false); nuclear@95: nuclear@95: slider_time->setMinimum(tstart); nuclear@95: slider_time->setMaximum(tend); nuclear@95: nuclear@95: spin_time->setMinimum(tstart); nuclear@95: spin_time->setMaximum(tend); nuclear@97: nuclear@97: label_time_start->setText(QString::number(tstart)); nuclear@97: label_time_end->setText(QString::number(tend)); nuclear@95: } nuclear@95: nuclear@95: post_redisplay(); nuclear@95: return true; nuclear@95: } nuclear@95: nuclear@73: bool GoatView::make_menu() nuclear@73: { nuclear@82: // file menu nuclear@73: QMenu *menu_file = menuBar()->addMenu("&File"); nuclear@73: nuclear@73: QAction *act_open_sce = new QAction("&Open Scene", this); nuclear@73: act_open_sce->setShortcuts(QKeySequence::Open); nuclear@73: connect(act_open_sce, &QAction::triggered, this, &GoatView::open_scene); nuclear@73: menu_file->addAction(act_open_sce); nuclear@73: nuclear@73: QAction *act_open_anm = new QAction("Open &Animation", this); nuclear@73: connect(act_open_anm, &QAction::triggered, this, &GoatView::open_anim); nuclear@73: menu_file->addAction(act_open_anm); nuclear@73: nuclear@98: QAction *act_close = new QAction("&Close", this); nuclear@98: connect(act_close, &QAction::triggered, this, &GoatView::close_scene); nuclear@98: menu_file->addAction(act_close); nuclear@98: nuclear@98: menu_file->addSeparator(); nuclear@98: nuclear@73: QAction *act_quit = new QAction("&Quit", this); nuclear@73: act_quit->setShortcuts(QKeySequence::Quit); nuclear@90: connect(act_quit, &QAction::triggered, [&](){ qApp->quit(); }); nuclear@73: menu_file->addAction(act_quit); nuclear@82: nuclear@82: // view menu nuclear@82: QMenu *menu_view = menuBar()->addMenu("&View"); nuclear@82: nuclear@82: QAction *act_use_nodes = new QAction("use nodes", this); nuclear@82: act_use_nodes->setCheckable(true); nuclear@82: act_use_nodes->setChecked(use_nodes); nuclear@83: connect(act_use_nodes, &QAction::triggered, this, nuclear@88: [&](){ use_nodes = !use_nodes; post_redisplay(); }); nuclear@82: menu_view->addAction(act_use_nodes); nuclear@83: nuclear@83: QAction *act_use_lighting = new QAction("lighting", this); nuclear@83: act_use_lighting->setCheckable(true); nuclear@83: act_use_lighting->setChecked(use_lighting); nuclear@83: connect(act_use_lighting, &QAction::triggered, glview, &GoatViewport::toggle_lighting); nuclear@83: menu_view->addAction(act_use_lighting); nuclear@83: nuclear@83: // help menu nuclear@83: QMenu *menu_help = menuBar()->addMenu("&Help"); nuclear@83: nuclear@83: QAction *act_about = new QAction("&About", this); nuclear@83: connect(act_about, &QAction::triggered, this, &GoatView::show_about); nuclear@83: menu_help->addAction(act_about); nuclear@73: return true; nuclear@73: } nuclear@73: nuclear@73: bool GoatView::make_dock() nuclear@73: { nuclear@73: // ---- side-dock ---- nuclear@73: QWidget *dock_cont = new QWidget; nuclear@73: QVBoxLayout *dock_vbox = new QVBoxLayout; nuclear@73: dock_cont->setLayout(dock_vbox); nuclear@73: nuclear@90: QDockWidget *dock = new QDockWidget(this); nuclear@73: dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); nuclear@73: dock->setWidget(dock_cont); nuclear@73: addDockWidget(Qt::LeftDockWidgetArea, dock); nuclear@73: nuclear@82: // make the tree view widget nuclear@85: treeview = new QTreeView; nuclear@85: treeview->setAlternatingRowColors(true); nuclear@88: treeview->setSelectionMode(QAbstractItemView::SingleSelection); nuclear@85: dock_vbox->addWidget(treeview); nuclear@82: nuclear@85: scene_model = new SceneModel; nuclear@88: connect(scene_model, &SceneModel::dataChanged, [&](){ post_redisplay(); }); nuclear@85: treeview->setModel(scene_model); nuclear@82: nuclear@88: connect(treeview->selectionModel(), &QItemSelectionModel::selectionChanged, nuclear@88: [&](){ scene_model->selchange(treeview->selectionModel()->selectedIndexes()); }); nuclear@88: nuclear@82: // misc nuclear@82: QPushButton *bn_quit = new QPushButton("quit"); nuclear@82: dock_vbox->addWidget(bn_quit); nuclear@85: connect(bn_quit, &QPushButton::clicked, [&](){ qApp->quit(); }); nuclear@82: nuclear@73: // ---- bottom dock ---- nuclear@73: dock_cont = new QWidget; nuclear@73: QHBoxLayout *dock_hbox = new QHBoxLayout; nuclear@73: dock_cont->setLayout(dock_hbox); nuclear@73: nuclear@90: // animation control box nuclear@97: grp_anim_ctl = new QGroupBox("Animation controls"); nuclear@97: grp_anim_ctl->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred)); nuclear@97: grp_anim_ctl->setDisabled(true); nuclear@95: dock_hbox->addWidget(grp_anim_ctl); nuclear@90: nuclear@95: QVBoxLayout *anim_ctl_box = new QVBoxLayout; nuclear@95: grp_anim_ctl->setLayout(anim_ctl_box); nuclear@90: nuclear@90: chk_loop = new QCheckBox("loop"); nuclear@90: chk_loop->setChecked(false); nuclear@95: anim_ctl_box->addWidget(chk_loop); nuclear@90: nuclear@90: QToolBar *toolbar_ctl = new QToolBar; nuclear@95: anim_ctl_box->addWidget(toolbar_ctl); nuclear@90: nuclear@90: act_rewind = new QAction(style()->standardIcon(QStyle::SP_MediaSkipBackward), "Rewind", this); nuclear@95: connect(act_rewind, &QAction::triggered, [this](){ slider_time->setValue(slider_time->minimum()); }); nuclear@90: toolbar_ctl->addAction(act_rewind); nuclear@95: nuclear@90: act_play = new QAction(style()->standardIcon(QStyle::SP_MediaPlay), "Play", this); nuclear@90: toolbar_ctl->addAction(act_play); nuclear@90: nuclear@97: /* timeline controls nuclear@97: * /------------------------------\ grp_anim_time with vbox_timeline layout nuclear@97: * | /-------+---------+--------\ | nuclear@97: * | | label | spinbox | spacer | <-- hbox_timespin nuclear@97: * | \-------+---------+--------/ | nuclear@97: * +------------------------------+ nuclear@97: * | /-------+---------+--------\ | nuclear@97: * | | label | slider | label | <-- hbox_timeslider nuclear@97: * | \-------+---------+--------/ | nuclear@97: * \------------------------------/ nuclear@97: */ nuclear@97: grp_anim_time = new QGroupBox("Timeline"); nuclear@97: grp_anim_time->setDisabled(true); nuclear@97: dock_hbox->addWidget(grp_anim_time); nuclear@95: nuclear@97: QVBoxLayout *vbox_timeline = new QVBoxLayout; nuclear@97: grp_anim_time->setLayout(vbox_timeline); nuclear@97: QHBoxLayout *hbox_timespin = new QHBoxLayout; nuclear@97: vbox_timeline->addLayout(hbox_timespin); nuclear@97: QHBoxLayout *hbox_timeslider = new QHBoxLayout; nuclear@97: vbox_timeline->addLayout(hbox_timeslider); nuclear@95: nuclear@97: hbox_timespin->addWidget(new QLabel("msec")); nuclear@95: spin_time = new QSpinBox; nuclear@97: hbox_timespin->addWidget(spin_time); nuclear@97: hbox_timespin->addStretch(); nuclear@97: nuclear@97: label_time_start = new QLabel; nuclear@97: hbox_timeslider->addWidget(label_time_start); nuclear@95: nuclear@90: slider_time = new QSlider(Qt::Orientation::Horizontal); nuclear@97: hbox_timeslider->addWidget(slider_time); nuclear@97: nuclear@97: label_time_end = new QLabel; nuclear@97: hbox_timeslider->addWidget(label_time_end); nuclear@95: nuclear@90: connect(slider_time, &QSlider::valueChanged, nuclear@95: [&](){ anim_time = slider_time->value(); spin_time->setValue(anim_time); post_redisplay(); }); nuclear@95: nuclear@95: typedef void (QSpinBox::*ValueChangedIntFunc)(int); nuclear@95: connect(spin_time, (ValueChangedIntFunc)&QSpinBox::valueChanged, nuclear@95: [&](){ anim_time = spin_time->value(); slider_time->setValue(anim_time); post_redisplay(); }); nuclear@73: nuclear@90: dock = new QDockWidget(this); nuclear@73: dock->setAllowedAreas(Qt::BottomDockWidgetArea); nuclear@73: dock->setWidget(dock_cont); nuclear@73: addDockWidget(Qt::BottomDockWidgetArea, dock); nuclear@73: nuclear@73: return true; nuclear@73: } nuclear@73: nuclear@73: bool GoatView::make_center() nuclear@73: { nuclear@88: glview = ::glview = new GoatViewport(this); nuclear@82: setCentralWidget(glview); nuclear@73: return true; nuclear@73: } nuclear@73: nuclear@73: void GoatView::open_scene() nuclear@73: { nuclear@74: std::string fname = QFileDialog::getOpenFileName(this, "Open scene file", "", nuclear@74: "Goat3D Scene (*.goatsce);;All Files (*)").toStdString(); nuclear@74: if(fname.empty()) { nuclear@74: statusBar()->showMessage("Abort: No file selected!"); nuclear@74: return; nuclear@74: } nuclear@74: nuclear@74: statusBar()->showMessage("opening scene file"); nuclear@76: if(!load_scene(fname.c_str())) { nuclear@74: statusBar()->showMessage("failed to load scene file"); nuclear@94: return; nuclear@74: } nuclear@95: statusBar()->showMessage("Successfully loaded scene: " + QString(fname.c_str())); nuclear@73: } nuclear@73: nuclear@98: void GoatView::close_scene() nuclear@98: { nuclear@98: scene_model->clear_scene(); nuclear@98: treeview->reset(); nuclear@98: goat3d_free(scene); nuclear@98: scene = 0; nuclear@100: nuclear@100: grp_anim_ctl->setDisabled(true); nuclear@100: grp_anim_time->setDisabled(true); nuclear@98: } nuclear@98: nuclear@73: void GoatView::open_anim() nuclear@73: { nuclear@94: std::string fname = QFileDialog::getOpenFileName(this, "Open animation file", "", nuclear@94: "Goat3D Animation (*.goatanm);;All Files (*)").toStdString(); nuclear@94: if(fname.empty()) { nuclear@98: statusBar()->showMessage("Abort: No file selected!"); nuclear@94: return; nuclear@94: } nuclear@94: nuclear@94: statusBar()->showMessage("opening animation file"); nuclear@94: if(!load_anim(fname.c_str())) { nuclear@94: statusBar()->showMessage("failed to load animation file"); nuclear@94: return; nuclear@94: } nuclear@95: statusBar()->showMessage("Successfully loaded animation: " + QString(fname.c_str())); nuclear@73: } nuclear@73: nuclear@73: nuclear@73: // ---- OpenGL viewport ---- nuclear@82: GoatViewport::GoatViewport(QWidget *main_win) nuclear@92: : QGLWidget(QGLFormat(QGL::DepthBuffer | QGL::SampleBuffers)) nuclear@73: { nuclear@82: this->main_win = main_win; nuclear@82: initialized = false; nuclear@73: } nuclear@73: nuclear@73: GoatViewport::~GoatViewport() nuclear@73: { nuclear@73: } nuclear@73: nuclear@73: QSize GoatViewport::sizeHint() const nuclear@73: { nuclear@73: return QSize(800, 600); nuclear@73: } nuclear@73: nuclear@82: #define CRITICAL(error, detail) \ nuclear@82: do { \ nuclear@82: fprintf(stderr, "%s: %s\n", error, detail); \ nuclear@82: QMessageBox::critical(main_win, error, detail); \ nuclear@82: abort(); \ nuclear@82: } while(0) nuclear@82: nuclear@73: void GoatViewport::initializeGL() nuclear@73: { nuclear@82: if(initialized) return; nuclear@82: initialized = true; nuclear@82: nuclear@79: init_opengl(); nuclear@79: nuclear@82: if(!GLEW_ARB_transpose_matrix) { nuclear@82: CRITICAL("OpenGL initialization failed", "ARB_transpose_matrix extension not found!"); nuclear@82: } nuclear@82: nuclear@79: glClearColor(0.1f, 0.1f, 0.1f, 1.0f); nuclear@78: nuclear@78: glEnable(GL_DEPTH_TEST); nuclear@78: glEnable(GL_CULL_FACE); nuclear@83: if(use_lighting) { nuclear@83: glEnable(GL_LIGHTING); nuclear@83: } nuclear@83: glEnable(GL_LIGHT0); nuclear@83: nuclear@83: float ldir[] = {-1, 1, 2, 0}; nuclear@83: glLightfv(GL_LIGHT0, GL_POSITION, ldir); nuclear@92: nuclear@92: glEnable(GL_MULTISAMPLE); nuclear@73: } nuclear@73: nuclear@73: void GoatViewport::resizeGL(int xsz, int ysz) nuclear@73: { nuclear@73: glViewport(0, 0, xsz, ysz); nuclear@75: glMatrixMode(GL_PROJECTION); nuclear@75: glLoadIdentity(); nuclear@75: gluPerspective(60.0, (float)xsz / (float)ysz, 0.5, 5000.0); nuclear@73: } nuclear@73: nuclear@73: void GoatViewport::paintGL() nuclear@73: { nuclear@75: glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); nuclear@75: nuclear@75: glMatrixMode(GL_MODELVIEW); nuclear@75: glLoadIdentity(); nuclear@75: glTranslatef(0, 0, -cam_dist); nuclear@75: glRotatef(cam_phi, 1, 0, 0); nuclear@75: glRotatef(cam_theta, 0, 1, 0); nuclear@75: nuclear@90: draw_grid(); nuclear@90: nuclear@82: if(!scene) return; nuclear@82: nuclear@82: if(use_nodes) { nuclear@75: int node_count = goat3d_get_node_count(scene); nuclear@75: for(int i=0; i 0.666666 ? 1.0 : (t - 0.333333) / 0.333333); nuclear@90: nuclear@90: glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BITS); nuclear@90: nuclear@90: glEnable(GL_BLEND); nuclear@90: glDepthFunc(GL_ALWAYS); nuclear@90: nuclear@90: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); nuclear@90: draw_grid(sz0 * 2.0, 10, 1.0 - alpha); nuclear@90: draw_grid(sz1 * 2.0, 10, alpha); nuclear@90: nuclear@90: glPopAttrib(); nuclear@90: } nuclear@90: nuclear@90: static void draw_grid(float sz, int nlines, float alpha) nuclear@90: { nuclear@90: float hsz = sz / 2.0; nuclear@90: float offs = sz / (float)nlines; nuclear@90: nuclear@90: glPushAttrib(GL_ENABLE_BIT); nuclear@90: glDisable(GL_LIGHTING); nuclear@90: glDisable(GL_TEXTURE_2D); nuclear@90: nuclear@90: glLineWidth(2.0); nuclear@90: glBegin(GL_LINES); nuclear@90: glColor4f(1, 0, 0, alpha); nuclear@90: glVertex3f(-hsz, 0, 0); nuclear@90: glVertex3f(hsz, 0, 0); nuclear@90: glColor4f(0, 0, 1, alpha); nuclear@90: glVertex3f(0, 0, -hsz); nuclear@90: glVertex3f(0, 0, hsz); nuclear@90: glEnd(); nuclear@90: nuclear@90: glLineWidth(1.0); nuclear@90: glBegin(GL_LINES); nuclear@90: glColor4f(0.5, 0.5, 0.5, alpha); nuclear@90: for(int i=0; i 0 ? -1.0 : 1.0; nuclear@90: glVertex3f(-hsz, 0, dist * sign); nuclear@90: glVertex3f(hsz, 0, dist * sign); nuclear@90: glVertex3f(dist * sign, 0, -hsz); nuclear@90: glVertex3f(dist * sign, 0, hsz); nuclear@90: } nuclear@90: } nuclear@90: glEnd(); nuclear@90: nuclear@90: glPopAttrib(); nuclear@90: } nuclear@90: nuclear@75: static void draw_node(goat3d_node *node) nuclear@75: { nuclear@85: SceneNodeData *data = sdata ? sdata->get_node_data(node) : 0; nuclear@85: if(!data) return; nuclear@85: nuclear@75: float xform[16]; nuclear@75: goat3d_get_node_matrix(node, xform, anim_time); nuclear@78: nuclear@78: glPushMatrix(); nuclear@82: glMultTransposeMatrixf(xform); nuclear@75: nuclear@85: if(data->visible) { nuclear@85: if(goat3d_get_node_type(node) == GOAT3D_NODE_MESH) { nuclear@85: goat3d_mesh *mesh = (goat3d_mesh*)goat3d_get_node_object(node); nuclear@75: nuclear@85: draw_mesh(mesh); nuclear@88: nuclear@88: if(data->selected) { nuclear@88: float bmin[3], bmax[3]; nuclear@88: goat3d_get_mesh_bounds(mesh, bmin, bmax); nuclear@88: nuclear@88: glPushAttrib(GL_ENABLE_BIT); nuclear@88: glDisable(GL_LIGHTING); nuclear@88: nuclear@88: glColor3f(0.3, 1, 0.2); nuclear@88: nuclear@88: glBegin(GL_LINE_LOOP); nuclear@88: glVertex3f(bmin[0], bmin[1], bmin[2]); nuclear@88: glVertex3f(bmax[0], bmin[1], bmin[2]); nuclear@88: glVertex3f(bmax[0], bmin[1], bmax[2]); nuclear@88: glVertex3f(bmin[0], bmin[1], bmax[2]); nuclear@88: glEnd(); nuclear@88: glBegin(GL_LINE_LOOP); nuclear@88: glVertex3f(bmin[0], bmax[1], bmin[2]); nuclear@88: glVertex3f(bmax[0], bmax[1], bmin[2]); nuclear@88: glVertex3f(bmax[0], bmax[1], bmax[2]); nuclear@88: glVertex3f(bmin[0], bmax[1], bmax[2]); nuclear@88: glEnd(); nuclear@88: glBegin(GL_LINES); nuclear@88: glVertex3f(bmin[0], bmin[1], bmin[2]); nuclear@88: glVertex3f(bmin[0], bmax[1], bmin[2]); nuclear@88: glVertex3f(bmin[0], bmin[1], bmax[2]); nuclear@88: glVertex3f(bmin[0], bmax[1], bmax[2]); nuclear@88: glVertex3f(bmax[0], bmin[1], bmin[2]); nuclear@88: glVertex3f(bmax[0], bmax[1], bmin[2]); nuclear@88: glVertex3f(bmax[0], bmin[1], bmax[2]); nuclear@88: glVertex3f(bmax[0], bmax[1], bmax[2]); nuclear@88: glEnd(); nuclear@88: nuclear@88: glPopAttrib(); nuclear@88: } nuclear@85: } nuclear@75: } nuclear@75: nuclear@83: int num_child = goat3d_get_node_child_count(node); nuclear@75: for(int i=0; ix(); nuclear@78: prev_y = ev->y(); nuclear@78: } nuclear@78: nuclear@78: void GoatViewport::mouseMoveEvent(QMouseEvent *ev) nuclear@78: { nuclear@78: int dx = ev->x() - prev_x; nuclear@78: int dy = ev->y() - prev_y; nuclear@80: prev_x = ev->x(); nuclear@80: prev_y = ev->y(); nuclear@78: nuclear@78: if(!dx && !dy) return; nuclear@78: nuclear@78: if(ev->buttons() & Qt::LeftButton) { nuclear@78: cam_theta += dx * 0.5; nuclear@78: cam_phi += dy * 0.5; nuclear@78: nuclear@78: if(cam_phi < -90) cam_phi = -90; nuclear@78: if(cam_phi > 90) cam_phi = 90; nuclear@78: } nuclear@78: if(ev->buttons() & Qt::RightButton) { nuclear@78: cam_dist += dy * 0.1; nuclear@78: nuclear@78: if(cam_dist < 0.0) cam_dist = 0.0; nuclear@78: } nuclear@78: updateGL(); nuclear@78: } nuclear@83: nuclear@83: static const char *about_str = nuclear@83: "GoatView - Goat3D scene file viewer
" nuclear@83: "Copyright (C) 2014 John Tsiombikas <nuclear@mutantstargoat.com>
" nuclear@83: "
" nuclear@83: "This program is free software: you can redistribute it and/or modify
" nuclear@83: "it under the terms of the GNU General Public License as published by
" nuclear@83: "the Free Software Foundation, either version 3 of the License, or
" nuclear@83: "(at your option) any later version.
" nuclear@83: "
" nuclear@83: "This program is distributed in the hope that it will be useful,
" nuclear@83: "but WITHOUT ANY WARRANTY; without even the implied warranty of
" nuclear@83: "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
" nuclear@83: "GNU General Public License for more details.
" nuclear@83: "
" nuclear@83: "You should have received a copy of the GNU General Public License
" nuclear@83: "along with this program. If not, see http://www.gnu.org/licenses/gpl."; nuclear@83: nuclear@83: void GoatView::show_about() nuclear@83: { nuclear@83: QMessageBox::information(this, "About GoatView", about_str); nuclear@83: } nuclear@90: nuclear@93: nuclear@90: static int next_pow2(int x) nuclear@90: { nuclear@90: x--; nuclear@90: x = (x >> 1) | x; nuclear@90: x = (x >> 2) | x; nuclear@90: x = (x >> 4) | x; nuclear@90: x = (x >> 8) | x; nuclear@90: x = (x >> 16) | x; nuclear@90: return x + 1; nuclear@90: }