From 15138ed63622c4a885b0bf9f8a00a2e8ba1d60e6 Mon Sep 17 00:00:00 2001 From: trisyoungs Date: Sun, 15 Jul 2012 13:44:20 +0000 Subject: [PATCH] Overhaul to rendering engine. Moved RenderPrimitives to separate files (engine_primitives.h|cpp). Made an external instantiation of RenderEngine, rather than it being owned by TCanvas. Moved main scene rendering code from TCanvas to RenderEngine. Added default use of QGLPixelBuffer for offscreen rendering. Removed references to stored Model pixel data throughout, since it was no longer used. Moved VBO check and setup code to RenderEngine constructor. Removed self-handling of QGLFormat. Renamed Prefs::useFrameBuffer_ to Prefs::usePixelBuffers_. Removed Prefs::useNiceText_ option since it is no longer valid. Added separate primitive lists for line objects (renamed from stickLines_ etc.) to make handling easier. Fragment rendering now is filtered into the GuiObject (or NormalGuiLineObject and SelectedGuiLineObject) lists. --- CMakeLists.txt | 2 +- TODO | 2 +- aten.kdev4 | 4 +- aten.spec | 2 +- configure.ac | 2 +- data/filters/gromacs | 15 +- extra/aten.dsc | 4 +- src/classes/prefs.cpp | 29 +- src/classes/prefs.h | 26 +- src/command/image.cpp | 27 +- src/gui/contextmenu_funcs.cpp | 20 +- src/gui/glyphs_funcs.cpp | 12 +- src/gui/gui.cpp | 69 +-- src/gui/gui.h | 13 +- src/gui/mainmenuactions.cpp | 29 +- src/gui/mainwindow_init.cpp | 4 +- src/gui/prefs.h | 4 +- src/gui/prefs.ui | 32 +- src/gui/prefs_funcs.cpp | 50 +- src/gui/scriptmovie_funcs.cpp | 9 +- src/gui/tcanvas.uih | 81 +-- src/gui/tcanvas_funcs.cpp | 411 +------------- src/gui/tcanvas_input.cpp | 24 +- src/gui/tcanvas_render.cpp | 135 ++--- src/gui/toolbaractions.cpp | 2 +- src/main/version.h | 4 +- src/model/model.cpp | 9 +- src/model/model.h | 14 - src/model/trajectory.cpp | 2 - src/model/view.cpp | 47 -- src/parser/prefs.cpp | 36 +- src/parser/prefs.h | 2 +- src/render/CMakeLists.txt | 2 + src/render/Makefile.am | 4 +- src/render/engine.cpp | 855 +++++++++++++++--------------- src/render/engine.h | 160 +++--- src/render/engine_glyph.cpp | 40 +- src/render/engine_model.cpp | 340 +++++++++--- src/render/engine_primitives.cpp | 311 +++++++++++ src/render/engine_primitives.h | 96 ++++ src/render/engine_useractions.cpp | 95 +++- src/render/engine_windows.cpp | 10 +- src/render/primitive.cpp | 18 +- src/render/primitive.h | 4 + src/render/textprimitive.cpp | 19 +- src/render/textprimitive.h | 4 +- src/render/triangles.cpp | 2 +- 47 files changed, 1550 insertions(+), 1532 deletions(-) create mode 100644 src/render/engine_primitives.cpp create mode 100644 src/render/engine_primitives.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ae1f7fef8..beccf9699 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ project(Aten) set(DESCRIPTION "Aten - Atomic configuration builder and editor") set(AUTHOR "Tristan Youngs") set(VERSION_MAJOR "1") -set(VERSION_MINOR "828") +set(VERSION_MINOR "829") set(VERSION_PATCH "1") set(CMAKE_BUILD_TYPE "Release") diff --git a/TODO b/TODO index c107a5367..21ab2246a 100644 --- a/TODO +++ b/TODO @@ -1,10 +1,10 @@

Known Bugs

diff --git a/aten.kdev4 b/aten.kdev4 index fcd5364c1..52b683bec 100644 --- a/aten.kdev4 +++ b/aten.kdev4 @@ -1,3 +1,3 @@ [Project] -Manager=KDevCustomMakeManager -Name=aten +Manager=KDevCMakeManager +Name=aten-pixmap diff --git a/aten.spec b/aten.spec index a480090f5..bcfed077c 100644 --- a/aten.spec +++ b/aten.spec @@ -4,7 +4,7 @@ # Name, brief description, and version Summary: Aten - Atomic configuration builder and editor Name: %{shortname} -Version: 1.828 +Version: 1.829 Release: 1 License: GPL %define fullname %{name}-%{version} diff --git a/configure.ac b/configure.ac index ac20d965f..ff109af26 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ(2.60) # Set program name, version, bug-address and source directory -m4_define([ATEN_VERSION],[1.828]) +m4_define([ATEN_VERSION],[1.829]) AC_INIT(aten,ATEN_VERSION,tris@projectaten.net) AC_CONFIG_SRCDIR([src/main.cpp]) diff --git a/data/filters/gromacs b/data/filters/gromacs index 9820a5170..f57a91e67 100644 --- a/data/filters/gromacs +++ b/data/filters/gromacs @@ -119,18 +119,13 @@ filter(type="exportexpression",name="Gromacs .top File", extension="top", glob=" i = p.atoms[n]; i.bit = chgrp; # All atoms attached to this atom which have only one bond will be part of the same charge group - for (Bond x = i.bonds; x; ++x) + for (Bond bnd = i.bonds; bnd; ++bnd) { - i.bit = chgrp; - # All atoms attached to this atom which have only one bond will be part of the same charge group - for (Bond x = i.bonds; x; ++x) - { - j = x.partner(i); - if ((j.nBonds != 1) || (j.bit != 0)) continue; - j.bit = chgrp; - } - ++chgrp; + j = bnd.partner(i); + if ((j.nBonds != 1) || (j.bit != 0)) continue; + j.bit = chgrp; } + ++chgrp; } } } diff --git a/extra/aten.dsc b/extra/aten.dsc index 300503ecd..6ee30a34c 100644 --- a/extra/aten.dsc +++ b/extra/aten.dsc @@ -1,9 +1,9 @@ Format: 1.0 Source: aten -Version: 1.828 +Version: 1.829 Binary: aten Maintainer: Tristan Youngs Architecture: any Build-Depends: debhelper (>= 4.1.16), libqt4-dev | libqt4-core, libqt4-opengl-dev, libreadline5-dev | libreadline-dev, libgl1-mesa-dev, pkgconfig | pkg-config, libncurses5 Files: - 4aec60597e330aeae02c1af550c6deab 4293031 aten-1.828.tar.gz + 4aec60597e330aeae02c1af550c6deab 4293031 aten-1.829.tar.gz diff --git a/src/classes/prefs.cpp b/src/classes/prefs.cpp index b62e25eed..c4e96361b 100644 --- a/src/classes/prefs.cpp +++ b/src/classes/prefs.cpp @@ -340,14 +340,13 @@ Prefs::Prefs() partitionGridSize_.set(50,50,50); // Rendering Options - useNiceText_ = TRUE; distanceLabelFormat_ = "%0.3f "; angleLabelFormat_ = "%0.2f"; chargeLabelFormat_ = "(%0.3f e)"; - labelSize_ = 10; + labelSize_ = 1.5; manualSwapBuffers_ = FALSE; mouseMoveFilter_ = 10; - useFrameBuffer_ = FALSE; + usePixelBuffers_ = TRUE; renderDashedAromatics_ = TRUE; nModelsPerRow_ = 2; drawHydrogenBonds_ = FALSE; @@ -1892,29 +1891,17 @@ const char *Prefs::chargeLabelFormat() } // Set the scale of labels in the model -void Prefs::setLabelSize(int size) +void Prefs::setLabelSize(double size) { labelSize_ = size; } // Return the current label scale -int Prefs::labelSize() const +double Prefs::labelSize() const { return labelSize_; } -// Set whether to use nice text rendering -void Prefs::setUseNiceText(bool b) -{ - useNiceText_ = b; -} - -// Return whether to use nice text rendering -bool Prefs::useNiceText() const -{ - return useNiceText_; -} - // Set manual swapbuffers void Prefs::setManualSwapBuffers(bool on) { @@ -1928,15 +1915,15 @@ bool Prefs::manualSwapBuffers() const } // Return whether manual buffer swapping is enabled -bool Prefs::useFrameBuffer() const +bool Prefs::usePixelBuffers() const { - return useFrameBuffer_; + return usePixelBuffers_; } // Set manual swapbuffers -void Prefs::setUseFrameBuffer(bool on) +void Prefs::setUsePixelBuffers(bool on) { - useFrameBuffer_ = on; + usePixelBuffers_ = on; } // Return whether to use solid or dashed circles for aromatic ring rendering diff --git a/src/classes/prefs.h b/src/classes/prefs.h index 46ffb2a27..461565c77 100644 --- a/src/classes/prefs.h +++ b/src/classes/prefs.h @@ -308,14 +308,12 @@ class Prefs Dnchar angleLabelFormat_; // C-style format for charge label values Dnchar chargeLabelFormat_; - // Pointsize for labels - int labelSize_; - // Use QGlWidget::renderText (FALSE) or QPainter::drawText (TRUE) for labels etc. - bool useNiceText_; + // Relative size for text labels, in percentage of view height + double labelSize_; // Flag to manually perform swapBuffers bool manualSwapBuffers_; - // Flag to use framebuffer for image saving rather than the renderPixmap() method - bool useFrameBuffer_; + // Flag to use pixelbuffers for image saving rather than the renderPixmap() method + bool usePixelBuffers_; // Whether to use solid or dashed circles for aromatic ring rendering bool renderDashedAromatics_; // Mouse move event filter rate @@ -341,21 +339,17 @@ class Prefs // Return C-style format for charge label values const char *chargeLabelFormat(); // Set the pointsize of labels in the model - void setLabelSize(int size); + void setLabelSize(double size); // Return the current label pointsize - int labelSize() const; - // Set whether to use nice text rendering - void setUseNiceText(bool b); - // Return whether to use nice text rendering - bool useNiceText() const; + double labelSize() const; // Set manual swapbuffers void setManualSwapBuffers(bool on); // Return whether manual buffer swapping is enabled bool manualSwapBuffers() const; - // Set usage of framebuffer in image saving - void setUseFrameBuffer(bool on); - // Return whether to use framebuffer for image saving - bool useFrameBuffer() const; + // Set usage of pixelbuffers in image saving + void setUsePixelBuffers(bool on); + // Return whether to use pixelbuffers for image saving + bool usePixelBuffers() const; // Return whether to use solid or dashed circles for aromatic ring rendering bool renderDashedAromatics(); // Set whether to use solid or dashed circles for aromatic ring rendering diff --git a/src/command/image.cpp b/src/command/image.cpp index ece89e0dc..e2d8a5115 100644 --- a/src/command/image.cpp +++ b/src/command/image.cpp @@ -36,7 +36,6 @@ int movieSetup(bool pre, int height) { static bool framemodel = prefs.frameCurrentModel(), frameview = prefs.frameWholeView(), viewglobe = prefs.viewRotationGlobe(); - static int oldlabelsize = prefs.labelSize(); if (pre) { // Check that defined encoder exe exists @@ -65,11 +64,6 @@ int movieSetup(bool pre, int height) } while (fileExists(basename)); msg.print("First temporary basename for movie images is '%s'.\n", basename.get()); - // Temporarily adjust label size... - oldlabelsize = prefs.labelSize(); - int newlabelsize = int (oldlabelsize*( (1.0*height / gui.mainCanvas()->height()) )); - prefs.setLabelSize(newlabelsize); - return runid; } else @@ -77,9 +71,6 @@ int movieSetup(bool pre, int height) prefs.setFrameCurrentModel(framemodel); prefs.setFrameWholeView(frameview); prefs.setViewRotationGlobe(viewglobe); - - // Restore label size - prefs.setLabelSize(oldlabelsize); } } @@ -172,8 +163,8 @@ bool Command::function_SaveBitmap(CommandNode *c, Bundle &obj, ReturnValue &rv) if (obj.notifyNull(Bundle::ModelPointer)) return FALSE; // Convert format to bitmap_format - GuiQt::BitmapFormat bf = GuiQt::bitmapFormat(c->argc(0)); - if (bf == GuiQt::nBitmapFormats) + RenderEngine::BitmapFormat bf = RenderEngine::bitmapFormat(c->argc(0)); + if (bf == RenderEngine::nBitmapFormats) { msg.print("Unrecognised bitmap format.\n"); return FALSE; @@ -202,9 +193,9 @@ bool Command::function_SaveBitmap(CommandNode *c, Bundle &obj, ReturnValue &rv) msg.print("Maximum number of frames for image redirect reached. Raising error...\n"); result = FALSE; } - else result = gui.saveImage(filename, bf, width, height, quality); + else result = engine().saveImage(filename, bf, width, height, quality); } - else result = gui.saveImage(c->argc(1), bf, width, height, quality); + else result = engine().saveImage(c->argc(1), bf, width, height, quality); prefs.setViewRotationGlobe(viewglobe); return result; @@ -235,7 +226,7 @@ bool Command::function_SaveMovie(CommandNode *c, Bundle &obj, ReturnValue &rv) // Check initial movie 'setup' int runid = movieSetup(TRUE, height); - if (runid == -1 ) return FALSE; + if (runid == -1) return FALSE; // Save all trajectory frame images QPixmap pixmap; @@ -249,9 +240,9 @@ bool Command::function_SaveMovie(CommandNode *c, Bundle &obj, ReturnValue &rv) { obj.m->seekTrajectoryFrame(n, TRUE); basename.sprintf("%s%caten-movie-%i-%i-%09i.png", prefs.tempDir(), PATHSEP, gui.pid(), runid, n); - gui.mainCanvas()->postRedisplay(TRUE); +// gui.mainCanvas()->postRedisplay(); - pixmap = gui.mainCanvas()->generateImage(width, height, TRUE); + pixmap = engine().renderSceneImage(RenderEngine::HighQuality, width, height); pixmap.save(basename.get(), "png", -1); files << basename.get(); @@ -321,9 +312,9 @@ bool Command::function_SaveVibrationMovie(CommandNode *c, Bundle &obj, ReturnVal obj.rs()->setVibrationFrameIndex(n); basename.sprintf("%s%caten-movie-%i-%i-%09i.png", prefs.tempDir(), PATHSEP, gui.pid(), runid, n); - gui.mainCanvas()->postRedisplay(TRUE); +// gui.mainCanvas()->postRedisplay(); - pixmap = gui.mainCanvas()->generateImage(width, height, TRUE); + pixmap = engine().renderSceneImage(RenderEngine::HighQuality, width, height); pixmap.save(basename.get(), "png", -1); if (!progress.update(progid,n)) diff --git a/src/gui/contextmenu_funcs.cpp b/src/gui/contextmenu_funcs.cpp index 604e5aa25..2a9520fe4 100644 --- a/src/gui/contextmenu_funcs.cpp +++ b/src/gui/contextmenu_funcs.cpp @@ -35,7 +35,7 @@ Atom *target = NULL; void GuiQt::updateContextMenu() { msg.enter("GuiQt::updateContextMenu"); - Model *viewTarget = gui.mainCanvas()->displayModel(); + Model *viewTarget = aten.currentModel(); // Enable bond, angle, and torsion editing int nselected = (viewTarget == NULL ? 0 : viewTarget->nSelected()); mainWindow_->ui.actionSetBondLength->setEnabled(FALSE); @@ -65,7 +65,7 @@ void GuiQt::callContextMenu(Atom *undermouse, int x, int y) // If the atom under the mouse is selected, just run the popup. If it is not selected, deselect everything else and select it QPoint pos(x,y); // printf("AtomPopup: model %li, undermouse = %li, nselected = %i\n", viewTarget, target, viewTarget->nSelected()); - Model *viewTarget = gui.mainCanvas()->displayModel(); + Model *viewTarget = aten.currentModel(); if (!target->isSelected()) { viewTarget->beginUndoState("Select atom (Context Menu)"); @@ -86,7 +86,7 @@ void GuiQt::callContextMenu(Atom *undermouse, int x, int y) // Set atom style void AtenForm::setAtomStyle(Atom::DrawStyle ds) { - if ((target == NULL) || (gui.mainCanvas()->displayModel()->nSelected() > 1)) CommandNode::run(Command::AtomStyle, "c", Atom::drawStyle(ds)); + if ((target == NULL) || (aten.currentModel()->nSelected() > 1)) CommandNode::run(Command::AtomStyle, "c", Atom::drawStyle(ds)); else CommandNode::run(Command::AtomStyle, "ci", Atom::drawStyle(ds), target->id()+1); target = NULL; } @@ -118,7 +118,7 @@ void AtenForm::on_actionAtomStyleScaled_triggered(bool checked) // Set atom labels void AtenForm::setAtomLabel(Atom::AtomLabel al) { - if ((target == NULL) || (gui.mainCanvas()->displayModel()->nSelected() > 1)) CommandNode::run(Command::Label, "c", Atom::atomLabel(al)); + if ((target == NULL) || (aten.currentModel()->nSelected() > 1)) CommandNode::run(Command::Label, "c", Atom::atomLabel(al)); else CommandNode::run(Command::Label, "ci", Atom::atomLabel(al), target->id()+1); target = NULL; gui.mainCanvas()->postRedisplay(); @@ -128,7 +128,7 @@ void AtenForm::setAtomLabel(Atom::AtomLabel al) void AtenForm::removeAtomLabels(bool all) { if (all) CommandNode::run(Command::ClearLabels, ""); - else if ((target == NULL) || (gui.mainCanvas()->displayModel()->nSelected() > 1)) CommandNode::run(Command::RemoveLabels, ""); + else if ((target == NULL) || (aten.currentModel()->nSelected() > 1)) CommandNode::run(Command::RemoveLabels, ""); else CommandNode::run(Command::RemoveLabels, "i", target->id()+1); target = NULL; gui.mainCanvas()->postRedisplay(); @@ -236,7 +236,7 @@ void AtenForm::on_actionOrderReorder_triggered(bool checked) // Set atom hidden void AtenForm::setAtomHidden(bool hidden) { - if ((target == NULL) || (gui.mainCanvas()->displayModel()->nSelected() > 1)) + if ((target == NULL) || (aten.currentModel()->nSelected() > 1)) { if (hidden) CommandNode::run(Command::Hide, ""); else CommandNode::run(Command::Show, ""); @@ -261,14 +261,14 @@ void AtenForm::on_actionAtomProbe_triggered(bool checked) void AtenForm::on_actionAtomFixPosition_triggered(bool checked) { - if ((target == NULL) || (gui.mainCanvas()->displayModel()->nSelected() > 1)) CommandNode::run(Command::Fix, ""); + if ((target == NULL) || (aten.currentModel()->nSelected() > 1)) CommandNode::run(Command::Fix, ""); else CommandNode::run(Command::Fix, "i", target->id()+1); gui.mainCanvas()->postRedisplay(); } void AtenForm::on_actionAtomFreePosition_triggered(bool checked) { - if ((target == NULL) || (gui.mainCanvas()->displayModel()->nSelected() > 1)) CommandNode::run(Command::Free, ""); + if ((target == NULL) || (aten.currentModel()->nSelected() > 1)) CommandNode::run(Command::Free, ""); else CommandNode::run(Command::Free, "i", target->id()+1); gui.mainCanvas()->postRedisplay(); } @@ -296,7 +296,7 @@ void AtenForm::on_actionCentreAtOrigin_triggered(bool checked) void AtenForm::on_actionCreateFragment_triggered(bool checked) { - Model *viewTarget = gui.mainCanvas()->displayModel(); + Model *viewTarget = aten.currentModel(); aten.addFragmentFromSelection(viewTarget, "Selections"); gui.fragmentsWidget->refresh(); } @@ -322,7 +322,7 @@ void AtenForm::createGlyph() // Create glyph in model CommandNode::run(Command::NewGlyph, "c", Glyph::glyphType(gt)); // Set data to atom selection - Model *viewTarget = gui.mainCanvas()->displayModel(); + Model *viewTarget = aten.currentModel(); n = 1; for (Refitem *ri = viewTarget->selection(); ri != NULL; ri = ri->next) { diff --git a/src/gui/glyphs_funcs.cpp b/src/gui/glyphs_funcs.cpp index 18c006197..63c6beece 100644 --- a/src/gui/glyphs_funcs.cpp +++ b/src/gui/glyphs_funcs.cpp @@ -209,7 +209,7 @@ void GlyphsWidget::on_GlyphList_currentRowChanged(int row) g = (Glyph*) ((TListWidgetItem*) ui.GlyphList->item(i))->data.asPointer(VTypes::GlyphData); g->setSelected(ui.GlyphList->item(i)->isSelected()); } - gui.mainCanvas()->postRedisplay(FALSE, TRUE); + gui.mainCanvas()->postRedisplay(); } void GlyphsWidget::on_GlyphList_itemSelectionChanged() @@ -312,21 +312,21 @@ void GlyphsWidget::on_GlyphInvertSelectionButton_clicked(bool checked) g->setSelected( ui.GlyphList->item(i)->isSelected()); } refreshing_ = FALSE; - gui.mainCanvas()->postRedisplay(FALSE, TRUE); + gui.mainCanvas()->postRedisplay(); } void GlyphsWidget::on_GlyphHideAllButton_clicked(bool checked) { Model *m = aten.currentModelOrFrame(); for (Glyph *g = m->glyphs(); g != NULL; g = g->next) g->setVisible(FALSE); - gui.mainCanvas()->postRedisplay(FALSE, TRUE); + gui.mainCanvas()->postRedisplay(); } void GlyphsWidget::on_GlyphHideNoneButton_clicked(bool checked) { Model *m = aten.currentModelOrFrame(); for (Glyph *g = m->glyphs(); g != NULL; g = g->next) g->setVisible(TRUE); - gui.mainCanvas()->postRedisplay(FALSE, TRUE); + gui.mainCanvas()->postRedisplay(); } void GlyphsWidget::on_GlyphHideSelectedButton_clicked(bool checked) @@ -337,7 +337,7 @@ void GlyphsWidget::on_GlyphHideSelectedButton_clicked(bool checked) g = (Glyph*) ((TListWidgetItem*) ui.GlyphList->item(i))->data.asPointer(VTypes::GlyphData); g->setVisible(FALSE); } - gui.mainCanvas()->postRedisplay(FALSE, TRUE); + gui.mainCanvas()->postRedisplay(); } void GlyphsWidget::on_GlyphTypeCombo_currentIndexChanged(int row) @@ -360,7 +360,7 @@ void GlyphsWidget::on_GlyphTypeCombo_currentIndexChanged(int row) item->setText(s); } aten.currentModelOrFrame()->changeLog.add(Log::Glyphs); - gui.mainCanvas()->postRedisplay(FALSE, TRUE); + gui.mainCanvas()->postRedisplay(); } void GlyphsWidget::on_GlyphLineEdit_returnPressed() diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 910a483f0..cef4b6635 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -66,28 +66,6 @@ // External Declarations GuiQt gui; -// Bitmap Image Formats (conform to allowable pixmap formats in Qt) -const char *bitmapFormatFilters[GuiQt::nBitmapFormats] = { "Windows Bitmap (*.bmp)", "Joint Photographic Experts Group (*.jpg)", "Portable Network Graphics (*.png)", "Portable Pixmap (*.ppm)", "X11 Bitmap (*.xbm)", "X11 Pixmap (*.xpm)" }; -const char *bitmapFormatExtensions[GuiQt::nBitmapFormats] = { "bmp", "jpg", "png", "ppm", "xbm", "xpm" }; -GuiQt::BitmapFormat GuiQt::bitmapFormat(const char *s, bool reportError) -{ - GuiQt::BitmapFormat bf = (GuiQt::BitmapFormat) enumSearch("bitmap format",GuiQt::nBitmapFormats,bitmapFormatExtensions,s); - if ((bf == GuiQt::nBitmapFormats) && reportError) enumPrintValid(GuiQt::nBitmapFormats,bitmapFormatExtensions); - return bf; -} -GuiQt::BitmapFormat GuiQt::bitmapFormatFromFilter(const char *s) -{ - return (GuiQt::BitmapFormat) enumSearch("bitmap format",GuiQt::nBitmapFormats,bitmapFormatFilters,s); -} -const char *GuiQt::bitmapFormatFilter(GuiQt::BitmapFormat bf) -{ - return bitmapFormatFilters[bf]; -} -const char *GuiQt::bitmapFormatExtension(GuiQt::BitmapFormat bf) -{ - return bitmapFormatExtensions[bf]; -} - // Constructor GuiQt::GuiQt() { @@ -95,7 +73,7 @@ GuiQt::GuiQt() application_ = NULL; mainWindow_ = NULL; mainCanvas_ = NULL; - mainContext_ = NULL; + prefsDialog = NULL; forcefieldEditorDialog = NULL; loadModelDialog = NULL; @@ -166,16 +144,14 @@ void GuiQt::initialise(int &argc, char **argv) { mainWindow_ = new AtenForm; - // Create the main QGLWidget - QGLFormat format; - format.setSampleBuffers(TRUE); - mainContext_ = new QGLContext(format); - // Create the widget - mainCanvas_ = new TCanvas(mainContext_, mainWindow_); + mainCanvas_ = new TCanvas(engine().canvasContext(), mainWindow_); mainCanvas_->probeFeatures(); mainCanvas_->setGeometry(0,0,800,600); mainCanvas_->setCursor(Qt::ArrowCursor); + + // Reinitialise pixelbuffer so we can share its QGLContext + engine().initialisePixelBuffer(800,600, TRUE); } msg.exit("GuiQt::initialise"); @@ -296,8 +272,7 @@ void GuiQt::run() // Reset view of all loaded models for (Model *m = aten.models(); m != NULL; m = m->next) if (!prefs.keepView()) m->resetView(); - gui.mainCanvas()->setDrawingTarget(TCanvas::ScreenTarget); - gui.mainCanvas()->postRedisplay(TRUE); + gui.mainCanvas()->postRedisplay(); // Display message box warning if there was a filter load error if (aten.nFiltersFailed() == -1) @@ -525,38 +500,6 @@ bool GuiQt::saveBeforeClose() return TRUE; } -// Save image of current view -bool GuiQt::saveImage(const char *filename, BitmapFormat bf, int width, int height, int quality) -{ - msg.enter("GuiQt::saveImage"); - if (bf == GuiQt::nBitmapFormats) - { - msg.print("Invalid bitmap format given to Gui::saveImage().\n"); - msg.exit("GuiQt::saveImage"); - return FALSE; - } - - QPixmap pixmap; - // Get current mainCanvas_ geometry if none was specified - if (width == 0) width = mainCanvas_->width(); - if (height == 0) height = mainCanvas_->height(); - // Temporarily adjust label size... - int oldlabelsize = prefs.labelSize(); - int newlabelsize = int (oldlabelsize*( (1.0*height / mainCanvas_->height()) )); - prefs.setLabelSize(newlabelsize); - - pixmap = mainCanvas_->generateImage(width, height, TRUE); - - // Restore label size - prefs.setLabelSize(oldlabelsize); - - pixmap.save(filename, GuiQt::bitmapFormatExtension(bf), quality); - msg.print("Saved current view as '%s' [%ix%i %s]\n", filename, width, height, GuiQt::bitmapFormatFilter(bf)); - - msg.exit("GuiQt::saveImage"); - return TRUE; -} - // Return main view Widget TCanvas *GuiQt::mainCanvas() { diff --git a/src/gui/gui.h b/src/gui/gui.h index 79c1fd28a..36afa6567 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -72,18 +72,12 @@ class Model; class Forcefield; class Grid; -// QT4 GUI +// Qt4 GUI class GuiQt { public: // Constructor GuiQt(); - // Bitmap Formats - enum BitmapFormat { BitmapBMP, BitmapPG, BitmapPNG, BitmapPPM, BitmapXBM, BitmapX11, nBitmapFormats }; - static BitmapFormat bitmapFormat(const char *name, bool reportError = 0); - static BitmapFormat bitmapFormatFromFilter(const char *s); - static const char *bitmapFormatFilter(BitmapFormat bf); - static const char *bitmapFormatExtension(BitmapFormat bf); // Update Targets enum UpdateTarget { AtomsTarget = 1, CellTarget = 2, ForcefieldsTarget = 4, GlyphsTarget = 8, GridsTarget = 16, ModelsTarget = 32, CanvasTarget = 64, StatusBarTarget = 128, GeometryTarget = 256, VibrationsTarget = 512, SelectTarget = 1024, TrajectoryTarget = 2048, AllTarget = 4095 }; @@ -109,6 +103,7 @@ class GuiQt // Set interactivity (to full or zero), except for main view camera changes void setInteractive(bool interactive); + /* // Refresh Functions */ @@ -133,8 +128,6 @@ class GuiQt void printMessage(const char*); // Save before close bool saveBeforeClose(); - // Save image of current view - bool saveImage(const char *filename, BitmapFormat bf, int width, int height, int quality = 85); /* @@ -147,8 +140,6 @@ class GuiQt AtenForm *mainWindow_; // Main rendering canvas TCanvas *mainCanvas_; - // Original QGLContext, created in initialise() - QGLContext *mainContext_; // Type of application initialised QApplication::Type applicationType_; diff --git a/src/gui/mainmenuactions.cpp b/src/gui/mainmenuactions.cpp index 5fc3d1155..30f71b677 100644 --- a/src/gui/mainmenuactions.cpp +++ b/src/gui/mainmenuactions.cpp @@ -69,13 +69,12 @@ void AtenForm::on_actionFileOpen_triggered(bool checked) // Local save function bool AtenForm::runSaveModelDialog() { - saveModelFilter = NULL; saveModelFilename.clear(); + saveModelFilter = NULL; Tree *filter = NULL; - static QString selectedFilter(aten.filters(FilterData::ModelExport)->item->filter.name()); + static QString selectedFilter(aten.filters(FilterData::ModelExport) == NULL ? NULL : aten.filters(FilterData::ModelExport)->item->filter.name()); static QDir currentDirectory_(aten.workDir()); QString filename = QFileDialog::getSaveFileName(this, "Save Model", currentDirectory_.path(), saveModelFilters, &selectedFilter); - if (!filename.isEmpty()) { // Store path for next use @@ -229,10 +228,10 @@ void AtenForm::on_actionFileSaveImage_triggered(bool checked) ui.setProperty(TreeGuiWidgetEvent::TextProperty, "Save Image Options"); ui.addEdit("geometry", "Image Size", geometry.get(),1,1); group = ui.addRadioGroup("framechoice"); - ui.addRadioButton("noframes", "No Frames", "framechoice", 1, 1,2); - ui.addRadioButton("framemodel", "Frame Current Model", "framechoice", 0, 1,3); - ui.addRadioButton("frameview", "FramDie Whole View", "framechoice", 0, 1,4); - ui.addRadioButton("frameboth", "Frame Current Model and View", "framechoice", 0, 1,5); + ui.addRadioButton("noframes", "No Frames", "framechoice", 1, 1, 2, 1); + ui.addRadioButton("framemodel", "Frame Current Model", "framechoice", 0, 1, 3, 1); + ui.addRadioButton("frameview", "Frame Whole View", "framechoice", 0, 1, 4, 1); + ui.addRadioButton("frameboth", "Frame Current Model and View", "framechoice", 0, 1, 5, 1); // Poke values into dialog widgets and execute ui.setWidgetValue("framechoice", framemodel ? (frameview ? 4 : 2) : (frameview ? 3 : 1) ); @@ -256,7 +255,7 @@ void AtenForm::on_actionFileSaveImage_triggered(bool checked) viewglobe = prefs.viewRotationGlobe(); // Get filename from user - GuiQt::BitmapFormat bf; + RenderEngine::BitmapFormat bf; static QString selectedFilter("Windows Bitmap (*.bmp)"); static QDir currentDirectory_(aten.workDir()); QString filename = QFileDialog::getSaveFileName(this, "Save Bitmap", currentDirectory_.path(), saveBitmapFilters, &selectedFilter); @@ -266,9 +265,9 @@ void AtenForm::on_actionFileSaveImage_triggered(bool checked) currentDirectory_.setPath(filename); // Grab filename extension and search for it Dnchar ext = afterLastChar(qPrintable(filename), '.'); - bf = GuiQt::bitmapFormat(ext.get()); + bf = RenderEngine::bitmapFormat(ext.get()); // If we didn't recognise the extension, complain and quit - if (bf == GuiQt::nBitmapFormats) + if (bf == RenderEngine::nBitmapFormats) { Dnchar message(-1, "Bitmap format not recognised - '%s'.\n", ext.get()); QMessageBox::warning(this, "Aten", message.get(), QMessageBox::Ok); @@ -278,7 +277,7 @@ void AtenForm::on_actionFileSaveImage_triggered(bool checked) prefs.setFrameCurrentModel(framemodel); prefs.setFrameWholeView(frameview); prefs.setViewRotationGlobe(FALSE); - if (!gui.saveImage(qPrintable(filename), bf, width, height, -1)) msg.print("Failed to save image.\n"); + if (!engine().saveImage(qPrintable(filename), bf, width, height, -1)) msg.print("Failed to save image.\n"); prefs.setFrameCurrentModel(currentframemodel); prefs.setFrameWholeView(currentframeview); prefs.setViewRotationGlobe(viewglobe); @@ -450,14 +449,14 @@ void AtenForm::on_actionViewPerspective_triggered(bool checked) { if (!checked) return; prefs.setPerspective(TRUE); - gui.mainCanvas()->postRedisplay(TRUE); + gui.mainCanvas()->postRedisplay(); } // Set orthographic view void AtenForm::on_actionViewOrthographic_triggered(bool checked) { prefs.setPerspective(FALSE); - gui.mainCanvas()->postRedisplay(TRUE); + gui.mainCanvas()->postRedisplay(); } // Set view along cartesian axis supplied @@ -591,7 +590,7 @@ void AtenForm::setActiveSchemeAction(Prefs::ColouringScheme cs) else if (cs == Prefs::CustomScheme) ui.actionSchemeCustom->setChecked(TRUE); prefs.setColourScheme(cs); aten.globalLogChange(Log::Style); - gui.mainCanvas()->postRedisplay(TRUE); + gui.mainCanvas()->postRedisplay(); } // Toggle detection and siaply of hydrogen bonds in models @@ -599,7 +598,7 @@ void AtenForm::on_actionDetectDisplayHBonds_triggered(bool checked) { prefs.setDrawHydrogenBonds(checked); aten.globalLogChange(Log::Style); - gui.mainCanvas()->postRedisplay(TRUE); + gui.mainCanvas()->postRedisplay(); } /* diff --git a/src/gui/mainwindow_init.cpp b/src/gui/mainwindow_init.cpp index 625dfae1e..bf406d53f 100644 --- a/src/gui/mainwindow_init.cpp +++ b/src/gui/mainwindow_init.cpp @@ -253,10 +253,10 @@ void AtenForm::createDialogFilters() // Save image saveBitmapFilters.clear(); - for (n=0; n < GuiQt::nBitmapFormats; n++) + for (n=0; n < RenderEngine::nBitmapFormats; n++) { if (!saveBitmapFilters.isEmpty()) saveBitmapFilters += ";;"; - saveBitmapFilters += GuiQt::bitmapFormatFilter( (GuiQt::BitmapFormat) n); + saveBitmapFilters += RenderEngine::bitmapFormatFilter( (RenderEngine::BitmapFormat) n); } // Save vector diff --git a/src/gui/prefs.h b/src/gui/prefs.h index f4ed592ee..12ce761fa 100644 --- a/src/gui/prefs.h +++ b/src/gui/prefs.h @@ -84,7 +84,7 @@ class AtenPrefs : public QDialog // View Page */ private: - void updateAfterViewPrefs(bool force); + void updateAfterViewPrefs(); void setRadiusChanged(Atom::DrawStyle ds, double value, bool foratom); void spotlightPosChanged(int i, double value); void spotlightColourChanged(Prefs::ColourComponent); @@ -102,7 +102,7 @@ class AtenPrefs : public QDialog void on_AngleLabelFormatEdit_textEdited(const QString &text); void on_DistanceLabelFormatEdit_textEdited(const QString &text); void on_ChargeLabelFormatEdit_textEdited(const QString &text); - void on_LabelSizeSpin_valueChanged(int value); + void on_LabelSizeSpin_valueChanged(double value); void on_RenderDashedAromaticsCheck_clicked(bool checked); void on_DrawHydrogenBondsCheck_clicked(bool checked); void on_HydrogenBondDotRadiusSpin_valueChanged(double value); diff --git a/src/gui/prefs.ui b/src/gui/prefs.ui index 12292883b..6ed516a05 100644 --- a/src/gui/prefs.ui +++ b/src/gui/prefs.ui @@ -5,8 +5,8 @@ 0 0 - 435 - 536 + 476 + 603 @@ -891,7 +891,7 @@ - C_style format string to use for on-screen distances + C-style format string to use for on-screen distances @@ -905,31 +905,21 @@ - C_style format string to use for on-screen angles + C-style format string to use for on-screen angles - Point Size - - - - - - - 1 - - - 200 + Relative Size - C_style format string to use for on-screen distances + C-style format string to use for on-screen distances @@ -940,6 +930,16 @@ + + + + Text size, expressed as a percent of the total view height + + + 0.200000000000000 + + + diff --git a/src/gui/prefs_funcs.cpp b/src/gui/prefs_funcs.cpp index 5c04d12d5..517131ac6 100644 --- a/src/gui/prefs_funcs.cpp +++ b/src/gui/prefs_funcs.cpp @@ -73,6 +73,7 @@ void AtenPrefs::setControls() ui.HydrogenBondDotRadiusSpin->setValue(prefs.hydrogenBondDotRadius()); ui.StickLineNormalWidthSpin->setValue(prefs.stickLineNormalWidth()); ui.StickLineSelectedWidthSpin->setValue(prefs.stickLineSelectedWidth()); + // View Page - Colours Tab ui.ColoursTable->setRowCount(Prefs::nObjectColours); QColor qcol; @@ -86,6 +87,7 @@ void AtenPrefs::setControls() item->setBackgroundColor(qcol); ui.ColoursTable->setItem(n, 1, item); } + // View Page - Rendering / Quality tab ui.SpotlightAmbientColourFrame->setColour(prefs.spotlightColour(Prefs::AmbientComponent)); ui.SpotlightDiffuseColourFrame->setColour(prefs.spotlightColour(Prefs::DiffuseComponent)); @@ -192,7 +194,7 @@ void AtenPrefs::on_PrefsOkButton_clicked(bool checked) // Copy old preferences values back into main structure, update view and close window gui.mainWindow()->updateControls(); aten.globalLogChange(Log::Style); - gui.mainCanvas()->postRedisplay(TRUE,TRUE); + gui.mainCanvas()->postRedisplay(); accept(); } @@ -205,7 +207,7 @@ void AtenPrefs::on_PrefsCancelButton_clicked(bool checked) gui.mainWindow()->updateControls(); aten.globalLogChange(Log::Style); - gui.mainCanvas()->postRedisplay(TRUE,TRUE); + gui.mainCanvas()->postRedisplay(); reject(); } @@ -273,12 +275,12 @@ void AtenPrefs::on_ElementRadiusSpin_valueChanged(double value) // View Page */ -void AtenPrefs::updateAfterViewPrefs(bool force) +void AtenPrefs::updateAfterViewPrefs() { if (refreshing_) return; - gui.mainCanvas()->updatePrimitives(force); + engine().updatePrimitives(); aten.globalLogChange(Log::Style); - gui.mainCanvas()->postRedisplay(TRUE,TRUE); + gui.mainCanvas()->postRedisplay(); } /* @@ -290,7 +292,7 @@ void AtenPrefs::setRadiusChanged(Atom::DrawStyle ds, double value, bool foratom) if (refreshing_) return; if (foratom) prefs.setAtomStyleRadius(ds, value); else prefs.setBondStyleRadius(ds, value); - updateAfterViewPrefs(TRUE); + updateAfterViewPrefs(); } void AtenPrefs::on_StickRadiusSpin_valueChanged(double value) @@ -336,7 +338,7 @@ void AtenPrefs::on_ScaledBondRadiusSpin_valueChanged(double value) void AtenPrefs::on_SelectionScaleSpin_valueChanged(double value) { prefs.setSelectionScale(value); - updateAfterViewPrefs(TRUE); + updateAfterViewPrefs(); } void AtenPrefs::on_AngleLabelFormatEdit_textEdited(const QString &text) @@ -357,7 +359,7 @@ void AtenPrefs::on_ChargeLabelFormatEdit_textEdited(const QString &text) gui.mainCanvas()->postRedisplay(); } -void AtenPrefs::on_LabelSizeSpin_valueChanged(int value) +void AtenPrefs::on_LabelSizeSpin_valueChanged(double value) { prefs.setLabelSize(value); gui.mainCanvas()->postRedisplay(); @@ -431,13 +433,13 @@ void AtenPrefs::on_ColoursTable_cellDoubleClicked(int row, int column) void AtenPrefs::on_PrimitiveQualitySlider_valueChanged(int value) { prefs.setPrimitiveQuality(value); - updateAfterViewPrefs(TRUE); + updateAfterViewPrefs(); } void AtenPrefs::on_PrimitiveQualitySpin_valueChanged(int value) { prefs.setPrimitiveQuality(value); - updateAfterViewPrefs(TRUE); + updateAfterViewPrefs(); } void AtenPrefs::on_ImagePrimitivesGroup_clicked(bool checked) @@ -458,19 +460,19 @@ void AtenPrefs::on_ImagePrimitiveQualitySpin_valueChanged(int value) void AtenPrefs::on_LevelOfDetailNLevelsSpin_valueChanged(int value) { prefs.setLevelsOfDetail(value); - updateAfterViewPrefs(TRUE); + updateAfterViewPrefs(); } void AtenPrefs::on_LevelOfDetailStartZSpin_valueChanged(double value) { prefs.setLevelOfDetailStartZ(value); - updateAfterViewPrefs(TRUE); + updateAfterViewPrefs(); } void AtenPrefs::on_LevelOfDetailWidthSpin_valueChanged(double value) { prefs.setLevelOfDetailWidth(value); - updateAfterViewPrefs(TRUE); + updateAfterViewPrefs(); } void AtenPrefs::on_TransparencyGroup_clicked(bool checked) @@ -483,7 +485,7 @@ void AtenPrefs::on_TransparencyNSlicesSpin_valueChanged(int value) { prefs.setTransparencyNBins(value); if (refreshing_) return; - gui.mainCanvas()->reinitialiseTransparency(); + engine().initialiseTransparency(); gui.mainCanvas()->postRedisplay(); } @@ -491,7 +493,7 @@ void AtenPrefs::on_TransparencyStartZSpin_valueChanged(double value) { prefs.setTransparencyBinStartZ(value); if (refreshing_) return; - gui.mainCanvas()->reinitialiseTransparency(); + engine().initialiseTransparency(); gui.mainCanvas()->postRedisplay(); } @@ -499,7 +501,7 @@ void AtenPrefs::on_TransparencyBinWidthSpin_valueChanged(double value) { prefs.setTransparencyBinWidth(value); if (refreshing_) return; - gui.mainCanvas()->reinitialiseTransparency(); + engine().initialiseTransparency(); gui.mainCanvas()->postRedisplay(); } @@ -524,37 +526,37 @@ void AtenPrefs::on_NearClipSpin_valueChanged(double value) void AtenPrefs::on_NearDepthSpin_valueChanged(int value) { prefs.setDepthNear(value); - updateAfterViewPrefs(FALSE); + updateAfterViewPrefs(); } void AtenPrefs::on_LineAliasingCheck_stateChanged(int state) { prefs.setLineAliasing(state == Qt::Checked); - updateAfterViewPrefs(FALSE); + updateAfterViewPrefs(); } void AtenPrefs::on_PolygonAliasingCheck_stateChanged(int state) { prefs.setPolygonAliasing(state == Qt::Checked); - gui.mainCanvas()->postRedisplay(TRUE); + gui.mainCanvas()->postRedisplay(); } void AtenPrefs::on_MultiSamplingCheck_stateChanged(int state) { prefs.setMultiSampling(state == Qt::Checked); - gui.mainCanvas()->postRedisplay(TRUE); + gui.mainCanvas()->postRedisplay(); } void AtenPrefs::on_SpotlightGroup_clicked(bool checked) { prefs.setSpotlightActive(checked); - gui.mainCanvas()->postRedisplay(TRUE); + gui.mainCanvas()->postRedisplay(); } void AtenPrefs::spotlightPosChanged(int i, double value) { prefs.setSpotlightPosition(i, (GLfloat) value); - gui.mainCanvas()->postRedisplay(TRUE,TRUE); + gui.mainCanvas()->postRedisplay(); } void AtenPrefs::on_SpotlightPositionXSpin_valueChanged(double value) @@ -591,7 +593,7 @@ void AtenPrefs::spotlightColourChanged(Prefs::ColourComponent sc) colframe->setColour(newcol); colframe->update(); // Update display - gui.mainCanvas()->postRedisplay(TRUE); + gui.mainCanvas()->postRedisplay(); } void AtenPrefs::on_SpotlightAmbientColourButton_clicked(bool checked) @@ -995,7 +997,7 @@ void AtenPrefs::ParameterRuleChanged(int id) void AtenPrefs::on_ParameterTable_itemChanged(QTableWidgetItem *w) { - // TGAY Do we need this? + // Do we need this? } /* diff --git a/src/gui/scriptmovie_funcs.cpp b/src/gui/scriptmovie_funcs.cpp index 8363d2207..ad2ca1bbe 100644 --- a/src/gui/scriptmovie_funcs.cpp +++ b/src/gui/scriptmovie_funcs.cpp @@ -67,6 +67,7 @@ void ScriptMovieWidget::on_LoadScriptButton_clicked(bool on) void ScriptMovieWidget::on_SaveScriptButton_clicked(bool on) { + // TODO } void ScriptMovieWidget::on_SaveScriptedMovieButton_clicked(bool on) @@ -137,11 +138,6 @@ void ScriptMovieWidget::on_SaveScriptedMovieButton_clicked(bool on) basename.sprintf("%s%caten-movie-%i-%i-%%09i.png", prefs.tempDir(), PATHSEP, gui.pid(), runid); aten.initialiseImageRedirect(basename, maxframes); - // Temporarily adjust label size... - int oldlabelsize = prefs.labelSize(); - int newlabelsize = int (oldlabelsize*( (1.0*height / gui.mainCanvas()->height()) )); - prefs.setLabelSize(newlabelsize); - int progid = progress.initialise("Saving scripted movie frames...", -1, FALSE); bool canceled = FALSE; ReturnValue rv; @@ -152,9 +148,6 @@ void ScriptMovieWidget::on_SaveScriptedMovieButton_clicked(bool on) prefs.setFrameWholeView(frameview); prefs.setViewRotationGlobe(viewglobe); - // Restore label size - prefs.setLabelSize(oldlabelsize); - // Now run external program to create movie TProcess encoderProcess; // Grab encoder command and replace diff --git a/src/gui/tcanvas.uih b/src/gui/tcanvas.uih index d4d47fac6..3b9bd2ce9 100644 --- a/src/gui/tcanvas.uih +++ b/src/gui/tcanvas.uih @@ -41,17 +41,14 @@ class TCanvas : public QGLWidget // Constructor / Destructor TCanvas(QGLContext *context, QWidget *parent); ~TCanvas(); - // Drawing Targets enum - enum DrawingTarget { NoTarget, ScreenTarget, PixmapTarget }; - + + /* // Character / Setup */ private: // Width, height, and aspect ratio of the canvas GLsizei contextWidth_, contextHeight_; - // Flag to indicate whether we may draw to the canvas - bool valid_; public: // Return the current height of the drawing area @@ -62,49 +59,25 @@ class TCanvas : public QGLWidget void probeFeatures(); - /* - // Render Target - */ - private: - // ID of last trajectory frame displayed (if any) - int displayFrameId_; - // Flag whether to use Aten's current model as rendering source (the default) - bool useCurrentModel_; - // Model to render if we are not using the current model in Aten - Model *renderSource_; - - public: - // Return the current display model - Model *displayModel() const; - // Set the rendering source to the supplied model (reverts to useCurrentModel_ if a NULL pointer is supplied) - void setRenderSource(Model *source); - // Determine target model based on clicked position on TCanvas - Model *modelAt(int x, int y); - - /* // Rendering Functions */ private: - // Flag indicating if we are currently drawing to this canvas - bool drawing_; - // Current drawing target (none, screen, or pixmap) - DrawingTarget drawingTarget_; - // Flag stating whether high-quality primitives should be used for rendering - bool highQuality_; // Time counter for mouse move events QTime mouseMoveCounter_; - // Rendering engine - RenderEngine engine_; - // Flag specifying that stored model pixel data should not be used - bool noPixelData_; - // Flag specifying that the active model should be redrawn regardless of pixeldata availability - bool redrawActiveModel_; + // Flag indicating if we are currently drawing + bool drawing_; + // Primtive set to use for rendering + RenderEngine::PrimitiveSet primitiveSet_; + // Rendering type to pass to RenderEngine + RenderEngine::RenderType renderType_; + // Model icon source for renderType==OffscreenModel (if relevant) + Model *renderIconSource_; protected: // Initialise context widget (when created by Qt) void initializeGL(); - // General repaint callback + // General repaint callbacks void paintGL(); void paintEvent(QPaintEvent *event); // Resize function @@ -119,30 +92,18 @@ class TCanvas : public QGLWidget bool beginGl(); // Finish OpenGL commands void endGl(); - // Check for GL error - void checkGlError(); - // Render 3D objects for current displayModel_ - void render3D(Model *source, bool currentModel); - // Draw 2D objects with supplied QPainter - void render2D(QPainter &painter, Model *source); + // Render whole scene + void renderScene(int width, int height); public: - // Set current drawing target (none, screen, or pixmap) - void setDrawingTarget(DrawingTarget dt); - // Return current drawing target (none, screen, or pixmap) - DrawingTarget drawingTarget(); - // Return whether rendering should use high quality primitives - bool highQuality() const; - // Update primitives (after, e.g., change in preferences) - void updatePrimitives(bool force = FALSE); - // Reinitialise transparency correction - void reinitialiseTransparency(); + // Determine target model based on clicked position on TCanvas + Model *modelAt(int x, int y); + // Request a high-quality rendering pass on next redraw (for image saving, etc.) + void requestHighQuality(); + // Set renderType to pass to RenderEngine::renderScene() + void setRenderType(RenderEngine::RenderType type, Model *iconSource = NULL); // Update Canvas - void postRedisplay(bool noImages = FALSE, bool redrawActive = FALSE); - // Update view matrix stored in RenderEngine - void updateTransformation(Matrix &mat, Vec3 cellcentre); - // Render or grab image - QPixmap generateImage(int w, int h, bool highQuality); + void postRedisplay(); /* @@ -254,7 +215,7 @@ class TCanvas : public QGLWidget // Clears the subsel of atoms void clearPicked(); // End manual picking - void endddManualPick(bool resetaction); + void endManualPick(bool resetaction); // Return start of picked atom list Refitem *pickedAtoms(); diff --git a/src/gui/tcanvas_funcs.cpp b/src/gui/tcanvas_funcs.cpp index 4cdff353a..f66a1afd2 100644 --- a/src/gui/tcanvas_funcs.cpp +++ b/src/gui/tcanvas_funcs.cpp @@ -32,21 +32,13 @@ TCanvas::TCanvas(QGLContext *ctxt, QWidget *parent) : QGLWidget(ctxt, parent) // Character / Setup contextWidth_ = 0; contextHeight_ = 0; - valid_ = FALSE; - - // Render Target - displayFrameId_ = -1; - useCurrentModel_ = TRUE; - renderSource_ = NULL; - redrawActiveModel_ = FALSE; - noPixelData_ = FALSE; // Rendering - drawingTarget_ = TCanvas::NoTarget; - noPixelData_ = FALSE; drawing_ = FALSE; - highQuality_ = FALSE; mouseMoveCounter_.start(); + primitiveSet_ = RenderEngine::LowQuality; + renderType_ = RenderEngine::OnscreenScene; + renderIconSource_ = NULL; // Atom Selection atomClicked_ = NULL; @@ -117,28 +109,6 @@ void TCanvas::probeFeatures() // Render Target */ -// Return the current display model -Model *TCanvas::displayModel() const -{ - Model *source = (useCurrentModel_ ? aten.currentModelOrFrame() : renderSource_); - return source; -} - -// Set the rendering source to the supplied model (reverts to useCurrentModel_ if a NULL pointer is supplied) -void TCanvas::setRenderSource(Model *source) -{ - if (source == NULL) - { - useCurrentModel_ = TRUE; - renderSource_ = NULL; - } - else - { - useCurrentModel_ = FALSE; - renderSource_ = source; - } -} - // Determine target model based on clicked position on TCanvas Model *TCanvas::modelAt(int x, int y) { @@ -159,6 +129,19 @@ Model *TCanvas::modelAt(int x, int y) return (id >= aten.nVisibleModels() ? aten.currentModel() : aten.visibleModel(id)); } +// Request a high-quality rendering pass on next redraw (for image saving, etc.) +void TCanvas::requestHighQuality() +{ + primitiveSet_ = RenderEngine::HighQuality; +} + +// Set renderType to pass to RenderEngine::renderScene() +void TCanvas::setRenderType(RenderEngine::RenderType type, Model *iconSource) +{ + renderType_ = type; + renderIconSource_ = iconSource; +} + /* // Rendering Functions */ @@ -168,266 +151,27 @@ void TCanvas::initializeGL() { // Initialize GL msg.enter("TCanvas::initializeGL"); - valid_ = TRUE; - // Check VBO capability, if requested - // There are some things to consider here, namely that we can *only* use and create VBOs on the mainWidget's context. - // The major reason it that, since QGLWidget::renderPixmap() seems to fail regularly when reusing the main context (and - // which cannot be done at all on Windows platforms) we either have to regenerate and manage a secondary set of - // VBOs when rendering to offscreen bitmaps, or simply not use VBOs when rendering offscreen bitmaps. - // The latter is simpler, so this function and the main rendering call take note of which context is being used. - static int count = 0; - if ((count == 0) && (prefs.instanceType() == PrimitiveInstance::VBOInstance)) + // Push primitive instance for the TCanvas *if* we are not using pixelbuffers + if (!prefs.usePixelBuffers()) { - // Get Extensions string and check for presence of 'GL_ARB_vertex_buffer_object' - const GLubyte *glexts = NULL; - glexts = glGetString( GL_EXTENSIONS ); - msg.print(Messenger::Verbose, "Available GL Extensions are : %s\n", glexts); - if (strstr( (const char *) glexts, "GL_ARB_vertex_buffer_object") == 0) - { - printf("Error: VBOs requested but the extension is not available. Falling back to display lists.\n"); - prefs.setInstanceType(PrimitiveInstance::ListInstance); - } - else - { - // Store VBO function pointers (Windows only) - #ifdef _WIN32 - Primitive::glGenBuffers = (PFNGLGENBUFFERSPROC) wglGetProcAddress("glGenBuffers"); - Primitive::glBindBuffer = (PFNGLBINDBUFFERPROC) wglGetProcAddress("glBindBuffer"); - Primitive::glBufferData = (PFNGLBUFFERDATAPROC) wglGetProcAddress("glBufferData"); - Primitive::glDeleteBuffers = (PFNGLDELETEBUFFERSPROC) wglGetProcAddress("glDeleteBuffers"); - if ((Primitive::glGenBuffers == NULL) || (Primitive::glBindBuffer == NULL) || (Primitive::glBufferData == NULL) || (Primitive::glDeleteBuffers == NULL)) - { - printf("Error: VBOs requested but the relevant procedures could not be located. Falling back to display lists.\n"); - prefs.setInstanceType(PrimitiveInstance::ListInstance); - } - #endif - } - ++count; + engine().pushInstance(primitiveSet_, context()); +// engine().pushInstance(RenderEngine::HighQuality, context()); } - // Push a primitives instance in the rendering engine - engine_.pushInstance(highQuality_, context()); - msg.exit("TCanvas::initializeGL"); } void TCanvas::paintGL() { - if (drawingTarget_ == TCanvas::PixmapTarget) paintEvent(NULL); + renderScene(contextWidth_, contextHeight_); } // General repaint callback void TCanvas::paintEvent(QPaintEvent *event) { - msg.enter("TCanvas::paintGL"); - QColor color; - QRect currentBox; - Refitem *first, localri; - int px, py, nperrow = prefs.nModelsPerRow(), nrows, col, row, nmodels; - bool usepixels, modelIsCurrentModel; - Model *m; - // Do nothing if the canvas is not valid, or we are still drawing from last time. - if ((!valid_) || drawing_ || (drawingTarget_ == TCanvas::NoTarget) || (aten.currentModel() == NULL)) - { - msg.exit("TCanvas::paintGL"); - return; - } - - // Setup basic GL stuff - engine_.initialiseGL(); - - // Note: An internet source suggests that the QPainter documentation is incomplete, and that - // all OpenGL calls should be made after the QPainter is constructed, and before the QPainter - // is destroyed. However, this results in mangled graphics on the Linux (and other?) versions, - // so here it is done in the 'wrong' order. - - // Set the first item to consider - if we are rendering from a specific model (useCurrentModel_ == FALSE) use the local listitem - if (useCurrentModel_) - { - nmodels = aten.nVisibleModels(); - if (nmodels == 0) - { - localri.item = aten.currentModel(); - nmodels = 1; - first = &localri; - } - else first = aten.visibleModels(); - } - else - { - // Quick check for NULL pointer - if (renderSource_ == NULL) - { - msg.exit("TCanvas::paintGL"); - return; - } - localri.item = renderSource_; - first = &localri; - nmodels = 1; - } - if (first == NULL) - { - msg.exit("TCanvas::paintGL"); - return; - } - - // Begin the GL commands - if (!beginGl()) - { - msg.print(Messenger::GL, " --> RENDERING END (BAD BEGIN)\n"); - msg.exit("TCanvas::paintGL"); - return; - } - - - // Clear view - msg.print(Messenger::GL, " --> Clearing context, background, and setting pen colour\n"); - glViewport(0,0,contextWidth_,contextHeight_); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - engine_.forgetTextPrimitives(); - - // Set up some useful values - nrows = nmodels/nperrow + (nmodels%nperrow == 0 ? 0 : 1); - py = contextHeight_ / nrows; - px = (nmodels == 1 ? contextWidth_ : contextWidth_ / nperrow); - - // Loop over model refitems in list (or single refitem) - col = 0; - row = 0; - for (Refitem *ri = first; ri != NULL; ri = ri->next) - { - // Grab model pointer - m = ri->item; - if (m == NULL) continue; - - // Store coordinates for box if this is the current model - if ((m == aten.currentModel()) && useCurrentModel_) - { - modelIsCurrentModel = TRUE; - currentBox.setRect(col*px, row*py, px, py); - } - else modelIsCurrentModel = FALSE; - - // Vibration frame? - m = m->renderSourceModel(); - if (m->renderFromVibration() && (m->vibrationCurrentFrame() != NULL)) m = m->vibrationCurrentFrame(); - - // If the stored model pixel data is out of date or rerendering has specifically been requested, redraw the model - usepixels = TRUE; -// printf("For Model %s, global noPixelData_ and redrawActive flags are %i and %i, logpoint is %i, and pixel data validity is %i\n", ri->item->name(), noPixelData_, redrawActiveModel_, m->changeLog.log(Log::Total), m->pixelDataIsValid(px,py,m,m->changeLog.log(Log::Total))); - if (redrawActiveModel_ && (ri->item == aten.currentModel())) usepixels = FALSE; - else if (noPixelData_) usepixels = FALSE; - else if (!m->pixelDataIsValid(px,py,m,m->changeLog.log(Log::Total))) usepixels = FALSE; - - if (usepixels) - { - // Setup flat projection for pixel rendering - glViewport(0,0,contextWidth_,contextHeight_); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0.0, (double)contextWidth_, 0.0, (double)contextHeight_, -1.0, 1.0); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glRasterPos2i(col*px, contextHeight_-(row+1)*py); - glDrawPixels(px, py, GL_RGBA, GL_UNSIGNED_BYTE, m->pixelData()); - } - else - { - // Determine desired pixel range and set up view(port) - checkGlError(); - m->setupView(col*px, contextHeight_-(row+1)*py, px, py); - - // Render the 3D parts of the model - render3D(m, modelIsCurrentModel); - } - - // Increase counters - ++col; - if (col%nperrow == 0) - { - col = 0; - ++row; - } - } - endGl(); - - // Start of QPainter code - static QFont font; - font.setPointSize(prefs.labelSize()); - QBrush nobrush(Qt::NoBrush); - QPen pen; - QPainter painter(this); - - // Render text elements for all models (with QPainter) - painter.setFont(font); - painter.setRenderHint(QPainter::Antialiasing); - engine_.renderText(painter, this); - - // Render 2D mode embellishments for current model (with QPainter) - m = useCurrentModel_ ? aten.currentModel() : renderSource_; - if (m != NULL) - { - m = m->renderFromVibration() ? m->vibrationCurrentFrame() : m->renderSourceModel(); - render2D(painter, m); - } - - // Draw box around current model - color.setRgbF(0.0,0.0,0.0,1.0); - pen.setColor(color); - pen.setWidth(2); - painter.setBrush(nobrush); - painter.setPen(Qt::SolidLine); - painter.setPen(pen); -// printf("CurrentBox = %i %i %i %i\n", currentBox.x(), currentBox.y(), currentBox.width(), currentBox.height()); - - if (prefs.frameCurrentModel()) painter.drawRect(currentBox); - if (prefs.frameWholeView()) - { - currentBox.setRect(0,0,contextWidth_,contextHeight_); - painter.drawRect(currentBox); - } - painter.end(); - - // Store pixel data for models that need it - col = 0; - row = 0; - glReadBuffer(GL_BACK); - glViewport(0,0,contextWidth_,contextHeight_); - for (Refitem *ri = first; ri != NULL; ri = ri->next) - { - // Grab model pointer - m = ri->item; - if (m == NULL) continue; - - // Vibration frame? - m = m->renderSourceModel(); - if (m->renderFromVibration() && (m->vibrationCurrentFrame() != NULL)) m = m->vibrationCurrentFrame(); - - // If the stored model pixel data is out of date re-copy pixel data -// if (!m->pixelDataIsValid(px,py,m,m->changeLog.log(Log::Total))) -// { -// m->preparePixelData(px,py,m,m->changeLog.log(Log::Total)); -// glReadPixels(col*px, contextHeight_-(row+1)*py, px, py, GL_RGBA, GL_UNSIGNED_BYTE, m->pixelData()); -// } - - // Increase counters - ++col; - if (col%nperrow == 0) - { - col = 0; - ++row; - } - } - - // Swap buffers if necessary - if (prefs.manualSwapBuffers()) swapBuffers(); - - // Special case when rendering to Pixmap - must delete associated context - if (drawingTarget_ == TCanvas::PixmapTarget) engine_.popInstance(highQuality_, context()); - - msg.exit("TCanvas::paintGL"); + renderScene(contextWidth_, contextHeight_); } // Resize function @@ -441,7 +185,6 @@ void TCanvas::resizeGL(int newwidth, int newheight) // Begin GL bool TCanvas::beginGl() { - if (!valid_) return FALSE; drawing_ = TRUE; return TRUE; } @@ -452,119 +195,13 @@ void TCanvas::endGl() drawing_ = FALSE; } -void TCanvas::checkGlError() -{ - // Do GL error check - if (msg.isOutputActive(Messenger::GL)) - { - GLenum glerr = GL_NO_ERROR; - do - { - switch (glGetError()) - { - case (GL_INVALID_ENUM): msg.print(Messenger::GL, "GLenum argument out of range\n"); break; - case (GL_INVALID_VALUE): msg.print(Messenger::GL, "Numeric argument out of range\n"); break; - case (GL_INVALID_OPERATION): msg.print(Messenger::GL, "Operation illegal in current state\n"); break; - case (GL_STACK_OVERFLOW): msg.print(Messenger::GL, "Command would cause a stack overflow\n"); break; - case (GL_STACK_UNDERFLOW): msg.print(Messenger::GL, "Command would cause a stack underflow\n"); break; - case (GL_OUT_OF_MEMORY): msg.print(Messenger::GL, "Not enough memory left to execute command\n"); break; - case (GL_NO_ERROR): msg.print(Messenger::GL, "No GL error\n"); break; - default: - msg.print(Messenger::GL, "Unknown GL error?\n"); - break; - } - } while (glerr != GL_NO_ERROR); - } -} - -// Set current drawing target (none, screen, or pixmap) -void TCanvas::setDrawingTarget(TCanvas::DrawingTarget dt) -{ - drawingTarget_ = dt; -} - -// Return current drawing target (none, screen, or pixmap) -TCanvas::DrawingTarget TCanvas::drawingTarget() -{ - return drawingTarget_; -} - -// Return whether rendering should use high quality primitives -bool TCanvas::highQuality() const -{ - return highQuality_; -} - // Refresh widget -void TCanvas::postRedisplay(bool noImages, bool redrawActive) +void TCanvas::postRedisplay() { - if ((!valid_) || drawing_) return; - noPixelData_ = noImages; - redrawActiveModel_ = redrawActive; + if (drawing_) return; update(); } -// Update view matrix stored in RenderEngine -void TCanvas::updateTransformation(Matrix& mat, Vec3 cellcentre) -{ - engine_.setTransformationMatrix(mat, cellcentre); -} - -// Render or grab image -QPixmap TCanvas::generateImage(int w, int h, bool highQuality) -{ - // Store current quality value - bool oldquality = highQuality_; - highQuality_ = highQuality; - if (prefs.useFrameBuffer() == FALSE) - { - // Set some flags so that the main view is redrawn properly, clearing lists and preventing image use - noPixelData_ = TRUE; - engine_.flagClearLists(); - - // Specify that we are about to render offscreen (so instance can be automatically removed) - TCanvas::DrawingTarget oldTarget = drawingTarget_; - drawingTarget_ = TCanvas::PixmapTarget; - - // Generate offscreen bitmap (a temporary context will be created) - QPixmap pixmap = renderPixmap(w, h, FALSE); - - // Return to last rendering mode, and flag for rendering list regeneration again - drawingTarget_ = oldTarget; - engine_.flagClearLists(); - - // Ensure correct widget context size is stored - contextWidth_ = (GLsizei) width(); - contextHeight_ = (GLsizei) height(); - // Revert to old quality setting - highQuality_ = oldquality; - return pixmap; - } - else - { - drawingTarget_ = TCanvas::PixmapTarget; - noPixelData_ = TRUE; - engine_.flagClearLists(); - if (highQuality != oldquality) postRedisplay(); - QImage image = grabFrameBuffer(); - // Revert to old quality setting - highQuality_ = oldquality; - return QPixmap::fromImage(image); - } -} - -// Reinitialise primitives -void TCanvas::updatePrimitives(bool force) -{ - engine_.updatePrimitives(context(), force); -} - -// Reinitialise transparency correction -void TCanvas::reinitialiseTransparency() -{ - engine_.initialiseTransparency(); -} - /* // Other Qt Virtuals */ diff --git a/src/gui/tcanvas_input.cpp b/src/gui/tcanvas_input.cpp index 789d3979f..a0e2786cd 100644 --- a/src/gui/tcanvas_input.cpp +++ b/src/gui/tcanvas_input.cpp @@ -69,7 +69,7 @@ void TCanvas::mousePressEvent(QMouseEvent *event) } // Get current active model - Model *source = displayModel(); + Model *source = aten.currentModel(); if (source == NULL) { printf("Pointless TCanvas::mousePressEvent - no source model.\n"); @@ -134,7 +134,7 @@ void TCanvas::mouseReleaseEvent(QMouseEvent *event) } atomClicked_ = NULL; - postRedisplay(FALSE,TRUE); + postRedisplay(); msg.exit("TCanvas::mouseReleaseEvent"); } @@ -145,7 +145,7 @@ void TCanvas::mouseMoveEvent(QMouseEvent *event) static Vec3 delta; // Get current active model - Model *source = displayModel(); + Model *source = aten.currentModel(); if (source == NULL) { printf("Pointless TCanvas::mouseMoveEvent - no source model.\n"); @@ -181,7 +181,7 @@ void TCanvas::mouseMoveEvent(QMouseEvent *event) { if (atomClicked_ == NULL) gui.fragmentsWidget->currentFragment()->rotateOrientedModel(delta.x/2.0,delta.y/2.0); else gui.fragmentsWidget->currentFragment()->rotateAnchoredModel(delta.x, delta.y); - postRedisplay(FALSE,TRUE); + postRedisplay(); } break; case (UserAction::TransformRotateXYAction): @@ -209,7 +209,7 @@ void TCanvas::mouseMoveEvent(QMouseEvent *event) if (mouseMoveCounter_.elapsed() > prefs.mouseMoveFilter()) { mouseMoveCounter_.start(); - postRedisplay(FALSE,TRUE); + postRedisplay(); } } rMouseLast_.set(event->x(), event->y(), 0.0); @@ -222,7 +222,7 @@ void TCanvas::wheelEvent(QWheelEvent *event) msg.enter("TCanvas::modeScroll"); // Get current active model - Model *source = displayModel(); + Model *source = aten.currentModel(); if (source == NULL) { printf("Pointless TCanvas::modeScroll - no source model.\n"); @@ -297,7 +297,7 @@ void TCanvas::keyPressEvent(QKeyEvent *event) keyModifier_[Prefs::AltKey] = km&Qt::AltModifier; // Get current active model - Model *source = displayModel(); + Model *source = aten.currentModel(); if (source == NULL) { printf("Pointless TCanvas::keyPressEvent - no source model.\n"); @@ -405,7 +405,7 @@ void TCanvas::keyPressEvent(QKeyEvent *event) break; } // Update display if necessary - if (refresh) postRedisplay(FALSE,TRUE); + if (refresh) postRedisplay(); if (ignore) event->ignore(); } @@ -422,7 +422,7 @@ void TCanvas::keyReleaseEvent(QKeyEvent *event) keyModifier_[Prefs::AltKey] = km&Qt::AltModifier; // Get current active model - Model *source = displayModel(); + Model *source = aten.currentModel(); if (source == NULL) { printf("Pointless TCanvas::keyReleaseEvent - no source model.\n"); @@ -471,7 +471,7 @@ void TCanvas::setSelectedMode(UserAction::Action ua, int atomsToPick, void (*cal msg.enter("TCanvas::setSelectedMode"); // Get current active model - Model *source = displayModel(); + Model *source = aten.currentModel(); if (source == NULL) { printf("Pointless TCanvas::setSelectedMode - no source model.\n"); @@ -598,7 +598,7 @@ void TCanvas::beginMode(Prefs::MouseButton button) // the signal, current selection / draw modes and key modifier states. // Get current active model - Model *source = displayModel(); + Model *source = aten.currentModel(); if (source == NULL) { printf("Pointless TCanvas::beginMode - no source model.\n"); @@ -706,7 +706,7 @@ void TCanvas::endMode(Prefs::MouseButton button) Fragment *frag; // Get current active model - Model *source = displayModel(); + Model *source = aten.currentModel(); if (source == NULL) { printf("Pointless TCanvas::endMode - no source model.\n"); diff --git a/src/gui/tcanvas_render.cpp b/src/gui/tcanvas_render.cpp index ce25609a6..185b36b2c 100644 --- a/src/gui/tcanvas_render.cpp +++ b/src/gui/tcanvas_render.cpp @@ -20,119 +20,54 @@ */ #include "gui/tcanvas.uih" -#include "model/model.h" +#include "main/aten.h" #include "base/sysfunc.h" -// Draw 2D objects with QPainter -void TCanvas::render2D(QPainter &painter, Model *source) +// Perform main rendering +void TCanvas::renderScene(int width, int height) { - msg.enter("TCanvas::render2D"); - // Variables - static Dnchar text; - QColor color; - QBrush solidbrush(Qt::SolidPattern), nobrush(Qt::NoBrush); - GLfloat colour[4]; - Vec4 screenr; - Vec3 r; - int i, skip, n; - double dx, halfw; - - // Text Primitives - prefs.copyColour(Prefs::TextColour, colour); - color.setRgbF(colour[0], colour[1], colour[2], colour[3]); - solidbrush.setColor(color); - painter.setBrush(solidbrush); - painter.setPen(Qt::SolidLine); - painter.setPen(color); - engine_.renderText(painter, this); - - // Active mode embellishments - prefs.copyColour(Prefs::BackgroundColour, colour); - color.setRgbF(1.0-colour[0], 1.0-colour[1], 1.0-colour[2], 1.0); - painter.setPen(color); - painter.setPen(Qt::DashLine); - painter.setBrush(nobrush); - switch (activeMode_) + // Begin the GL commands + if (!beginGl()) { - case (UserAction::NoAction): - break; - // Only selection mode where we draw a selection box - case (UserAction::SelectAction): - painter.drawRect(rMouseDown_.x, rMouseDown_.y, rMouseLast_.x-rMouseDown_.x, rMouseLast_.y-rMouseDown_.y); - break; - default: - break; + msg.print(Messenger::GL, " --> RENDERING END (BAD BEGIN)\n"); + msg.exit("TCanvas::renderScene"); + return; } - // Passive mode embellishments - switch (selectedMode_) + // If we're rendering offscreen, store the current context size so we can restore it afterwards + int oldWidth, oldHeight; + if (renderType_ != RenderEngine::OnscreenScene) { - // Draw on distance ruler for drawing modes - case (UserAction::DrawAtomAction): - case (UserAction::DrawChainAction): - // Get pixel 'length' in Angstrom terms at current draw depth - r = source->screenToModel(contextWidth_/2+10, contextHeight_/2, currentDrawDepth_); - r -= source->screenToModel(contextWidth_/2, contextHeight_/2, currentDrawDepth_); - dx = 10.0 / r.magnitude(); - - halfw = contextWidth_ / 2.0; - i = int( halfw / dx); - skip = 1; - while ( (i/skip) > 5) - { - skip += (skip == 1 ? 4 : 5); - } - for (n = -i; n <= i; n ++) - { - if ((n%skip) != 0) continue; - painter.drawLine(halfw + n*dx, 20, halfw + n*dx, 10); - painter.drawLine(halfw + n*dx, contextHeight_-20, halfw + n*dx, contextHeight_-10); - if (n != i) - { - painter.drawLine(halfw + (n+0.5*skip)*dx, contextHeight_-15, halfw + (n+0.5*skip)*dx, contextHeight_-10); - painter.drawLine(halfw + (n+0.5*skip)*dx, contextHeight_-15, halfw + (n+0.5*skip)*dx, contextHeight_-10); - } - } - painter.drawLine(halfw - i*dx, 10, halfw + i*dx, 10); - painter.drawLine(halfw - i*dx, contextHeight_-10, halfw + i*dx, contextHeight_-10); - for (n = -i; n <= i; n++) - { - if ((n%skip) != 0) continue; - renderText(halfw + n*dx - (n < 0 ? 8 : 3), contextHeight_, itoa(n)); - } - break; - default: - break; + oldWidth = contextWidth_; + oldHeight = contextHeight_; } - msg.exit("TCanvas::render2D"); -} -// Render 3D objects for current displayModel_ -void TCanvas::render3D(Model *source, bool currentModel) -{ - // Valid pointer set? - if (source == NULL) return; - msg.enter("TCanvas::render3D"); + // Render model data - pass this TCanvas' QGLContext pointer so we can check it against the one owned by RenderEngine. + engine().renderScene(primitiveSet_, width, height, context(), renderType_, renderIconSource_); + + // Swap buffers if necessary + if (prefs.manualSwapBuffers()) swapBuffers(); - // Render model - msg.print(Messenger::GL, " --> RENDERING BEGIN : source model pointer = %p, renderpoint = %d\n", source, source->changeLog.log(Log::Total)); + // Pop primitive instance if we were using the TCanvas to render a high-quality offscreen pixmap + if ((primitiveSet_ == RenderEngine::HighQuality) && (!prefs.usePixelBuffers())) + { + msg.print(Messenger::Verbose, "GUI rendering offscreen, so popping instance created earlier.\n"); + engine().popInstance(RenderEngine::HighQuality, context()); + } + + // Always revert to lower quality for next pass + primitiveSet_ = RenderEngine::LowQuality; - // If this is a trajectory frame, check its ID against the last one rendered - if (source->parent() != NULL) + // If we're rendering offscreen, restore the old context size + if (renderType_ != RenderEngine::OnscreenScene) { - displayFrameId_ = source->parent()->trajectoryFrameIndex(); - msg.print(Messenger::GL, " --> Source model is a trajectory frame - index = %i\n", displayFrameId_); + contextWidth_ = oldWidth; + contextHeight_ = oldHeight; } - - // Render 3D elements (with OpenGL) - checkGlError(); - if (selectedMode_ == UserAction::DrawFragmentAction) engine_.flagClearLists(); - engine_.render3D(highQuality_, source, this, currentModel); - //glFlush(); - checkGlError(); + + endGl(); - msg.print(Messenger::GL, " --> RENDERING END\n"); - msg.exit("TCanvas::render3D"); + msg.exit("TCanvas::renderScene"); } // Attempt to detect/check for corrupt rendering @@ -169,7 +104,7 @@ void TCanvas::isRenderingOk() if (!result) { prefs.setManualSwapBuffers(TRUE); - QMessageBox::information(NULL, "Rendering Check", "Aten detected that it's rendering was producing corrupt images. To attempt to remedy this, manual buffer swapping has been activated. If the main rendering canvas now displays everything correctly, you can add the line 'aten.prefs.manualswapbuffers = TRUE;' to your personal '.aten/user.dat' file in your home directory."); + QMessageBox::information(NULL, "Rendering Check", "Aten detected that it's rendering was producing corrupt images. To attempt to remedy this, manual buffer swapping has been activated. If the main rendering canvas now displays everything correctly, you can add the line 'aten.prefs.manualSwapBuffers = TRUE;' to your personal '.aten/user.txt' file in your home directory."); } // Set the clear colour back to the user-defined value diff --git a/src/gui/toolbaractions.cpp b/src/gui/toolbaractions.cpp index 999a09283..7f6e9c879 100644 --- a/src/gui/toolbaractions.cpp +++ b/src/gui/toolbaractions.cpp @@ -73,7 +73,7 @@ void AtenForm::setActiveStyleAction(Atom::DrawStyle ds) else if (ds == Atom::ScaledStyle) ui.actionStyleScaled->setChecked(TRUE); else if (ds == Atom::IndividualStyle) ui.actionStyleIndividual->setChecked(TRUE); prefs.setRenderStyle(ds); - gui.mainCanvas()->postRedisplay(TRUE); + gui.mainCanvas()->postRedisplay(); } // Enter basic atom selection mode diff --git a/src/main/version.h b/src/main/version.h index 7aeb53e21..359681fe2 100644 --- a/src/main/version.h +++ b/src/main/version.h @@ -22,8 +22,8 @@ #ifndef ATEN_VERSION_H #define ATEN_VERSION_H -#define ATENVERSION "1.828" -#define ATENREVISION "1828" +#define ATENVERSION "1.829" +#define ATENREVISION "1829" #define ATENDATE "Thu 28 Jun - 13:36" #define ATENURL "http://aten.googlecode.com/svn/trunk" diff --git a/src/model/model.cpp b/src/model/model.cpp index 682adaa06..c91454d54 100644 --- a/src/model/model.cpp +++ b/src/model/model.cpp @@ -43,10 +43,6 @@ Model::Model() // Camera / View / render modelViewMatrix_.setIdentity(); modelViewMatrix_[14] = -10.0; - pixelData_ = NULL; - pixelDataLogPoint_ = -1; - pixelDataWidth_ = -1; - pixelDataHeight_ = -1; renderSource_ = Model::ModelSource; renderFromVibration_ = FALSE; @@ -118,7 +114,6 @@ Model::~Model() distanceMeasurements_.clear(); angleMeasurements_.clear(); torsionMeasurements_.clear(); - if (pixelData_ != NULL) delete[] pixelData_; } // Sets the filename of the model @@ -214,15 +209,13 @@ void Model::regenerateIcon() prefs.setFrameCurrentModel(FALSE); prefs.setFrameWholeView(FALSE); prefs.setViewRotationGlobe(FALSE); - gui.mainCanvas()->setRenderSource(this); changeLog.add(Log::Style); - icon_ = gui.mainCanvas()->generateImage(100, 100, FALSE); + icon_ = engine().renderModelIcon(this); prefs.setFrameCurrentModel(framemodel); prefs.setFrameWholeView(frameview); prefs.setViewRotationGlobe(viewglobe); - gui.mainCanvas()->setRenderSource(NULL); msg.exit("Model::regenerateIcon"); } diff --git a/src/model/model.h b/src/model/model.h index f05891d8e..c91a7096d 100644 --- a/src/model/model.h +++ b/src/model/model.h @@ -856,14 +856,6 @@ class Model // Rendering Source */ private: - // Stored pixel data from last render - GLubyte *pixelData_; - // Model source of stored pixel data - Model *pixelDataSource_; - // Logpoint of last stored pixel data - int pixelDataLogPoint_; - // Width and height of stored pixel data - int pixelDataWidth_, pixelDataHeight_; // Where to get render source data RenderSource renderSource_; // Flags whether to draw from associated vibration instead of model @@ -880,12 +872,6 @@ class Model void setRenderFromVibration(bool b); // Return whether to render from vibration frames bool renderFromVibration(); - // Return whether stored pixel data is valid - bool pixelDataIsValid(int currentwidth, int currentheight, Model *source, int logpoint); - // Prepare pixel data buffer - void preparePixelData(int width, int height, Model *source, int logpoint); - // Return pixel data buffer pointer - GLubyte *pixelData(); /* diff --git a/src/model/trajectory.cpp b/src/model/trajectory.cpp index 59465dc73..cfaf33971 100644 --- a/src/model/trajectory.cpp +++ b/src/model/trajectory.cpp @@ -439,8 +439,6 @@ void Model::seekTrajectoryFrame(int frameno, bool quiet) } trajectoryCurrentFrame_->changeLog.add(Log::Camera); trajectoryCurrentFrame_->changeLog.add(Log::Structure); - // Reset log flag for pixeldata so we redraw every frame change - pixelDataLogPoint_ = -1; } trajectoryFrameIndex_ = frameno; if (!quiet) msg.print("Seek to frame %i\n", trajectoryFrameIndex_+1); diff --git a/src/model/view.cpp b/src/model/view.cpp index 77cf8ad0e..6118b2d65 100644 --- a/src/model/view.cpp +++ b/src/model/view.cpp @@ -62,53 +62,6 @@ bool Model::renderFromVibration() return renderFromVibration_; } -// Return whether stored pixel data is valid -bool Model::pixelDataIsValid(int currentwidth, int currentheight, Model *source, int logpoint) -{ - if (parent_ != NULL) return parent_->pixelDataIsValid(currentwidth, currentheight, source, logpoint); - else - { -// printf("Model [%s] : data = %p, wxh = %ix%i, new wxh = %ix%i, source = %p, newsource = %p, lp = %i, newlp = %i\n", name_.get(), pixelData_, pixelDataWidth_, pixelDataHeight_, currentwidth, currentheight, pixelDataSource_, source, pixelDataLogPoint_, logpoint); - // First, check for presence of array data - if (pixelData_ == NULL) return FALSE; - // Second, check width and height - if ((currentwidth != pixelDataWidth_) || (currentheight != pixelDataHeight_)) return FALSE; - // Third, check model source - if (pixelDataSource_ != source) return FALSE; - // Lastly, check logpoint - return (pixelDataLogPoint_ == logpoint); - } -} - -// Prepare pixel data buffer -void Model::preparePixelData(int width, int height, Model *source, int logpoint) -{ - // Forward to parent model if one is defined - if (parent_ != NULL) parent_->preparePixelData(width, height, source, logpoint); - else - { - // Reallocate array if necessary - if ((width != pixelDataWidth_) || (height != pixelDataHeight_)) - { - // Deallocate old array if necessary - if (pixelData_ != NULL) delete[] pixelData_; - pixelDataWidth_ = width; - pixelDataHeight_ = height; - pixelData_ = new GLubyte[4*pixelDataWidth_*pixelDataHeight_]; - } - // Set new log point and source model - pixelDataSource_ = source; - pixelDataLogPoint_ = logpoint; - } -} - -// Return pixel data buffer pointer -GLubyte *Model::pixelData() -{ - if (parent_ != NULL) return parent_->pixelData(); - else return pixelData_; -} - // Set the current modelview matrix void Model::setModelViewMatrix(Matrix &rmat) { diff --git a/src/parser/prefs.cpp b/src/parser/prefs.cpp index 7271f8fd8..cb48fd71d 100644 --- a/src/parser/prefs.cpp +++ b/src/parser/prefs.cpp @@ -97,7 +97,7 @@ Accessor PreferencesVariable::accessorData[PreferencesVariable::nAccessors] = { { "hDistance", VTypes::DoubleData, 0, FALSE }, { "imageQuality", VTypes::IntegerData, 0, FALSE }, { "keyAction", VTypes::StringData, Prefs::nModifierKeys, FALSE }, - { "labelSize", VTypes::IntegerData, 0, FALSE }, + { "labelSize", VTypes::DoubleData, 0, FALSE }, { "levelOfDetailStartZ", VTypes::DoubleData, 0, FALSE }, { "levelOfDetailWidth", VTypes::DoubleData, 0, FALSE }, { "levelsOfDetail", VTypes::IntegerData, 0, FALSE }, @@ -141,8 +141,7 @@ Accessor PreferencesVariable::accessorData[PreferencesVariable::nAccessors] = { { "transparentSelection", VTypes::IntegerData, 0, FALSE }, { "unitCellAxesColour", VTypes::DoubleData, 4, FALSE }, { "unitCellColour", VTypes::DoubleData, 4, FALSE }, - { "useFrameBuffer", VTypes::IntegerData, 0, FALSE }, - { "useNiceText", VTypes::IntegerData, 0, FALSE }, + { "usePixelBuffers", VTypes::IntegerData, 0, FALSE }, { "vdwCutoff", VTypes::DoubleData, 0, FALSE }, { "vibrationArrowColour", VTypes::DoubleData, 4, FALSE }, { "viewRotationGlobe", VTypes::IntegerData, 0, FALSE }, @@ -552,11 +551,8 @@ bool PreferencesVariable::retrieveAccessor(int i, ReturnValue &rv, bool hasArray if (hasArrayIndex) rv.set( ptr->colour(Prefs::UnitCellColour)[arrayIndex-1] ); else rv.setArray( VTypes::DoubleData, ptr->colour(Prefs::UnitCellColour), 4); break; - case (PreferencesVariable::UseFrameBuffer): - rv.set( ptr->useFrameBuffer() ); - break; - case (PreferencesVariable::UseNiceText): - rv.set( ptr->useNiceText() ); + case (PreferencesVariable::UsePixelBuffers): + rv.set( ptr->usePixelBuffers() ); break; case (PreferencesVariable::VdwCutoff): rv.set( ptr->vdwCutoff() ); @@ -683,7 +679,7 @@ bool PreferencesVariable::setAccessor(int i, ReturnValue &sourcerv, ReturnValue if (newvalue.arraySize() == Atom::nDrawStyles) for (n=0; nsetAtomStyleRadius( (Atom::DrawStyle) n, newvalue.asDouble(n, result)); else if (hasArrayIndex) ptr->setAtomStyleRadius( (Atom::DrawStyle) (arrayIndex-1), newvalue.asDouble(result)); else for (n=0; nsetAtomStyleRadius( (Atom::DrawStyle) n, newvalue.asDouble(result)); - gui.mainCanvas()->updatePrimitives(); + engine().updatePrimitives(); break; case (PreferencesVariable::BackCull): ptr->setBackfaceCulling(newvalue.asBool()); @@ -697,7 +693,7 @@ bool PreferencesVariable::setAccessor(int i, ReturnValue &sourcerv, ReturnValue if (newvalue.arraySize() == Atom::nDrawStyles) for (n=0; nsetBondStyleRadius( (Atom::DrawStyle) n, newvalue.asDouble(n, result)); else if (hasArrayIndex) ptr->setBondStyleRadius( (Atom::DrawStyle) (arrayIndex-1), newvalue.asDouble(result)); else for (n=0; nsetBondStyleRadius( (Atom::DrawStyle) n, newvalue.asDouble(result)); - gui.mainCanvas()->updatePrimitives(); + engine().updatePrimitives(); break; case (PreferencesVariable::BondTolerance): ptr->setBondTolerance( newvalue.asDouble(result) ); @@ -860,7 +856,7 @@ bool PreferencesVariable::setAccessor(int i, ReturnValue &sourcerv, ReturnValue } break; case (PreferencesVariable::LabelSize): - ptr->setLabelSize( newvalue.asInteger(result) ); + ptr->setLabelSize( newvalue.asDouble(result) ); break; case (PreferencesVariable::LevelOfDetailStartZ): ptr->setLevelOfDetailStartZ( newvalue.asDouble(result) ); @@ -870,7 +866,7 @@ bool PreferencesVariable::setAccessor(int i, ReturnValue &sourcerv, ReturnValue break; case (PreferencesVariable::LevelsOfDetail): ptr->setLevelsOfDetail( newvalue.asInteger(result) ); - gui.mainCanvas()->updatePrimitives(); + engine().updatePrimitives(); break; case (PreferencesVariable::LineAliasing): ptr->setLineAliasing( newvalue.asBool() ); @@ -960,7 +956,7 @@ bool PreferencesVariable::setAccessor(int i, ReturnValue &sourcerv, ReturnValue break; case (PreferencesVariable::SelectionScale): ptr->setSelectionScale( newvalue.asDouble(result) ); - gui.mainCanvas()->updatePrimitives(); + engine().updatePrimitives(); break; case (PreferencesVariable::Shininess): ptr->setShininess( newvalue.asInteger(result) ); @@ -1032,11 +1028,8 @@ bool PreferencesVariable::setAccessor(int i, ReturnValue &sourcerv, ReturnValue else if (hasArrayIndex) ptr->setColour(Prefs::UnitCellColour, arrayIndex-1, newvalue.asDouble(result)); else for (n=0; n<4; ++n) ptr->setColour(Prefs::UnitCellColour, n, newvalue.asDouble(result)); break; - case (PreferencesVariable::UseFrameBuffer): - ptr->setUseFrameBuffer( newvalue.asBool() ); - break; - case (PreferencesVariable::UseNiceText): - ptr->setUseNiceText( newvalue.asBool() ); + case (PreferencesVariable::UsePixelBuffers): + ptr->setUsePixelBuffers( newvalue.asBool() ); break; case (PreferencesVariable::VdwCutoff): ptr->setVdwCutoff( newvalue.asDouble(result) ); @@ -1070,12 +1063,7 @@ bool PreferencesVariable::setAccessor(int i, ReturnValue &sourcerv, ReturnValue result = FALSE; break; } - // Update model and main view TGAY necessary? -// if (result) -// { -// if (aten.current.rs() != NULL) aten.current.rs()->changeLog.add(Log::Visual); -// gui.mainCanvas()->postRedisplay(); -// } + msg.exit("PreferencesVariable::setAccessor"); return result; } diff --git a/src/parser/prefs.h b/src/parser/prefs.h index 6f8e3758f..ee34492cb 100644 --- a/src/parser/prefs.h +++ b/src/parser/prefs.h @@ -41,7 +41,7 @@ class PreferencesVariable : public Variable */ public: // Accessor list - enum Accessors { AllowDialogs, AngleLabelFormat, AromaticRingColour, AtomStyleRadius, BackCull, BackgroundColour, BondStyleRadius, BondTolerance, CacheLimit, CalculateIntra, CalculateVdw, ChargeLabelFormat, ClipFar, ClipNear, ColourScales, ColourScheme, CombinationRule, CommonElements, DashedAromatics, DensityUnit, DepthCue, DepthFar, DepthNear, DistanceLabelFormat, ElecCutoff, ElecMethod, EncoderArgs, EncoderExe, EncoderPostArgs, EncoderPostExe, EnergyUnit, EnergyUpdate, EwaldAlpha, EwaldKMax, EwaldPrecision, ForceRhombohedral, FrameCurrentModel, FrameWholeView, GlobeAxesColour, GlobeColour, GlobeSize, GlyphDefaultColour, HBonds, HBondDotRadius, HDistance, ImageQuality, KeyAction, LabelSize, LevelOfDetailStartZ, LevelOfDetailWidth, LevelsOfDetail, LineAliasing, ManualSwapBuffers, MaxCuboids, MaxRings, MaxRingSize, MaxUndo, ModelUpdate, MopacExe, MouseAction, MouseMoveFilter, MultiSampling, NoQtSettings, PartitionGrid, Perspective, PerspectiveFov, PolygonAliasing, Quality, RenderStyle, ReplicateFold, ReplicateTrim, ReuseQuality, SelectionScale, Shininess, SpecularColour, Spotlight, SpotlightAmbient, SpotlightDiffuse, SpotlightPosition, SpotlightSpecular, StickNormalWidth, StickSelectedWidth, TempDir, TextColour, TransparencyBinStartZ, TransparencyBinWidth, TransparencyCorrect, TransparencyNBins, TransparentSelection, UnitCellAxesColour, UnitCellColour, UseFrameBuffer, UseNiceText, VdwCutoff, VibrationArrowColour, ViewRotationGlobe, Warn1056, WireSelectionColour, ZMapping, ZoomThrottle, nAccessors }; + enum Accessors { AllowDialogs, AngleLabelFormat, AromaticRingColour, AtomStyleRadius, BackCull, BackgroundColour, BondStyleRadius, BondTolerance, CacheLimit, CalculateIntra, CalculateVdw, ChargeLabelFormat, ClipFar, ClipNear, ColourScales, ColourScheme, CombinationRule, CommonElements, DashedAromatics, DensityUnit, DepthCue, DepthFar, DepthNear, DistanceLabelFormat, ElecCutoff, ElecMethod, EncoderArgs, EncoderExe, EncoderPostArgs, EncoderPostExe, EnergyUnit, EnergyUpdate, EwaldAlpha, EwaldKMax, EwaldPrecision, ForceRhombohedral, FrameCurrentModel, FrameWholeView, GlobeAxesColour, GlobeColour, GlobeSize, GlyphDefaultColour, HBonds, HBondDotRadius, HDistance, ImageQuality, KeyAction, LabelSize, LevelOfDetailStartZ, LevelOfDetailWidth, LevelsOfDetail, LineAliasing, ManualSwapBuffers, MaxCuboids, MaxRings, MaxRingSize, MaxUndo, ModelUpdate, MopacExe, MouseAction, MouseMoveFilter, MultiSampling, NoQtSettings, PartitionGrid, Perspective, PerspectiveFov, PolygonAliasing, Quality, RenderStyle, ReplicateFold, ReplicateTrim, ReuseQuality, SelectionScale, Shininess, SpecularColour, Spotlight, SpotlightAmbient, SpotlightDiffuse, SpotlightPosition, SpotlightSpecular, StickNormalWidth, StickSelectedWidth, TempDir, TextColour, TransparencyBinStartZ, TransparencyBinWidth, TransparencyCorrect, TransparencyNBins, TransparentSelection, UnitCellAxesColour, UnitCellColour, UsePixelBuffers, VdwCutoff, VibrationArrowColour, ViewRotationGlobe, Warn1056, WireSelectionColour, ZMapping, ZoomThrottle, nAccessors }; // Function list enum Functions { DummyFunction, nFunctions }; // Search variable access list for provided accessor diff --git a/src/render/CMakeLists.txt b/src/render/CMakeLists.txt index 0512b8ca0..34b7fa84b 100644 --- a/src/render/CMakeLists.txt +++ b/src/render/CMakeLists.txt @@ -1,5 +1,6 @@ add_library(render engine.h +engine_primitives.h gridprimitive.h primitive.h primitivegroup.h @@ -10,6 +11,7 @@ vertexchunk.h engine.cpp engine_glyph.cpp engine_model.cpp +engine_primitives.cpp engine_useractions.cpp engine_windows.cpp gridprimitive.cpp diff --git a/src/render/Makefile.am b/src/render/Makefile.am index 34a09b95a..3b309b2f6 100644 --- a/src/render/Makefile.am +++ b/src/render/Makefile.am @@ -1,7 +1,7 @@ noinst_LIBRARIES = librender.a -librender_a_SOURCES = engine.cpp engine_glyph.cpp engine_model.cpp engine_useractions.cpp engine_windows.cpp gridprimitive.cpp primitive.cpp primitive_surface.cpp primitive_vertex.cpp primitivegroup.cpp primitiveinfo.cpp textprimitive.cpp triangles.cpp vertexchunk.cpp +librender_a_SOURCES = engine.cpp engine_glyph.cpp engine_model.cpp engine_primitives.cpp engine_useractions.cpp engine_windows.cpp gridprimitive.cpp primitive.cpp primitive_surface.cpp primitive_vertex.cpp primitivegroup.cpp primitiveinfo.cpp textprimitive.cpp triangles.cpp vertexchunk.cpp -noinst_HEADERS = engine.h gridprimitive.h primitive.h primitivegroup.h primitiveinfo.h textprimitive.h triangles.h vertexchunk.h +noinst_HEADERS = engine.h engine_primitives.h gridprimitive.h primitive.h primitivegroup.h primitiveinfo.h textprimitive.h triangles.h vertexchunk.h INCLUDES = -I$(top_srcdir)/src -I../ -I$(top_srcdir)/src/gui @GUI_CFLAGS@ diff --git a/src/render/engine.cpp b/src/render/engine.cpp index e185e87c5..a6fa82f2a 100644 --- a/src/render/engine.cpp +++ b/src/render/engine.cpp @@ -27,322 +27,136 @@ #include "gui/gui.h" #include "gui/tcanvas.uih" #include "main/aten.h" +#include "base/sysfunc.h" #include -/* -// Render Primitives -*/ - -// Constructor -RenderPrimitives::RenderPrimitives() +// Class return function +RenderEngine &engine() { - // Primitives - requestedQuality_ = -1; - currentQuality_ = -1; - stackSize_ = 0; - - // Set names of primitives (for bug-tracking) - atom_.setName("Atom"); - selectedAtom_.setName("SelectedAtom"); - bonds_[Atom::TubeStyle][Bond::Single].setName("Bond[Tube,Single]"); - bonds_[Atom::SphereStyle][Bond::Single].setName("Bond[Sphere,Single]"); - bonds_[Atom::ScaledStyle][Bond::Single].setName("Bond[Scaled,Single]"); - selectedBonds_[Atom::TubeStyle][Bond::Single].setName("SelectedBond[Tube,Single]"); - selectedBonds_[Atom::SphereStyle][Bond::Single].setName("SelectedBond[Sphere,Single]"); - selectedBonds_[Atom::ScaledStyle][Bond::Single].setName("SelectedBond[Scaled,Single]"); - bonds_[Atom::TubeStyle][Bond::Double].setName("Bond[Tube,Double]"); - bonds_[Atom::SphereStyle][Bond::Double].setName("Bond[Sphere,Double]"); - bonds_[Atom::ScaledStyle][Bond::Double].setName("Bond[Scaled,Double]"); - selectedBonds_[Atom::TubeStyle][Bond::Double].setName("SelectedBond[Tube,Double]"); - selectedBonds_[Atom::SphereStyle][Bond::Double].setName("SelectedBond[Sphere,Double]"); - selectedBonds_[Atom::ScaledStyle][Bond::Double].setName("SelectedBond[Scaled,Double]"); - bonds_[Atom::TubeStyle][Bond::Triple].setName("Bond[Tube,Triple]"); - bonds_[Atom::SphereStyle][Bond::Triple].setName("Bond[Sphere,Triple]"); - bonds_[Atom::ScaledStyle][Bond::Triple].setName("Bond[Scaled,Triple]"); - selectedBonds_[Atom::TubeStyle][Bond::Triple].setName("SelectedBond[Tube,Triple]"); - selectedBonds_[Atom::SphereStyle][Bond::Triple].setName("SelectedBond[Sphere,Triple]"); - selectedBonds_[Atom::ScaledStyle][Bond::Triple].setName("SelectedBond[Scaled,Triple]"); - bonds_[Atom::TubeStyle][Bond::Aromatic].setName("Bond[Tube,Aromatic]"); - bonds_[Atom::SphereStyle][Bond::Aromatic].setName("Bond[Sphere,Aromatic]"); - bonds_[Atom::ScaledStyle][Bond::Aromatic].setName("Bond[Scaled,Aromatic]"); - selectedBonds_[Atom::TubeStyle][Bond::Aromatic].setName("SelectedBond[Tube,Aromatic]"); - selectedBonds_[Atom::SphereStyle][Bond::Aromatic].setName("SelectedBond[Sphere,Aromatic]"); - selectedBonds_[Atom::ScaledStyle][Bond::Aromatic].setName("SelectedBond[Scaled,Aromatic]"); - cubes_.setName("Cubes"); - originCubes_.setName("OriginCubes"); - spheres_.setName("Spheres"); - cylinders_.setName("Cylinders"); - cones_.setName("Cones"); - tubeRings_.setName("TubeRings"); - segmentedTubeRings_.setName("SegmentedTubeRings"); - lineRings_.setName("LineRings"); - segmentedLineRings_.setName("SegmentedLineRings"); - wireCube_.setName("WireCube"); - crossedCube_.setName("CrossedCube"); - cellAxes_.setName("CellAxes"); - rotationGlobe_.setName("RotationGlobe"); - rotationGlobeAxes_.setName("RotationGlobeAxes"); + static RenderEngine engine_; + return engine_; } -// Destructor -RenderPrimitives::~RenderPrimitives() +// Bitmap Image Formats (conform to allowable pixmap formats in Qt) +const char *bitmapFormatFilters[RenderEngine::nBitmapFormats] = { "Windows Bitmap (*.bmp)", "Joint Photographic Experts Group (*.jpg)", "Portable Network Graphics (*.png)", "Portable Pixmap (*.ppm)", "X11 Bitmap (*.xbm)", "X11 Pixmap (*.xpm)" }; +const char *bitmapFormatExtensions[RenderEngine::nBitmapFormats] = { "bmp", "jpg", "png", "ppm", "xbm", "xpm" }; +RenderEngine::BitmapFormat RenderEngine::bitmapFormat(const char *s, bool reportError) { + RenderEngine::BitmapFormat bf = (RenderEngine::BitmapFormat) enumSearch("bitmap format", RenderEngine::nBitmapFormats, bitmapFormatExtensions, s); + if ((bf == RenderEngine::nBitmapFormats) && reportError) enumPrintValid(RenderEngine::nBitmapFormats, bitmapFormatExtensions); + return bf; } - -// Set the desired primitive quality -void RenderPrimitives::setQuality(int quality) +RenderEngine::BitmapFormat RenderEngine::bitmapFormatFromFilter(const char *s) { - requestedQuality_ = quality; + return (RenderEngine::BitmapFormat) enumSearch("bitmap format", RenderEngine::nBitmapFormats, bitmapFormatFilters,s); } - -// Return current primitive instance stacksize -int RenderPrimitives::stackSize() +const char *RenderEngine::bitmapFormatFilter(RenderEngine::BitmapFormat bf) { - return stackSize_; + return bitmapFormatFilters[bf]; } - -// (Re)Generate primitives -void RenderPrimitives::recreatePrimitives(bool force) +const char *RenderEngine::bitmapFormatExtension(RenderEngine::BitmapFormat bf) { - msg.enter("RenderPrimitives::recreatePrimitives"); - double radius, lodratio, aradius[Atom::nDrawStyles], bradius[Atom::nDrawStyles], selscale; - int n, m, lod, nstacks, nslices; - - // If current quality is the same as the requested quality, do nothing - if ((requestedQuality_ == currentQuality_) && (!force)) - { - msg.exit("RenderPrimitives::recreatePrimitives"); - return; - } - - currentQuality_ = requestedQuality_; - - // Clear old primitive groups - atom_.clear(); - selectedAtom_.clear(); - for (n=0; n= 0x040600 + QGL::setPreferredPaintEngine(QPaintEngine::OpenGL); + #endif + + // Create a context for the TCanvas + canvasContext_ = new QGLContext(QGLFormat::defaultFormat()); + + // Attempt to initialise a pixelbuffer + if (prefs.usePixelBuffers() && (!initialisePixelBuffer(2048,2048))) prefs.setUsePixelBuffers(FALSE); } // Destructor @@ -385,7 +199,7 @@ void RenderEngine::calculateAdjustments() } // Render primitive in specified colour and level of detail (coords/transform used only if filtered) -void RenderEngine::renderPrimitive(RenderEngine::RenderingObject obj, PrimitiveGroup& pg, GLfloat* colour, Matrix& transform, GLenum fillMode, GLfloat lineWidth) +void RenderEngine::renderPrimitive(RenderEngine::TriangleObject obj, PrimitiveGroup& pg, GLfloat* colour, Matrix& transform, GLenum fillMode, GLfloat lineWidth) { if (!activePrimitiveLists_[obj]) return; if ((colour[3] > 0.99f) || (fillMode != GL_FILL)) @@ -403,7 +217,7 @@ void RenderEngine::renderPrimitive(RenderEngine::RenderingObject obj, PrimitiveG } // Render primitive in specified colour -void RenderEngine::renderPrimitive(RenderEngine::RenderingObject obj, Primitive* primitive, bool isTransparent, GLfloat *colour, Matrix& transform, GLenum fillMode, GLfloat lineWidth) +void RenderEngine::renderPrimitive(RenderEngine::TriangleObject obj, Primitive* primitive, bool isTransparent, GLfloat *colour, Matrix& transform, GLenum fillMode, GLfloat lineWidth) { if (!activePrimitiveLists_[obj]) return; if ((!isTransparent) || (fillMode != GL_FILL) || ((colour != NULL) && (colour[3] > 0.99f))) @@ -423,7 +237,7 @@ void RenderEngine::renderPrimitive(RenderEngine::RenderingObject obj, Primitive* // Add text primitive for rendering later void RenderEngine::renderTextPrimitive(int x, int y, const char *text, QChar addChar, bool rightalign) { - textPrimitives_.add(x, y, text, addChar, rightalign); + textPrimitives_.add(x, contextHeight_-y, text, addChar, rightalign); } // Search for primitive associated to specified Grid pointer @@ -449,15 +263,15 @@ void RenderEngine::sortAndSendGL() Primitive *prim; // Transform and render each solid primitive in each list - for (int n=0; nnext) { // If the info structure has a pointer to a primitive in it, use that. // Otherwise, retrieve a primitive with a suitable level of detail by passing the current model transformation matrix prim = pi->primitive(); - if (prim == NULL) prim = (Q_ == 1 ? pi->bestPrimitive() : pi->primitive(modelTransformationMatrix_)); - + if (prim == NULL) prim = (set_ == 1 ? pi->bestPrimitive() : pi->primitive(modelTransformationMatrix_)); + // If colour data is not present in the vertex data array, use the colour stored in the PrimitiveInfo object if (!prim->colouredVertexData()) glColor4fv(pi->colour()); glPolygonMode(GL_FRONT_AND_BACK, pi->fillMode()); @@ -488,9 +302,11 @@ void RenderEngine::sortAndSendGL() glDisable(GL_LIGHTING); glLoadMatrixd(modelTransformationMatrix_.matrix()); glLineWidth(prefs.stickLineNormalWidth()); - stickLines_.sendToGL(); + linePrimitives_[NormalLineObject].sendToGL(); + linePrimitives_[NormalGuiLineObject].sendToGL(); glLineWidth(prefs.stickLineSelectedWidth()); - stickSelectedLines_.sendToGL(); + linePrimitives_[SelectedLineObject].sendToGL(); + linePrimitives_[SelectedGuiLineObject].sendToGL(); glEnable(GL_LIGHTING); glLineWidth(1.0); @@ -498,7 +314,7 @@ void RenderEngine::sortAndSendGL() if (prefs.transparencyCorrect()) { triangleChopper_.emptyTriangles(); - for (int n=0; nnext) { @@ -511,14 +327,14 @@ void RenderEngine::sortAndSendGL() triangleChopper_.sendToGL(); glPopClientAttrib(); } - else for (int n=0; nnext) { // If the info structure has a pointer to a primitive in it, use that. // Otherwise, work out a level of detail value to pass to the primitive group referenced. prim = pi->primitive(); - if (prim == NULL) prim = (Q_ == 1 ? pi->bestPrimitive() : pi->primitive(modelTransformationMatrix_)); + if (prim == NULL) prim = (set_ == 1 ? pi->bestPrimitive() : pi->primitive(modelTransformationMatrix_)); if (!prim->colouredVertexData()) glColor4fv(pi->colour()); A = modelTransformationMatrix_ * pi->localTransform(); glLoadMatrixd(A.matrix()); @@ -541,7 +357,9 @@ void RenderEngine::initialiseGL() GLfloat col[4]; prefs.copyColour(Prefs::BackgroundColour, col); glClearColor(col[0],col[1],col[2],col[3]); - //glClearDepth(1.0); +// glClearDepth(1.0); +// glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE); +// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Perspective hint glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_FASTEST); // Enable depth buffer @@ -611,206 +429,377 @@ void RenderEngine::initialiseGL() } // Push primitives instance (in specified quality) -void RenderEngine::pushInstance(bool highQuality, const QGLContext *context) +void RenderEngine::pushInstance(RenderEngine::PrimitiveSet set, const QGLContext *context) { - msg.print(Messenger::Verbose, "Pushing %s quality primitive instance for context %p\n", highQuality ? "high" : "normal", context); - primitives_[highQuality].pushInstance(context); - // Push separate instance of the stick primitives - stickLines_.pushInstance(context); - stickSelectedLines_.pushInstance(context); + msg.print(Messenger::Verbose, "Pushing %s primitive instance for context %p (current context is %p).\n", RenderEngine::primitiveSet(set), context, QGLContext::currentContext()); + primitives_[set].pushInstance(context); + // Push separate instance of the line primitives + for (int n = 0; n pixelBufferWidth_) || (h > pixelBufferHeight_)) + { + int newWidth = 1, newHeight = 1; + while (newWidth < w) { newWidth *= 2; }; + while (newHeight < h) { newHeight *= 2; }; + msg.print(Messenger::Verbose, "Creating new offscreen pixelbuffer - requested = %ix%i, old = %ix%i, new = %ix%i\n", w, h, pixelBufferWidth_, pixelBufferHeight_, newWidth, newHeight); + pixelBufferHeight_ = newHeight; + pixelBufferWidth_ = newWidth; + + // Clean up from previous instantiation + if (pixelBuffer_ != NULL) + { + delete pixelBuffer_; + popInstance(LowQuality, pixelBufferContext_); + popInstance(HighQuality, pixelBufferContext_); + } + + // Create new pixelbuffer and associated primitives + pixelBuffer_ = new QGLPixelBuffer(pixelBufferWidth_, pixelBufferHeight_, QGLFormat::defaultFormat(), gui.mainCanvas()); + + if (!pixelBuffer_->makeCurrent()) + { + msg.print("Error: Couldn't make pixelBuffer_'s context current.\n"); + delete pixelBuffer_; + pixelBuffer_ = NULL; + pixelBufferHeight_ = 0; + pixelBufferWidth_ = 0; + pixelBufferContext_ = NULL; + return FALSE; + } + pixelBufferContext_ = QGLContext::currentContext(); + pushInstance(LowQuality, pixelBufferContext_); + pushInstance(HighQuality, pixelBufferContext_); + } + return TRUE; } -// Flag that next render should clear all primitive lists -void RenderEngine::flagClearLists() +// Return context for TCanvas +QGLContext *RenderEngine::canvasContext() { - clearListsFlag_ = TRUE; + return canvasContext_; } -// Clear text primitive list -void RenderEngine::forgetTextPrimitives() +// Check for GL Error +void RenderEngine::checkGlError() { - textPrimitives_.forgetAll(); + // Do GL error check + if (msg.isOutputActive(Messenger::GL)) + { + GLenum glerr = GL_NO_ERROR; + do + { + switch (glGetError()) + { + case (GL_INVALID_ENUM): msg.print(Messenger::GL, "GLenum argument out of range\n"); break; + case (GL_INVALID_VALUE): msg.print(Messenger::GL, "Numeric argument out of range\n"); break; + case (GL_INVALID_OPERATION): msg.print(Messenger::GL, "Operation illegal in current state\n"); break; + case (GL_STACK_OVERFLOW): msg.print(Messenger::GL, "Command would cause a stack overflow\n"); break; + case (GL_STACK_UNDERFLOW): msg.print(Messenger::GL, "Command would cause a stack underflow\n"); break; + case (GL_OUT_OF_MEMORY): msg.print(Messenger::GL, "Not enough memory left to execute command\n"); break; + case (GL_NO_ERROR): msg.print(Messenger::GL, "No GL error\n"); break; + default: + msg.print(Messenger::GL, "Unknown GL error?\n"); + break; + } + } while (glerr != GL_NO_ERROR); + } } -// Render 3D -void RenderEngine::render3D(bool highQuality, Model *source, TCanvas *canvas, bool currentModel) +// Render Main Scene +void RenderEngine::renderScene(RenderEngine::PrimitiveSet set, int width, int height, const QGLContext* context, RenderEngine::RenderType renderType, Model* iconSource) { - GLfloat colour[4]; - - // Set initial transformation matrix, including any translation occurring from cell... - setTransformationMatrix(source->modelViewMatrix(), source->cell()->centre()); + msg.enter("RenderEngine::renderScene"); + QColor color; + QRect currentBox; + Refitem *first, localri; + int px, py, nperrow = prefs.nModelsPerRow(), nrows, col, row, nmodels; + bool modelIsCurrentModel; + Model *m; + + // Store set specifier + set_ = set; - // Set target matrix mode and reset it, and set colour mode - glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); - glEnable(GL_COLOR_MATERIAL); + // Store full context height + contextHeight_ = height; - // Store quality specifier - Q_ = highQuality ? 1 : 0; + // Setup basic GL stuff + initialiseGL(); - // Grab model-specific viewport - GLint *vp = source->viewportMatrix(); - - // Render rotation globe in small viewport in lower right-hand corner - if (prefs.viewRotationGlobe()) + // Note: An internet source suggests that the QPainter documentation is incomplete, and that + // all OpenGL calls should be made after the QPainter is constructed, and before the QPainter + // is destroyed. However, this results in mangled graphics on the Linux (and other?) versions, + // so here it is done in the 'wrong' order. + + // Set the first item to consider - set localri to the passed iconSource (if there was one) + localri.item = iconSource; + nmodels = aten.nVisibleModels(); + if ((nmodels == 0) || (renderType == OffscreenModel)) { - int n = prefs.globeSize(); - if (aten.nVisibleModels() > 2) n /= 2; - glViewport(vp[0]+vp[2]-n,vp[1],n,n); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(-1.0, 1.0, -1.0, 1.0, -10.0, 10.0); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - Matrix A = modelTransformationMatrix_; - A.removeTranslationAndScaling(); - A[14] = -1.2; - glMultMatrixd(A.matrix()); - prefs.copyColour(Prefs::GlobeColour, colour); - glColor4fv(colour); - primitives_[Q_].rotationGlobe_.sendToGL(); - prefs.copyColour(Prefs::GlobeAxesColour, colour); - glColor4fv(colour); - primitives_[Q_].rotationGlobeAxes_.sendToGL(); + if (renderType != OffscreenModel) localri.item = aten.currentModel(); + nmodels = 1; + first = &localri; } + else first = aten.visibleModels(); - // Prepare for model rendering - glViewport(vp[0], vp[1], vp[2], vp[3]); - glMatrixMode(GL_PROJECTION); - glLoadMatrixd(source->modelProjectionMatrix().matrix()); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - // Clear the necessary triangle lists (i.e. only those for which the content may have changed) - // By default, regenerate all lists (the case if the source model pointer has changed) - int n; - for (n=0; n Clearing context, background, and setting pen colour\n"); + glViewport(0,0,width,height); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + textPrimitives_.forgetAll(); + + // Set up some useful values + nrows = nmodels/nperrow + (nmodels%nperrow == 0 ? 0 : 1); + py = height / nrows; + px = (nmodels == 1 ? width : width / nperrow); + + // Loop over model refitems in list (or single refitem) + col = 0; + row = 0; + for (Refitem *ri = first; ri != NULL; ri = ri->next) { - // Check model logs against logs stored when the lists were last generated - bool redobasic = !lastLog_.isSame(Log::Coordinates,source->changeLog); - if (!redobasic) redobasic = !lastLog_.isSame(Log::Structure,source->changeLog); - // If the model style has changed, need to rerender both basic and selection lists. Otherwise, depends on other logs - if (redobasic || (!lastLog_.isSame(Log::Style,source->changeLog))) + // Grab model pointer + m = ri->item; + if (m == NULL) continue; + + // Store coordinates for box if this is the current model + if (m == aten.currentModel()) { - activePrimitiveLists_[RenderEngine::BasicObject] = TRUE; - activePrimitiveLists_[RenderEngine::AtomSelectionObject] = TRUE; + modelIsCurrentModel = TRUE; + currentBox.setRect(col*px, row*py, px, py); } - else + else modelIsCurrentModel = FALSE; + + // Vibration frame? + m = m->renderSourceModel(); + if (m->renderFromVibration() && (m->vibrationCurrentFrame() != NULL)) m = m->vibrationCurrentFrame(); + + // Determine desired pixel range and set up view(port) + checkGlError(); + m->setupView(col*px, height-(row+1)*py, px, py); + + // Render the 3D parts of the model + renderModel(m, modelIsCurrentModel, renderType); + + // Increase counters + ++col; + if (col%nperrow == 0) { - activePrimitiveLists_[RenderEngine::BasicObject] = FALSE; - activePrimitiveLists_[RenderEngine::AtomSelectionObject] = !lastLog_.isSame(Log::Selection, source->changeLog); + col = 0; + ++row; } - - // Stick style primitives - if (redobasic || (!lastLog_.isSame(Log::Style,source->changeLog)) || (!lastLog_.isSame(Log::Selection,source->changeLog))) + } + + // Select QPaintDevice* for QPainter + QPaintDevice* targetDevice = NULL; + if ((renderType != OnscreenScene) && prefs.usePixelBuffers()) targetDevice = pixelBuffer_; + else if (context != canvasContext_) targetDevice = QGLContext::currentContext()->device(); + else targetDevice = gui.mainCanvas(); + + // Start of QPainter code + static QFont font; + static QBrush nobrush(Qt::NoBrush), solidbrush(Qt::SolidPattern); + static QPen pen; + QPainter painter(targetDevice); + + // Need to offset QPainter vertically if using pixelbuffers + if ((renderType != OnscreenScene) && (prefs.usePixelBuffers())) + { + painter.translate(0, painter.viewport().height()-height); + } + + // Text Primitives + GLfloat colour[4]; + double fontSize = (prefs.labelSize()/100.0)*height; + font.setPointSizeF(fontSize); + painter.setFont(font); + painter.setRenderHint(QPainter::Antialiasing); + prefs.copyColour(Prefs::TextColour, colour); + color.setRgbF(colour[0], colour[1], colour[2], colour[3]); + solidbrush.setColor(color); + painter.setBrush(solidbrush); + painter.setPen(Qt::SolidLine); + painter.setPen(color); + textPrimitives_.renderAll(painter, fontSize); + + // Draw box around current model + color.setRgbF(0.0,0.0,0.0,1.0); + pen.setColor(color); + pen.setWidth(2); + painter.setBrush(nobrush); + painter.setPen(Qt::SolidLine); + painter.setPen(pen); + + if (prefs.frameCurrentModel()) painter.drawRect(currentBox); + if (prefs.frameWholeView()) + { + currentBox.setRect(0,0,width,height); + painter.drawRect(currentBox); + } + + // Render active user modes + if (renderType == OnscreenScene) renderActiveModes(painter, width, height); + + // Done + painter.end(); + + msg.print(Messenger::GL, " --> RENDERING END\n"); + + msg.exit("RenderEngine::renderScene"); +} + +// Render or grab image +QPixmap RenderEngine::renderSceneImage(RenderEngine::PrimitiveSet set, int w, int h) +{ + if (prefs.usePixelBuffers()) + { + // Check that we can create a pixelbuffer object + initialisePixelBuffer(w, h); + if (!pixelBuffer_->makeCurrent()) { - stickLines_.forgetAll(); - stickSelectedLines_.forgetAll(); - rebuildSticks_ = TRUE; + msg.print("Error: QGLPixelBuffer could not be made current.\n"); + return QPixmap(); } - else rebuildSticks_ = FALSE; + + // Clear lists in engine + clearListsFlag_ = TRUE; + + // Generate offscreen bitmap + renderScene(RenderEngine::HighQuality, w, h, pixelBufferContext_, OffscreenScene); + pixelBuffer_->doneCurrent(); + + // Flag for rendering list regeneration again + clearListsFlag_ = TRUE; - // Glyphs must be redone on basic change (since they may follow atom coordinates - if (redobasic || !lastLog_.isSame(Log::Glyphs, source->changeLog)) activePrimitiveLists_[RenderEngine::GlyphObject] = TRUE; - else activePrimitiveLists_[RenderEngine::GlyphObject] = FALSE; + QImage image = pixelBuffer_->toImage().copy(0,pixelBufferHeight_-h,w,h); - // Grids only depend on their own log - if (!lastLog_.isSame(Log::Grids, source->changeLog)) activePrimitiveLists_[RenderEngine::GridObject] = TRUE; + return QPixmap::fromImage(image); } else { - stickLines_.forgetAll(); - stickSelectedLines_.forgetAll(); - rebuildSticks_ = TRUE; - } + // Clear lists in engine + clearListsFlag_ = TRUE; - // Clear flagged lists - for (n=0; nrequestHighQuality(); + + // Generate offscreen bitmap (a temporary context will be created) + gui.mainCanvas()->setRenderType(OffscreenScene); + QPixmap pixmap = gui.mainCanvas()->renderPixmap(w,h); + gui.mainCanvas()->setRenderType(OnscreenScene); + + // Flag for rendering list regeneration again + clearListsFlag_ = TRUE; + return pixmap; } - - // Extra lists to clear for Glyphs - if (activePrimitiveLists_[RenderEngine::GlyphObject]) +} + +// Create icon pixmap for specific model +QPixmap RenderEngine::renderModelIcon(Model *source) +{ + msg.print(Messenger::Verbose, "Rendering model icon for '%s'....\n", source == NULL ? "??NULL Model??" : source->name()); + if (prefs.usePixelBuffers()) { - glyphTriangles_[RenderEngine::SolidTriangle].forgetAll(); - glyphTriangles_[RenderEngine::TransparentTriangle].forgetAll(); - glyphTriangles_[RenderEngine::WireTriangle].forgetAll(); - glyphLines_.forgetAll(); + // Check that we can use create a pixelbuffer object + initialisePixelBuffer(100, 100); + if (!pixelBuffer_->makeCurrent()) + { + msg.print("Error: QGLPixelBuffer could not be made current.\n"); + return QPixmap(); + } + + // Clear lists in engine + clearListsFlag_ = TRUE; + + // Generate offscreen bitmap + renderScene(RenderEngine::LowQuality, 100, 100, pixelBufferContext_, OffscreenModel, source); + pixelBuffer_->doneCurrent(); + + // Flag for rendering list regeneration again + clearListsFlag_ = TRUE; + + QImage image = pixelBuffer_->toImage().copy(0,pixelBufferHeight_-100,100,100); + + return QPixmap::fromImage(image); } - - // Always draw unit cell, regardless of list status - renderCell(source); - // Draw main model (atoms, bonds, etc.) - if (activePrimitiveLists_[RenderEngine::BasicObject] || activePrimitiveLists_[RenderEngine::AtomSelectionObject]) renderModel(source); - // Draw model glyphs - if (activePrimitiveLists_[RenderEngine::GlyphObject]) renderGlyphs(source); - renderTextGlyphs(source, gui.mainCanvas()); - // Draw model grids - if (activePrimitiveLists_[RenderEngine::GridObject]) renderGrids(source); - - lastSource_ = source; - lastLog_ = source->changeLog; - - if (gui.exists()) + else { - // Render embellshments for current UserAction - renderUserActions(source, gui.mainCanvas()); - // Render extras arising from open tool windows (current model only) - if (currentModel) renderWindowExtras(source); + // Clear lists in engine + clearListsFlag_ = TRUE; + + // Request high-quality primitives for next render + gui.mainCanvas()->requestHighQuality(); + + // Generate offscreen bitmap (a temporary context will be created) + gui.mainCanvas()->setRenderType(OffscreenModel, source); + QPixmap pixmap = gui.mainCanvas()->renderPixmap(100,100); + gui.mainCanvas()->setRenderType(OffscreenModel); + + // Flag for rendering list regeneration again + clearListsFlag_ = TRUE; + return pixmap; } +} - // If the stick primitives were regenerated, need to create a new instance (after popping the old one) - if (rebuildSticks_) +// Save image of current view +bool RenderEngine::saveImage(const char *filename, BitmapFormat bf, int width, int height, int quality) +{ + msg.enter("RenderEngine::saveImage"); + if (bf == RenderEngine::nBitmapFormats) { - const QGLContext *context = gui.mainCanvas()->context(); - stickLines_.popInstance(context); - stickSelectedLines_.popInstance(context); - stickLines_.pushInstance(context); - stickSelectedLines_.pushInstance(context); + msg.print("Invalid bitmap format given to Gui::saveImage().\n"); + msg.exit("RenderEngine::saveImage"); + return FALSE; } - // All 3D primitive objects have now been filtered, so sort and send to GL - sortAndSendGL(); + QPixmap pixmap; + // Get current mainCanvas_ geometry if none was specified + if (width == 0) width = pixelBufferWidth_; + if (height == 0) height = pixelBufferHeight_; - // Render overlays - renderModelOverlays(source); + pixmap = renderSceneImage(RenderEngine::HighQuality, width, height); - // Reset the clear lists flag - clearListsFlag_ = FALSE; + pixmap.save(filename, RenderEngine::bitmapFormatExtension(bf), quality); + msg.print("Saved current view as '%s' [%ix%i %s]\n", filename, width, height, RenderEngine::bitmapFormatFilter(bf)); + + msg.exit("RenderEngine::saveImage"); + return TRUE; } diff --git a/src/render/engine.h b/src/render/engine.h index 992567511..7cb3bc80e 100644 --- a/src/render/engine.h +++ b/src/render/engine.h @@ -27,9 +27,8 @@ #include #include "glext.h" #endif +#include "render/engine_primitives.h" #include "render/triangles.h" -#include "render/primitivegroup.h" -#include "render/primitiveinfo.h" #include "render/gridprimitive.h" #include "render/textprimitive.h" #include "base/log.h" @@ -41,57 +40,8 @@ // Forward declarations class Model; class TCanvas; -class RenderEngine; class QGLContext; -// Rendering Primitives -class RenderPrimitives -{ - public: - // Constructor / Destructor - RenderPrimitives(); - ~RenderPrimitives(); - // Declare RenderEngine as a Friend - friend class RenderEngine; - - /* - // Primitives - */ - private: - // Quality setting that primitives should be next generated at - int requestedQuality_; - // Quality setting that primitives were last generated at (if at all) - int currentQuality_; - // Stack size counter - int stackSize_; - // Atom - PrimitiveGroup atom_; - // Selected atom styles - PrimitiveGroup selectedAtom_; - // Bond styles - PrimitiveGroup bonds_[Atom::nDrawStyles][Bond::nBondTypes]; - // Selected bond styles - PrimitiveGroup selectedBonds_[Atom::nDrawStyles][Bond::nBondTypes]; - // Rings - PrimitiveGroup lineRings_, segmentedLineRings_, tubeRings_, segmentedTubeRings_; - // Primitive objects - PrimitiveGroup cubes_, originCubes_, spheres_, cylinders_, cones_; - // One-off objects - Primitive wireCube_, crossedCube_, cellAxes_, rotationGlobe_, rotationGlobeAxes_; - - public: - // Set the desired primitive quality - void setQuality(int quality); - // Return current primitive instance stacksize - int stackSize(); - // (Re)Generate primitive vertex arrays (if necessary) - void recreatePrimitives(bool force = FALSE); - // Push instance layer for all primitives - void pushInstance(const QGLContext *context, bool forceRegenerate = FALSE); - // Pop last instance layer - void popInstance(const QGLContext *context); -}; - // Render Engine class RenderEngine { @@ -101,8 +51,23 @@ class RenderEngine ~RenderEngine(); // Style enum for ease of coding enum TriangleStyle { SolidTriangle, TransparentTriangle, WireTriangle, nTriangleStyles }; - // Objects for rendering - enum RenderingObject { BasicObject, AtomSelectionObject, GridObject, GlyphObject, MiscObject, nRenderingObjects }; + // Triangle-based Objects for rendering + enum TriangleObject { BasicObject, AtomSelectionObject, GridObject, GlyphObject, GuiObject, nTriangleObjects }; + // Stick objects for rendering + enum LineObject { NormalLineObject, SelectedLineObject, NormalGuiLineObject, SelectedGuiLineObject, nLineObjects }; + // Drawing Targets enum + enum DrawingTarget { NoTarget, ScreenTarget, PixmapTarget }; + // Bitmap Formats + enum BitmapFormat { BitmapBMP, BitmapPG, BitmapPNG, BitmapPPM, BitmapXBM, BitmapX11, nBitmapFormats }; + static BitmapFormat bitmapFormat(const char *name, bool reportError = 0); + static BitmapFormat bitmapFormatFromFilter(const char *s); + static const char *bitmapFormatFilter(BitmapFormat bf); + static const char *bitmapFormatExtension(BitmapFormat bf); + // Primitive Set + enum PrimitiveSet { LowQuality, HighQuality, nPrimitiveSets }; + static const char *primitiveSet(PrimitiveSet ps); + // Rendering Type + enum RenderType { OnscreenScene, OffscreenScene, OffscreenModel }; /* @@ -123,22 +88,22 @@ class RenderEngine private: // Atom bond adjustment distances double sphereAtomAdjustment_, *scaledAtomAdjustments_; - // Normal (0, display) and high-quality (offscreen, 1) primitives - RenderPrimitives primitives_[2]; + // Primitive sets for on and offscreen rendering + RenderPrimitives primitives_[RenderEngine::nPrimitiveSets]; // Flag indicating that next render should completely clear all filtered primitive lists bool clearListsFlag_; - // Current primitive source quality (0 or 1) - int Q_; + // Current source primitive set + RenderEngine::PrimitiveSet set_; // Last rendered model Model *lastSource_; // Logs for last rendered source model Log lastLog_; // List of filtered solid primitives - List solidPrimitives_[RenderEngine::nRenderingObjects]; + List solidPrimitives_[RenderEngine::nTriangleObjects]; // List of filtered primitives - List transparentPrimitives_[RenderEngine::nRenderingObjects]; - // Basic (model) line primitive (for stick styles) - Primitive stickLines_, stickSelectedLines_; + List transparentPrimitives_[RenderEngine::nTriangleObjects]; + // Line primitives + Primitive linePrimitives_[RenderEngine::nLineObjects]; // Text primitives TextPrimitiveList textPrimitives_; // Triangle @@ -150,17 +115,19 @@ class RenderEngine // Glyph line primitives Primitive glyphLines_; // Flags indicating which primitive lists are open for rebuilding - bool activePrimitiveLists_[RenderEngine::nRenderingObjects]; + bool activePrimitiveLists_[RenderEngine::nTriangleObjects]; // Flag stating whether to rebuild stick primitives bool rebuildSticks_; + // Height of current rendering target + int contextHeight_; private: // Calculate atom/bond adjustments void calculateAdjustments(); // Render primitive from primitive group in specified colour and level of detail - void renderPrimitive(RenderEngine::RenderingObject obj, PrimitiveGroup& pg, GLfloat* colour, Matrix& transform, GLenum fillMode = GL_FILL, GLfloat lineWidth = 1.0); + void renderPrimitive(RenderEngine::TriangleObject obj, PrimitiveGroup& pg, GLfloat* colour, Matrix& transform, GLenum fillMode = GL_FILL, GLfloat lineWidth = 1.0); // Render primitive in specified colour - void renderPrimitive(RenderEngine::RenderingObject obj, Primitive *primitive, bool isTransparent, GLfloat *colour, Matrix& transform, GLenum fillMode = GL_FILL, GLfloat lineWidth = 1.0); + void renderPrimitive(RenderEngine::TriangleObject obj, Primitive *primitive, bool isTransparent, GLfloat *colour, Matrix& transform, GLenum fillMode = GL_FILL, GLfloat lineWidth = 1.0); // Add text primitive for rendering later void renderTextPrimitive(int x, int y, const char *text, QChar addChar = 0, bool rightalign = FALSE); // Search for primitive associated to specified Grid pointer @@ -170,21 +137,25 @@ class RenderEngine // Sort and render filtered polygons by depth void sortAndSendGL(); // Render bond - void renderBond(Matrix A, Vec3 vij, Atom *i, Atom::DrawStyle style_i, GLfloat *colour_i, double radius_i, Atom *j, Atom::DrawStyle style_j, GLfloat *colour_j, double radius_j, Bond::BondType bt, double selscale, Bond *b = NULL, bool transparentSel = FALSE, GLfloat *penColour = NULL); - // Render basic model information (atoms, bonds, labels) - void renderModel(Model *source, Matrix basetransform = Matrix()); + void renderBond(TriangleObject basicList, TriangleObject selectionList, Matrix A, Vec3 vij, Atom *i, Atom::DrawStyle style_i, GLfloat *colour_i, double radius_i, Atom *j, Atom::DrawStyle style_j, GLfloat *colour_j, double radius_j, Bond::BondType bt, double selscale, Bond *b = NULL, bool transparentSel = FALSE, GLfloat *penColour = NULL); + // Setup and render full Model + void renderModel(Model* source, bool currentModel, bool renderType); + // Render atoms and bonds + void renderAtomsAndBonds(Model* source, Matrix baseTransform = Matrix(), bool isFragment = FALSE); // Render model cell void renderCell(Model *source); // Render grids void renderGrids(Model *source); // Render 3D glyphs - void renderGlyphs(Model* source); + void renderGlyphs(Model *source); // Render text glyphs - void renderTextGlyphs(Model *source, TCanvas *canvas); + void renderTextGlyphs(Model *source); // Render additional model information (labels, measurements etc.) which need to appear on top of everything else void renderModelOverlays(Model *source); + // Render active mode embellishments + void renderActiveModes(QPainter& painter, int width, int height); // Render addition elements related to selected/active UserActions - void renderUserActions(Model *source, TCanvas *canvas); + void renderUserActions(Model *source); // Render addition elements related to visible windows void renderWindowExtras(Model *source); @@ -194,19 +165,46 @@ class RenderEngine // Initialise GL void initialiseGL(); // Push primitives instance (in specified quality) - void pushInstance(bool highQuality, const QGLContext *context); + void pushInstance(RenderEngine::PrimitiveSet set, const QGLContext *context); // Pop topmost primitive instance - void popInstance(bool highQuality, const QGLContext *context); + void popInstance(RenderEngine::PrimitiveSet set, const QGLContext *context); // Update all primitives (following prefs change, etc.) - void updatePrimitives(const QGLContext *context, bool regenerate = FALSE); - // Render text objects (with supplied QPainter) - void renderText(QPainter &painter, TCanvas *canvas); - // Flag that next render should clear all primitive lists - void flagClearLists(); - // Forget all stored text primitives - void forgetTextPrimitives(); - // Render 3D elements with OpenGL - void render3D(bool highQuality, Model* source, TCanvas* canvas, bool currentModel); + void updatePrimitives(); + + + /* + // Rendering + */ + private: + // QQLPixelBuffer for offscreen rendering + QGLPixelBuffer *pixelBuffer_; + // Size of QGLPixelBuffer at last creation + int pixelBufferWidth_, pixelBufferHeight_; + // Context of current pixelBuffer_ + const QGLContext *pixelBufferContext_; + // Context for TCanvas + QGLContext *canvasContext_; + // ID of last trajectory frame displayed (if any) + int displayFrameId_; + + public: + // (Re)initialise pixelbuffer to desired size + bool initialisePixelBuffer(int w, int h, bool forceRecreate = FALSE); + // Return context for TCanvas + QGLContext *canvasContext(); + // Check for GL error + void checkGlError(); + // Render whole scene + void renderScene(RenderEngine::PrimitiveSet set, int width, int height, const QGLContext* context, RenderType renderType, Model *iconSource = NULL); + // Create pixmap of whole scene + QPixmap renderSceneImage(RenderEngine::PrimitiveSet set, int w, int h); + // Create icon pixmap for specific model + QPixmap renderModelIcon(Model *source); + // Save image of current view + bool saveImage(const char *filename, BitmapFormat bf, int width, int height, int quality = 85); }; +// External Declaration +extern RenderEngine &engine(); + #endif diff --git a/src/render/engine_glyph.cpp b/src/render/engine_glyph.cpp index b875a8418..0ed662348 100644 --- a/src/render/engine_glyph.cpp +++ b/src/render/engine_glyph.cpp @@ -68,7 +68,7 @@ void RenderEngine::renderGlyphs(Model *source) // Move to endpoint A.applyTranslationZ(rij*arrowBodyLength); A.applyScaling(0.2,0.2,rij*arrowHeadLength/arrowBodyLength); - renderPrimitive(RenderEngine::GlyphObject, primitives_[Q_].cones_, colour[0], A, GL_LINE); + renderPrimitive(RenderEngine::GlyphObject, primitives_[set_].cones_, colour[0], A, GL_LINE); break; // Line - start = data[0], end = data[1] case (Glyph::LineGlyph): @@ -87,13 +87,13 @@ void RenderEngine::renderGlyphs(Model *source) A.applyScaling(r[1]); if (g->isSolid()) { - renderPrimitive(RenderEngine::GlyphObject, primitives_[Q_].spheres_, colour[0], A, GL_FILL); - if (g->isSelected()) renderPrimitive(RenderEngine::GlyphObject, primitives_[Q_].spheres_, textcolour, A, GL_LINE, 2.0); + renderPrimitive(RenderEngine::GlyphObject, primitives_[set_].spheres_, colour[0], A, GL_FILL); + if (g->isSelected()) renderPrimitive(RenderEngine::GlyphObject, primitives_[set_].spheres_, textcolour, A, GL_LINE, 2.0); } else { - if (g->isSelected()) renderPrimitive(RenderEngine::GlyphObject, primitives_[Q_].spheres_, textcolour, A, GL_LINE, 3.0); - else renderPrimitive(RenderEngine::GlyphObject, primitives_[Q_].spheres_, colour[0], A, GL_LINE, 1.0); + if (g->isSelected()) renderPrimitive(RenderEngine::GlyphObject, primitives_[set_].spheres_, textcolour, A, GL_LINE, 3.0); + else renderPrimitive(RenderEngine::GlyphObject, primitives_[set_].spheres_, colour[0], A, GL_LINE, 1.0); } break; // Cube - centre = data[0], scale = data[1] @@ -106,13 +106,13 @@ void RenderEngine::renderGlyphs(Model *source) A.applyScaling(r[1]); if (g->isSolid()) { - renderPrimitive(RenderEngine::GlyphObject, primitives_[Q_].cubes_, colour[0], A, GL_FILL); - if (g->isSelected()) renderPrimitive(RenderEngine::GlyphObject, primitives_[Q_].cubes_, textcolour, A, GL_LINE, 2.0); + renderPrimitive(RenderEngine::GlyphObject, primitives_[set_].cubes_, colour[0], A, GL_FILL); + if (g->isSelected()) renderPrimitive(RenderEngine::GlyphObject, primitives_[set_].cubes_, textcolour, A, GL_LINE, 2.0); } else { - if (g->isSelected()) renderPrimitive(RenderEngine::GlyphObject, primitives_[Q_].cubes_, textcolour, A, GL_LINE, 3); - else renderPrimitive(RenderEngine::GlyphObject, primitives_[Q_].cubes_, colour[0], A, GL_LINE, 1.0); + if (g->isSelected()) renderPrimitive(RenderEngine::GlyphObject, primitives_[set_].cubes_, textcolour, A, GL_LINE, 3); + else renderPrimitive(RenderEngine::GlyphObject, primitives_[set_].cubes_, colour[0], A, GL_LINE, 1.0); } break; // Triangle - vertex 1 = data[0], vertex 2 = data[1], vertex 3 = data[2] @@ -230,13 +230,13 @@ void RenderEngine::renderGlyphs(Model *source) A.multiplyRotation(B); if (g->isSolid()) { - renderPrimitive(RenderEngine::GlyphObject, primitives_[Q_].spheres_, colour[0], A, GL_FILL); - if (g->isSelected()) renderPrimitive(RenderEngine::GlyphObject, primitives_[Q_].spheres_, textcolour, A, GL_LINE, 2.0); + renderPrimitive(RenderEngine::GlyphObject, primitives_[set_].spheres_, colour[0], A, GL_FILL); + if (g->isSelected()) renderPrimitive(RenderEngine::GlyphObject, primitives_[set_].spheres_, textcolour, A, GL_LINE, 2.0); } else { - if (g->isSelected()) renderPrimitive(RenderEngine::GlyphObject, primitives_[Q_].spheres_, textcolour, A, GL_LINE, 3.0); - else renderPrimitive(RenderEngine::GlyphObject, primitives_[Q_].spheres_, colour[0], A, GL_LINE, 1.0); + if (g->isSelected()) renderPrimitive(RenderEngine::GlyphObject, primitives_[set_].spheres_, textcolour, A, GL_LINE, 3.0); + else renderPrimitive(RenderEngine::GlyphObject, primitives_[set_].spheres_, colour[0], A, GL_LINE, 1.0); } break; // Text - handled in RenderEngine::renderTextGlyphs() @@ -258,11 +258,11 @@ void RenderEngine::renderGlyphs(Model *source) else A.applyRotationAxis(-r[2].y, r[2].x, 0.0, phi, TRUE); // Draw cylinder A.applyScaling(0.1,0.1,rij*arrowBodyLength); - renderPrimitive(RenderEngine::GlyphObject, primitives_[Q_].cylinders_, colour[0], A, g->isSolid() ? GL_FILL : GL_LINE); + renderPrimitive(RenderEngine::GlyphObject, primitives_[set_].cylinders_, colour[0], A, g->isSolid() ? GL_FILL : GL_LINE); // Move to endpoint A.applyTranslationZ(1.0); A.applyScaling(2.0,2.0,arrowHeadLength/arrowBodyLength); - renderPrimitive(RenderEngine::GlyphObject, primitives_[Q_].cones_, colour[0], A, g->isSolid() ? GL_FILL : GL_LINE); + renderPrimitive(RenderEngine::GlyphObject, primitives_[set_].cones_, colour[0], A, g->isSolid() ? GL_FILL : GL_LINE); break; // Tube vector - centre = data[0], vector = data[1] case (Glyph::TubeVectorGlyph): @@ -279,11 +279,11 @@ void RenderEngine::renderGlyphs(Model *source) else A.applyRotationAxis(-r[1].y, r[1].x, 0.0, phi, TRUE); // Draw cylinder A.applyScaling(0.1,0.1,rij*arrowBodyLength); - renderPrimitive(RenderEngine::GlyphObject, primitives_[Q_].cylinders_, colour[0], A, g->isSolid() ? GL_FILL : GL_LINE); + renderPrimitive(RenderEngine::GlyphObject, primitives_[set_].cylinders_, colour[0], A, g->isSolid() ? GL_FILL : GL_LINE); // Move to endpoint A.applyTranslationZ(1.0); A.applyScaling(2.0,2.0,arrowHeadLength/arrowBodyLength); - renderPrimitive(RenderEngine::GlyphObject, primitives_[Q_].cones_, colour[0], A, g->isSolid() ? GL_FILL : GL_LINE); + renderPrimitive(RenderEngine::GlyphObject, primitives_[set_].cones_, colour[0], A, g->isSolid() ? GL_FILL : GL_LINE); break; // Vector - center = data[0], vector = data[1] case (Glyph::VectorGlyph): @@ -304,7 +304,7 @@ void RenderEngine::renderGlyphs(Model *source) // Move to endpoint A.applyTranslationZ(rij*arrowBodyLength); A.applyScaling(0.2, 0.2, rij*arrowHeadLength/arrowBodyLength); - renderPrimitive(RenderEngine::GlyphObject, primitives_[Q_].cones_, colour[0], A, GL_LINE); + renderPrimitive(RenderEngine::GlyphObject, primitives_[set_].cones_, colour[0], A, GL_LINE); break; } } @@ -318,7 +318,7 @@ void RenderEngine::renderGlyphs(Model *source) } // Render text glyphs -void RenderEngine::renderTextGlyphs(Model *source, TCanvas *canvas) +void RenderEngine::renderTextGlyphs(Model *source) { Vec3 r1, r2; Vec4 screenr; @@ -339,7 +339,7 @@ void RenderEngine::renderTextGlyphs(Model *source, TCanvas *canvas) // Grab first coordinate (always used) r1 = g->data(0)->vector(); - if (g->type() == Glyph::TextGlyph) renderTextPrimitive(r1.x, canvas->contextHeight()-r1.y, g->text()); + if (g->type() == Glyph::TextGlyph) renderTextPrimitive(r1.x, r1.y, g->text()); else if (g->type() == Glyph::Text3DGlyph) { r2 = source->modelToWorld(r1, &screenr); diff --git a/src/render/engine_model.cpp b/src/render/engine_model.cpp index 933f803b4..d0228f624 100644 --- a/src/render/engine_model.cpp +++ b/src/render/engine_model.cpp @@ -27,15 +27,16 @@ #include "base/pattern.h" #include "gui/tcanvas.uih" #include "base/sysfunc.h" +#include "main/aten.h" +#include "gui/gui.h" // Render bond -void RenderEngine::renderBond(Matrix A, Vec3 vij, Atom *i, Atom::DrawStyle style_i, GLfloat *colour_i, double radius_i, Atom *j, Atom::DrawStyle style_j, GLfloat *colour_j, double radius_j, Bond::BondType bt, double selscale, Bond *b, bool transparentSel, GLfloat *penColour) +void RenderEngine::renderBond(RenderEngine::TriangleObject basicList, RenderEngine::TriangleObject selectionList, Matrix A, Vec3< double > vij, Atom* i, Atom::DrawStyle style_i, GLfloat* colour_i, double radius_i, Atom* j, Atom::DrawStyle style_j, GLfloat* colour_j, double radius_j, Bond::BondType bt, double selscale, Bond* b, bool transparentSel, GLfloat* penColour) { double dvisible, selvisible, factor, rij, phi; Vec3 ri, rj, localx, localy, localz, stickpos, dx, normz; GLfloat alpha_i, alpha_j; - Primitive *sticks[2] = { &stickLines_, &stickSelectedLines_ }; - int stickList; + int lineList, baseLineList = (basicList == RenderEngine::BasicObject ? NormalLineObject : NormalGuiLineObject); Matrix B; // Store copies of alpha values @@ -99,51 +100,51 @@ void RenderEngine::renderBond(Matrix A, Vec3 vij, Atom *i, Atom::DrawSty switch (style_i) { case (Atom::StickStyle): - if (!rebuildSticks_) break; + if ((!rebuildSticks_) && (baseLineList == 0)) break; // First vertex is at 0,0,0 (i.e. translation elements of A). Second is vij * (0,0,1) stickpos = A * Vec3(0.0,0.0,1.0); // Determine how many sticks to draw (bond multiplicity : aromatic still counts as one bond) - stickList = i->isSelected() ? 1 : 0; + lineList = baseLineList + (i->isSelected() ? 1 : 0); switch (bt) { case (Bond::Double): dx = A.rotateVector(prefs.atomStyleRadius(Atom::StickStyle)*0.5,0.0,0.0); - sticks[stickList]->defineVertex(A[12]+dx.x, A[13]+dx.y, A[14]+dx.z, 0.0,0.0,1.0, colour_i, FALSE); - sticks[stickList]->defineVertex(stickpos.x+dx.x, stickpos.y+dx.y, stickpos.z+dx.z, 0.0,0.0,1.0, colour_i, FALSE); - sticks[stickList]->defineVertex(A[12]-dx.x, A[13]-dx.y, A[14]-dx.z, 0.0,0.0,1.0, colour_i, FALSE); - sticks[stickList]->defineVertex(stickpos.x-dx.x, stickpos.y-dx.y, stickpos.z-dx.z, 0.0,0.0,1.0, colour_i, FALSE); + linePrimitives_[lineList].defineVertex(A[12]+dx.x, A[13]+dx.y, A[14]+dx.z, 0.0,0.0,1.0, colour_i, FALSE); + linePrimitives_[lineList].defineVertex(stickpos.x+dx.x, stickpos.y+dx.y, stickpos.z+dx.z, 0.0,0.0,1.0, colour_i, FALSE); + linePrimitives_[lineList].defineVertex(A[12]-dx.x, A[13]-dx.y, A[14]-dx.z, 0.0,0.0,1.0, colour_i, FALSE); + linePrimitives_[lineList].defineVertex(stickpos.x-dx.x, stickpos.y-dx.y, stickpos.z-dx.z, 0.0,0.0,1.0, colour_i, FALSE); break; case (Bond::Triple): dx = A.rotateVector(prefs.atomStyleRadius(Atom::StickStyle),0.0,0.0); - sticks[stickList]->defineVertex(A[12], A[13], A[14], 0.0,0.0,1.0, colour_i, FALSE); - sticks[stickList]->defineVertex(stickpos.x, stickpos.y, stickpos.z, 0.0,0.0,1.0, colour_i, FALSE); - sticks[stickList]->defineVertex(A[12]+dx.x, A[13]+dx.y, A[14]+dx.z, 0.0,0.0,1.0, colour_i, FALSE); - sticks[stickList]->defineVertex(stickpos.x+dx.x, stickpos.y+dx.y, stickpos.z+dx.z, 0.0,0.0,1.0, colour_i, FALSE); - sticks[stickList]->defineVertex(A[12]-dx.x, A[13]-dx.y, A[14]-dx.z, 0.0,0.0,1.0, colour_i, FALSE); - sticks[stickList]->defineVertex(stickpos.x-dx.x, stickpos.y-dx.y, stickpos.z-dx.z, 0.0,0.0,1.0, colour_i, FALSE); + linePrimitives_[lineList].defineVertex(A[12], A[13], A[14], 0.0,0.0,1.0, colour_i, FALSE); + linePrimitives_[lineList].defineVertex(stickpos.x, stickpos.y, stickpos.z, 0.0,0.0,1.0, colour_i, FALSE); + linePrimitives_[lineList].defineVertex(A[12]+dx.x, A[13]+dx.y, A[14]+dx.z, 0.0,0.0,1.0, colour_i, FALSE); + linePrimitives_[lineList].defineVertex(stickpos.x+dx.x, stickpos.y+dx.y, stickpos.z+dx.z, 0.0,0.0,1.0, colour_i, FALSE); + linePrimitives_[lineList].defineVertex(A[12]-dx.x, A[13]-dx.y, A[14]-dx.z, 0.0,0.0,1.0, colour_i, FALSE); + linePrimitives_[lineList].defineVertex(stickpos.x-dx.x, stickpos.y-dx.y, stickpos.z-dx.z, 0.0,0.0,1.0, colour_i, FALSE); break; default: - sticks[stickList]->defineVertex(A[12], A[13], A[14], 0.0,0.0,1.0, colour_i, FALSE); - sticks[stickList]->defineVertex(stickpos.x, stickpos.y, stickpos.z, 0.0,0.0,1.0, colour_i, FALSE); + linePrimitives_[lineList].defineVertex(A[12], A[13], A[14], 0.0,0.0,1.0, colour_i, FALSE); + linePrimitives_[lineList].defineVertex(stickpos.x, stickpos.y, stickpos.z, 0.0,0.0,1.0, colour_i, FALSE); break; } break; case (Atom::TubeStyle): - renderPrimitive(RenderEngine::BasicObject, primitives_[Q_].bonds_[style_i][bt], colour_i, A); + renderPrimitive(basicList, primitives_[set_].bonds_[style_i][bt], colour_i, A); if (i->isSelected() && (selvisible > 0.0)) { if (transparentSel) { colour_i[3] = 0.5f; - renderPrimitive(RenderEngine::AtomSelectionObject, primitives_[Q_].selectedBonds_[style_i][bt], colour_i, A); + renderPrimitive(selectionList, primitives_[set_].selectedBonds_[style_i][bt], colour_i, A); colour_i[3] = alpha_i; } - else renderPrimitive(RenderEngine::AtomSelectionObject, primitives_[Q_].selectedBonds_[style_i][bt], penColour, A, GL_LINE); + else renderPrimitive(selectionList, primitives_[set_].selectedBonds_[style_i][bt], penColour, A, GL_LINE); } break; case (Atom::SphereStyle): case (Atom::ScaledStyle): - renderPrimitive(RenderEngine::BasicObject, primitives_[Q_].bonds_[style_i][bt], colour_i, A); + renderPrimitive(basicList, primitives_[set_].bonds_[style_i][bt], colour_i, A); if (i->isSelected() && (selvisible > 0.0)) { // Move to edge of selected atom and apply selection bond scaling @@ -152,10 +153,10 @@ void RenderEngine::renderBond(Matrix A, Vec3 vij, Atom *i, Atom::DrawSty if (transparentSel) { colour_i[3] = 0.5f; - renderPrimitive(RenderEngine::AtomSelectionObject, primitives_[Q_].selectedBonds_[style_i][bt], colour_i, A); + renderPrimitive(selectionList, primitives_[set_].selectedBonds_[style_i][bt], colour_i, A); colour_i[3] = alpha_i; } - else renderPrimitive(RenderEngine::AtomSelectionObject, primitives_[Q_].selectedBonds_[style_i][bt], penColour, A, GL_LINE); + else renderPrimitive(selectionList, primitives_[set_].selectedBonds_[style_i][bt], penColour, A, GL_LINE); } break; } @@ -164,51 +165,51 @@ void RenderEngine::renderBond(Matrix A, Vec3 vij, Atom *i, Atom::DrawSty switch (style_j) { case (Atom::StickStyle): - if (!rebuildSticks_) break; + if ((!rebuildSticks_) && (baseLineList == 0)) break; // First vertex is *still* at 0,0,0 (i.e. translation elements of A). Second is vij * (0,0,1) stickpos = B * Vec3(0.0,0.0,1.0); - stickList = j->isSelected() ? 1 : 0; + lineList = baseLineList + (j->isSelected() ? 1 : 0); switch (bt) { case (Bond::Double): dx = B.rotateVector(prefs.atomStyleRadius(Atom::StickStyle)*0.5,0.0,0.0); - sticks[stickList]->defineVertex(B[12]+dx.x, B[13]+dx.y, B[14]+dx.z, 0.0,0.0,1.0, colour_j, FALSE); - sticks[stickList]->defineVertex(stickpos.x+dx.x, stickpos.y+dx.y, stickpos.z+dx.z, 0.0,0.0,1.0, colour_j, FALSE); - sticks[stickList]->defineVertex(B[12]-dx.x, B[13]-dx.y, B[14]-dx.z, 0.0,0.0,1.0, colour_j, FALSE); - sticks[stickList]->defineVertex(stickpos.x-dx.x, stickpos.y-dx.y, stickpos.z-dx.z, 0.0,0.0,1.0, colour_j, FALSE); + linePrimitives_[lineList].defineVertex(B[12]+dx.x, B[13]+dx.y, B[14]+dx.z, 0.0,0.0,1.0, colour_j, FALSE); + linePrimitives_[lineList].defineVertex(stickpos.x+dx.x, stickpos.y+dx.y, stickpos.z+dx.z, 0.0,0.0,1.0, colour_j, FALSE); + linePrimitives_[lineList].defineVertex(B[12]-dx.x, B[13]-dx.y, B[14]-dx.z, 0.0,0.0,1.0, colour_j, FALSE); + linePrimitives_[lineList].defineVertex(stickpos.x-dx.x, stickpos.y-dx.y, stickpos.z-dx.z, 0.0,0.0,1.0, colour_j, FALSE); break; case (Bond::Triple): dx = B.rotateVector(prefs.atomStyleRadius(Atom::StickStyle),0.0,0.0); - sticks[stickList]->defineVertex(B[12], B[13], B[14], 0.0,0.0,1.0, colour_j, FALSE); - sticks[stickList]->defineVertex(stickpos.x, stickpos.y, stickpos.z, 0.0,0.0,1.0, colour_j, FALSE); - sticks[stickList]->defineVertex(B[12]+dx.x, B[13]+dx.y, B[14]+dx.z, 0.0,0.0,1.0, colour_j, FALSE); - sticks[stickList]->defineVertex(stickpos.x+dx.x, stickpos.y+dx.y, stickpos.z+dx.z, 0.0,0.0,1.0, colour_j, FALSE); - sticks[stickList]->defineVertex(B[12]-dx.x, B[13]-dx.y, B[14]-dx.z, 0.0,0.0,1.0, colour_j, FALSE); - sticks[stickList]->defineVertex(stickpos.x-dx.x, stickpos.y-dx.y, stickpos.z-dx.z, 0.0,0.0,1.0, colour_j, FALSE); + linePrimitives_[lineList].defineVertex(B[12], B[13], B[14], 0.0,0.0,1.0, colour_j, FALSE); + linePrimitives_[lineList].defineVertex(stickpos.x, stickpos.y, stickpos.z, 0.0,0.0,1.0, colour_j, FALSE); + linePrimitives_[lineList].defineVertex(B[12]+dx.x, B[13]+dx.y, B[14]+dx.z, 0.0,0.0,1.0, colour_j, FALSE); + linePrimitives_[lineList].defineVertex(stickpos.x+dx.x, stickpos.y+dx.y, stickpos.z+dx.z, 0.0,0.0,1.0, colour_j, FALSE); + linePrimitives_[lineList].defineVertex(B[12]-dx.x, B[13]-dx.y, B[14]-dx.z, 0.0,0.0,1.0, colour_j, FALSE); + linePrimitives_[lineList].defineVertex(stickpos.x-dx.x, stickpos.y-dx.y, stickpos.z-dx.z, 0.0,0.0,1.0, colour_j, FALSE); break; default: - sticks[stickList]->defineVertex(B[12], B[13], B[14], 0.0,0.0,1.0, colour_j, FALSE); - sticks[stickList]->defineVertex(stickpos.x, stickpos.y, stickpos.z, 0.0,0.0,1.0, colour_j, FALSE); + linePrimitives_[lineList].defineVertex(B[12], B[13], B[14], 0.0,0.0,1.0, colour_j, FALSE); + linePrimitives_[lineList].defineVertex(stickpos.x, stickpos.y, stickpos.z, 0.0,0.0,1.0, colour_j, FALSE); break; } break; case (Atom::TubeStyle): - renderPrimitive(RenderEngine::BasicObject, primitives_[Q_].bonds_[style_j][bt], colour_j, B); + renderPrimitive(basicList, primitives_[set_].bonds_[style_j][bt], colour_j, B); if (j->isSelected()) { B.applyTranslationZ((selscale*radius_j-radius_j) / dvisible); if (transparentSel) { colour_j[3] = 0.5f; - renderPrimitive(RenderEngine::AtomSelectionObject, primitives_[Q_].selectedBonds_[style_j][bt], colour_j, B); + renderPrimitive(selectionList, primitives_[set_].selectedBonds_[style_j][bt], colour_j, B); colour_j[3] = alpha_j; } - else renderPrimitive(RenderEngine::AtomSelectionObject, primitives_[Q_].selectedBonds_[style_j][bt], penColour, B, GL_LINE); + else renderPrimitive(selectionList, primitives_[set_].selectedBonds_[style_j][bt], penColour, B, GL_LINE); } break; case (Atom::SphereStyle): case (Atom::ScaledStyle): - renderPrimitive(RenderEngine::BasicObject, primitives_[Q_].bonds_[style_j][bt], colour_j, B); + renderPrimitive(basicList, primitives_[set_].bonds_[style_j][bt], colour_j, B); if (j->isSelected() && (selvisible > 0.0)) { B.applyTranslationZ((selscale*radius_j-radius_j) / dvisible); @@ -216,19 +217,195 @@ void RenderEngine::renderBond(Matrix A, Vec3 vij, Atom *i, Atom::DrawSty if (transparentSel) { colour_j[3] = 0.5f; - renderPrimitive(RenderEngine::AtomSelectionObject, primitives_[Q_].selectedBonds_[style_j][bt], colour_j, B); + renderPrimitive(selectionList, primitives_[set_].selectedBonds_[style_j][bt], colour_j, B); colour_j[3] = alpha_j; } - else renderPrimitive(RenderEngine::AtomSelectionObject, primitives_[Q_].selectedBonds_[style_j][bt], penColour, B, GL_LINE); + else renderPrimitive(selectionList, primitives_[set_].selectedBonds_[style_j][bt], penColour, B, GL_LINE); } break; } } -// Render basic model information (atoms, bonds, labels, and glyphs) -void RenderEngine::renderModel(Model *source, Matrix basetransform) +// Render full Model +void RenderEngine::renderModel(Model *source, bool currentModel, bool renderType) { msg.enter("RenderEngine::renderModel"); + GLfloat colour[4]; + + // Valid pointer passed? + if (source == NULL) + { + msg.exit("RenderEngine::renderModel"); + return; + } + msg.print(Messenger::GL, " --> RENDERING BEGIN : source model pointer = %p, renderpoint = %d\n", source, source->changeLog.log(Log::Total)); + + // If this is a trajectory frame, check its ID against the last one rendered + if (source->parent() != NULL) + { + displayFrameId_ = source->parent()->trajectoryFrameIndex(); + msg.print(Messenger::GL, " --> Source model is a trajectory frame - index = %i\n", displayFrameId_); + } + + // Set initial transformation matrix, including any translation occurring from cell... + setTransformationMatrix(source->modelViewMatrix(), source->cell()->centre()); + + // Set target matrix mode and reset it, and set colour mode + glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); + glEnable(GL_COLOR_MATERIAL); + + // Grab model-specific viewport + GLint *vp = source->viewportMatrix(); + + // Render rotation globe in small viewport in lower right-hand corner + if (prefs.viewRotationGlobe()) + { + int n = prefs.globeSize(); + if (aten.nVisibleModels() > 2) n /= 2; + glViewport(vp[0]+vp[2]-n,vp[1],n,n); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(-1.0, 1.0, -1.0, 1.0, -10.0, 10.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + Matrix A = modelTransformationMatrix_; + A.removeTranslationAndScaling(); + A[14] = -1.2; + glMultMatrixd(A.matrix()); + prefs.copyColour(Prefs::GlobeColour, colour); + glColor4fv(colour); + primitives_[set_].rotationGlobe_.sendToGL(); + prefs.copyColour(Prefs::GlobeAxesColour, colour); + glColor4fv(colour); + primitives_[set_].rotationGlobeAxes_.sendToGL(); + } + + // Prepare for model rendering + glViewport(vp[0], vp[1], vp[2], vp[3]); + glMatrixMode(GL_PROJECTION); + glLoadMatrixd(source->modelProjectionMatrix().matrix()); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + // Clear the necessary triangle lists (i.e. only those for which the content may have changed) + // By default, regenerate all lists (the case if the source model pointer has changed) + + int n; + for (n=0; nchangeLog); + if (!redobasic) redobasic = !lastLog_.isSame(Log::Structure,source->changeLog); + + // If the model style has changed, need to rerender both basic and selection lists. Otherwise, depends on other logs + if (redobasic || (!lastLog_.isSame(Log::Style,source->changeLog))) + { + activePrimitiveLists_[RenderEngine::BasicObject] = TRUE; + activePrimitiveLists_[RenderEngine::AtomSelectionObject] = TRUE; + } + else + { + activePrimitiveLists_[RenderEngine::BasicObject] = FALSE; + activePrimitiveLists_[RenderEngine::AtomSelectionObject] = !lastLog_.isSame(Log::Selection, source->changeLog); + } + + // Stick style primitives + if (redobasic || (!lastLog_.isSame(Log::Style,source->changeLog)) || (!lastLog_.isSame(Log::Selection,source->changeLog))) + { + linePrimitives_[RenderEngine::NormalLineObject].forgetAll(); + linePrimitives_[RenderEngine::SelectedLineObject].forgetAll(); + rebuildSticks_ = TRUE; + } + else rebuildSticks_ = FALSE; + + // Glyphs must be redone on basic change (since they may follow atom coordinates + if (redobasic || !lastLog_.isSame(Log::Glyphs, source->changeLog)) activePrimitiveLists_[RenderEngine::GlyphObject] = TRUE; + else activePrimitiveLists_[RenderEngine::GlyphObject] = FALSE; + + // Grids only depend on their own log + if (!lastLog_.isSame(Log::Grids, source->changeLog)) activePrimitiveLists_[RenderEngine::GridObject] = TRUE; + } + else + { + linePrimitives_[RenderEngine::NormalLineObject].forgetAll(); + linePrimitives_[RenderEngine::SelectedLineObject].forgetAll(); + rebuildSticks_ = TRUE; + } + + // Always clear GUI line primitives + linePrimitives_[RenderEngine::NormalGuiLineObject].forgetAll(); + linePrimitives_[RenderEngine::SelectedGuiLineObject].forgetAll(); + + // Clear flagged lists + for (n=0; nchangeLog; + + // Render additional data for active model + if ((renderType == OnscreenScene) && currentModel && gui.exists()) + { + // Render embellshments for current UserAction + renderUserActions(source); + // Render extras arising from open tool windows (current model only) + renderWindowExtras(source); + } + + // If the stick primitives were regenerated, need to create a new instance (after popping the old one) + if (rebuildSticks_) for (n = 0; n pos, v, ijk, r1, r2, r3, r4; Vec4 screenr; - Matrix atomtransform, A, B; + Matrix atomTransform, A, B; Refitem *rb; Refitem *ra; Atom::DrawStyle style_i, style_j, globalstyle; Prefs::ColouringScheme scheme; ForcefieldAtom *ffa; + // Set target lists based on whether this is a fragment + RenderEngine::TriangleObject basicList = (isFragment ? GuiObject : BasicObject); + RenderEngine::TriangleObject selectionList = (isFragment ? GuiObject : AtomSelectionObject); + int baseLineList = (isFragment ? NormalGuiLineObject : NormalLineObject); + // Grab global style values and atom radii scheme = prefs.colourScheme(); globalstyle = prefs.renderStyle(); @@ -266,8 +448,8 @@ void RenderEngine::renderModel(Model *source, Matrix basetransform) pos = i->r(); // Move to local atom position - atomtransform = basetransform; - atomtransform.applyTranslation(pos.x, pos.y, pos.z); + atomTransform = baseTransform; + atomTransform.applyTranslation(pos.x, pos.y, pos.z); // Select colour if (i->isPositionFixed()) prefs.copyColour(Prefs::FixedAtomColour, colour_i); @@ -302,26 +484,26 @@ void RenderEngine::renderModel(Model *source, Matrix basetransform) // Only need to draw something if the atom has no bonds if (i->nBonds() == 0) { - if (i->isSelected()) stickSelectedLines_.plotCross(0.5, atomtransform, colour_i); - else stickLines_.plotCross(0.5, atomtransform, colour_i); + if (i->isSelected()) linePrimitives_[baseLineList+1].plotCross(0.5, atomTransform, colour_i); + else linePrimitives_[baseLineList].plotCross(0.5, atomTransform, colour_i); } } else { radius_i = aradius[style_i]; if (style_i == Atom::ScaledStyle) radius_i *= elements().el[i->element()].atomicRadius; - A = atomtransform; + A = atomTransform; A.applyScaling(radius_i,radius_i,radius_i); - renderPrimitive(RenderEngine::BasicObject, primitives_[Q_].atom_, colour_i, A); + renderPrimitive(basicList, primitives_[set_].atom_, colour_i, A); if (i->isSelected()) { if (transparentSel) { colour_i[3] = 0.5f; - renderPrimitive(RenderEngine::AtomSelectionObject, primitives_[Q_].selectedAtom_, colour_i, A); + renderPrimitive(selectionList, primitives_[set_].selectedAtom_, colour_i, A); colour_i[3] = alpha_i; } - else renderPrimitive(RenderEngine::AtomSelectionObject, primitives_[Q_].selectedAtom_, penColour, A, GL_LINE); + else renderPrimitive(selectionList, primitives_[set_].selectedAtom_, penColour, A, GL_LINE); } } @@ -370,7 +552,7 @@ void RenderEngine::renderModel(Model *source, Matrix basetransform) v = source->cell()->mimVector(i, j); // Render bond - renderBond(atomtransform, v, i, style_i, colour_i, radius_i, j, style_j, colour_j, radius_j, rb->item->type(), selscale, rb->item, transparentSel, penColour); + renderBond(basicList, selectionList, atomTransform, v, i, style_i, colour_i, radius_i, j, style_j, colour_j, radius_j, rb->item->type(), selscale, rb->item, transparentSel, penColour); } } @@ -440,24 +622,24 @@ void RenderEngine::renderModel(Model *source, Matrix basetransform) mag -= radius_i*0.9; mag *= 0.75; // Construct transformation matrix - atomtransform = basetransform; - atomtransform.applyTranslation(pos.x, pos.y, pos.z); + atomTransform = baseTransform; + atomTransform.applyTranslation(pos.x, pos.y, pos.z); A.setColumn(0, r1*mag, 0.0); A.setColumn(1, r2*mag, 0.0); A.setColumn(2, r3*mag, 0.0); A.setColumn(3, 0.0, 0.0, 0.0, 1.0); - atomtransform *= A; + atomTransform *= A; // Render ring if (prefs.renderDashedAromatics()) { - if (globalstyle == Atom::StickStyle) renderPrimitive(RenderEngine::BasicObject, primitives_[Q_].segmentedLineRings_, colour_i, atomtransform); - else renderPrimitive(RenderEngine::BasicObject, primitives_[Q_].segmentedTubeRings_, colour_i, atomtransform); + if (globalstyle == Atom::StickStyle) renderPrimitive(basicList, primitives_[set_].segmentedLineRings_, colour_i, atomTransform); + else renderPrimitive(basicList, primitives_[set_].segmentedTubeRings_, colour_i, atomTransform); } else { - if (globalstyle == Atom::StickStyle) renderPrimitive(RenderEngine::BasicObject, primitives_[Q_].lineRings_, colour_i, atomtransform); - else renderPrimitive(RenderEngine::BasicObject, primitives_[Q_].tubeRings_, colour_i, atomtransform); + if (globalstyle == Atom::StickStyle) renderPrimitive(basicList, primitives_[set_].lineRings_, colour_i, atomTransform); + else renderPrimitive(basicList, primitives_[set_].tubeRings_, colour_i, atomTransform); } id_i += p->nAtoms(); @@ -553,32 +735,32 @@ void RenderEngine::renderModel(Model *source, Matrix basetransform) delta = (mag - 6.0*dotradius) < 0.0 ? -1.0 : mag / 4.0; // The matrix 'atomtransform' will only contain the translation. Scaling will be applied to its copy in 'A' - atomtransform = basetransform; - atomtransform.applyTranslation(pos); + atomTransform = baseTransform; + atomTransform.applyTranslation(pos); // H-bond dot 1 - if (delta > 0.0) atomtransform.applyTranslation(r1*delta); - A = atomtransform; + if (delta > 0.0) atomTransform.applyTranslation(r1*delta); + A = atomTransform; A.applyScaling(dotradius, dotradius, dotradius); - renderPrimitive(RenderEngine::BasicObject, primitives_[Q_].spheres_, colour_i, A); + renderPrimitive(basicList, primitives_[set_].spheres_, colour_i, A); // H-bond dot 2 - if (delta > 0.0) atomtransform.applyTranslation(r1*delta); - else atomtransform.applyTranslation(r1*mag*0.5); - A = atomtransform; + if (delta > 0.0) atomTransform.applyTranslation(r1*delta); + else atomTransform.applyTranslation(r1*mag*0.5); + A = atomTransform; A.applyScaling(dotradius, dotradius, dotradius); - renderPrimitive(RenderEngine::BasicObject, primitives_[Q_].spheres_, colour_i, A); + renderPrimitive(basicList, primitives_[set_].spheres_, colour_i, A); // H-bond dot 3 - if (delta > 0.0) atomtransform.applyTranslation(r1*delta); - else atomtransform.applyTranslation(r1*mag*0.5); - A = atomtransform; + if (delta > 0.0) atomTransform.applyTranslation(r1*delta); + else atomTransform.applyTranslation(r1*mag*0.5); + A = atomTransform; A.applyScaling(dotradius, dotradius, dotradius); - renderPrimitive(RenderEngine::BasicObject, primitives_[Q_].spheres_, colour_i, A); + renderPrimitive(basicList, primitives_[set_].spheres_, colour_i, A); } } } - msg.exit("RenderEngine::renderModel"); + msg.exit("RenderEngine::renderAtomsAndBonds"); } // Render model cell @@ -598,7 +780,7 @@ void RenderEngine::renderCell(Model *source) glMultMatrixd(A.matrix()); // Draw a wire cube for the cell - primitives_[Q_].wireCube_.sendToGL(); + primitives_[set_].wireCube_.sendToGL(); // Copy colour for axes, move to llh corner, and draw them prefs.copyColour(Prefs::UnitCellAxesColour, colour); @@ -606,7 +788,7 @@ void RenderEngine::renderCell(Model *source) glTranslated(-0.5, -0.5, -0.5); Vec3 v = source->cell()->lengths(); glScaled(1.0 / v.x, 1.0 / v.y, 1.0 / v.z); - primitives_[Q_].cellAxes_.sendToGL(); + primitives_[set_].cellAxes_.sendToGL(); msg.exit("RenderEngine::renderCell"); } @@ -676,7 +858,7 @@ void RenderEngine::renderGrids(Model *source) A.columnMultiply(0, g->nPoints().x); A.columnMultiply(1, g->nPoints().y); A.columnMultiply(2, g->nPoints().z); - renderPrimitive(RenderEngine::GridObject, primitives_[Q_].originCubes_, textcolour, A, GL_LINE, 1.0); + renderPrimitive(RenderEngine::GridObject, primitives_[set_].originCubes_, textcolour, A, GL_LINE, 1.0); } } msg.exit("RenderEngine::renderGrids"); diff --git a/src/render/engine_primitives.cpp b/src/render/engine_primitives.cpp new file mode 100644 index 000000000..0fca6fea5 --- /dev/null +++ b/src/render/engine_primitives.cpp @@ -0,0 +1,311 @@ +/* + *** Rendering Engine Primitives + *** src/render/engine_primitives.cpp + Copyright T. Youngs 2007-2012 + + This file is part of Aten. + + Aten 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 3 of the License, or + (at your option) any later version. + + Aten 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 Aten. If not, see . +*/ + +#include "render/engine.h" +#include "base/messenger.h" +#include "classes/prefs.h" +#include "classes/forcefieldatom.h" +#include "model/model.h" +#include "gui/gui.h" +#include "gui/tcanvas.uih" +#include "main/aten.h" +#include + +// Constructor +RenderPrimitives::RenderPrimitives() +{ + // Primitives + requestedQuality_ = -1; + currentQuality_ = -1; + stackSize_ = 0; + + // Set names of primitives (for bug-tracking) + atom_.setName("Atom"); + selectedAtom_.setName("SelectedAtom"); + bonds_[Atom::TubeStyle][Bond::Single].setName("Bond[Tube,Single]"); + bonds_[Atom::SphereStyle][Bond::Single].setName("Bond[Sphere,Single]"); + bonds_[Atom::ScaledStyle][Bond::Single].setName("Bond[Scaled,Single]"); + selectedBonds_[Atom::TubeStyle][Bond::Single].setName("SelectedBond[Tube,Single]"); + selectedBonds_[Atom::SphereStyle][Bond::Single].setName("SelectedBond[Sphere,Single]"); + selectedBonds_[Atom::ScaledStyle][Bond::Single].setName("SelectedBond[Scaled,Single]"); + bonds_[Atom::TubeStyle][Bond::Double].setName("Bond[Tube,Double]"); + bonds_[Atom::SphereStyle][Bond::Double].setName("Bond[Sphere,Double]"); + bonds_[Atom::ScaledStyle][Bond::Double].setName("Bond[Scaled,Double]"); + selectedBonds_[Atom::TubeStyle][Bond::Double].setName("SelectedBond[Tube,Double]"); + selectedBonds_[Atom::SphereStyle][Bond::Double].setName("SelectedBond[Sphere,Double]"); + selectedBonds_[Atom::ScaledStyle][Bond::Double].setName("SelectedBond[Scaled,Double]"); + bonds_[Atom::TubeStyle][Bond::Triple].setName("Bond[Tube,Triple]"); + bonds_[Atom::SphereStyle][Bond::Triple].setName("Bond[Sphere,Triple]"); + bonds_[Atom::ScaledStyle][Bond::Triple].setName("Bond[Scaled,Triple]"); + selectedBonds_[Atom::TubeStyle][Bond::Triple].setName("SelectedBond[Tube,Triple]"); + selectedBonds_[Atom::SphereStyle][Bond::Triple].setName("SelectedBond[Sphere,Triple]"); + selectedBonds_[Atom::ScaledStyle][Bond::Triple].setName("SelectedBond[Scaled,Triple]"); + bonds_[Atom::TubeStyle][Bond::Aromatic].setName("Bond[Tube,Aromatic]"); + bonds_[Atom::SphereStyle][Bond::Aromatic].setName("Bond[Sphere,Aromatic]"); + bonds_[Atom::ScaledStyle][Bond::Aromatic].setName("Bond[Scaled,Aromatic]"); + selectedBonds_[Atom::TubeStyle][Bond::Aromatic].setName("SelectedBond[Tube,Aromatic]"); + selectedBonds_[Atom::SphereStyle][Bond::Aromatic].setName("SelectedBond[Sphere,Aromatic]"); + selectedBonds_[Atom::ScaledStyle][Bond::Aromatic].setName("SelectedBond[Scaled,Aromatic]"); + cubes_.setName("Cubes"); + originCubes_.setName("OriginCubes"); + spheres_.setName("Spheres"); + cylinders_.setName("Cylinders"); + cones_.setName("Cones"); + tubeRings_.setName("TubeRings"); + segmentedTubeRings_.setName("SegmentedTubeRings"); + lineRings_.setName("LineRings"); + segmentedLineRings_.setName("SegmentedLineRings"); + wireCube_.setName("WireCube"); + crossedCube_.setName("CrossedCube"); + cellAxes_.setName("CellAxes"); + rotationGlobe_.setName("RotationGlobe"); + rotationGlobeAxes_.setName("RotationGlobeAxes"); +} + +// Destructor +RenderPrimitives::~RenderPrimitives() +{ +} + +// Set the desired primitive quality +void RenderPrimitives::setQuality(int quality) +{ + requestedQuality_ = quality; +} + +// Return current primitive instance stacksize +int RenderPrimitives::stackSize() +{ + return stackSize_; +} + +// (Re)Generate primitives +void RenderPrimitives::recreatePrimitives(bool force) +{ + msg.enter("RenderPrimitives::recreatePrimitives"); + double radius, lodratio, aradius[Atom::nDrawStyles], bradius[Atom::nDrawStyles], selscale; + int n, m, lod, nstacks, nslices; + + // If current quality is the same as the requested quality, do nothing + if ((requestedQuality_ == currentQuality_) && (!force)) + { + msg.exit("RenderPrimitives::recreatePrimitives"); + return; + } + + currentQuality_ = requestedQuality_; + + // Clear old primitive groups + atom_.clear(); + selectedAtom_.clear(); + for (n=0; n. +*/ + +#ifndef ATEN_RENDERENGINEPRIMITIVES_H +#define ATEN_RENDERENGINEPRIMITIVES_H + +#ifdef _WIN32 +#include +#include +#include "glext.h" +#endif +// #include "render/triangles.h" +#include "render/primitivegroup.h" +#include "render/primitiveinfo.h" +// #include "render/gridprimitive.h" +// #include "render/textprimitive.h" +// #include "base/log.h" +// #include "templates/vector3.h" +#include "base/atom.h" +#include "base/bond.h" +// #include "base/matrix.h" + +// Forward declarations +// class Model; +// class TCanvas; +// class RenderEngine; +class QGLContext; + +// Rendering Primitives +class RenderPrimitives +{ + public: + // Constructor / Destructor + RenderPrimitives(); + ~RenderPrimitives(); + // Declare RenderEngine as a Friend + friend class RenderEngine; + + + /* + // Primitives + */ + private: + // Quality setting that primitives should be next generated at + int requestedQuality_; + // Quality setting that primitives were last generated at (if at all) + int currentQuality_; + // Stack size counter + int stackSize_; + // Atom + PrimitiveGroup atom_; + // Selected atom styles + PrimitiveGroup selectedAtom_; + // Bond styles + PrimitiveGroup bonds_[Atom::nDrawStyles][Bond::nBondTypes]; + // Selected bond styles + PrimitiveGroup selectedBonds_[Atom::nDrawStyles][Bond::nBondTypes]; + // Rings + PrimitiveGroup lineRings_, segmentedLineRings_, tubeRings_, segmentedTubeRings_; + // Primitive objects + PrimitiveGroup cubes_, originCubes_, spheres_, cylinders_, cones_; + // One-off objects + Primitive wireCube_, crossedCube_, cellAxes_, rotationGlobe_, rotationGlobeAxes_; + + public: + // Set the desired primitive quality + void setQuality(int quality); + // Return current primitive instance stacksize + int stackSize(); + // (Re)Generate primitive vertex arrays (if necessary) + void recreatePrimitives(bool force = FALSE); + // Push instance layer for all primitives + void pushInstance(const QGLContext *context, bool forceRegenerate = FALSE); + // Pop last instance layer + void popInstance(const QGLContext *context); +}; + +#endif diff --git a/src/render/engine_useractions.cpp b/src/render/engine_useractions.cpp index 7ad262e35..2d46646f1 100644 --- a/src/render/engine_useractions.cpp +++ b/src/render/engine_useractions.cpp @@ -19,15 +19,92 @@ along with Aten. If not, see . */ +#include "main/aten.h" #include "render/engine.h" #include "model/model.h" #include "model/fragment.h" #include "gui/gui.h" #include "gui/tcanvas.uih" #include "gui/fragments.h" +#include "base/sysfunc.h" + +// Render active mode embellishments +void RenderEngine::renderActiveModes(QPainter& painter, int width, int height) +{ + // Variables + Dnchar text; + QColor color; + QBrush nobrush(Qt::NoBrush); + GLfloat colour[4]; + Vec3 r, mouseLast, mouseDown; + int i, skip, n; + double dx, halfw; + + // Active mode embellishments + prefs.copyColour(Prefs::BackgroundColour, colour); + color.setRgbF(1.0-colour[0], 1.0-colour[1], 1.0-colour[2], 1.0); + painter.setPen(color); + painter.setPen(Qt::DashLine); + painter.setBrush(nobrush); + switch (gui.mainCanvas()->activeMode()) + { + case (UserAction::NoAction): + break; + // Only selection mode where we draw a selection box + case (UserAction::SelectAction): + mouseLast = gui.mainCanvas()->rMouseLast(); + mouseDown = gui.mainCanvas()->rMouseDown(); + painter.drawRect(mouseDown.x, mouseDown.y, mouseLast.x-mouseDown.x, mouseLast.y-mouseDown.y); + break; + default: + break; + } + + // Passive mode embellishments + Model *modelTarget = aten.currentModel(); + if (modelTarget != NULL) switch (gui.mainCanvas()->selectedMode()) + { + // Draw on distance ruler for drawing modes + case (UserAction::DrawAtomAction): + case (UserAction::DrawChainAction): + // Get pixel 'length' in Angstrom terms at current draw depth + r = modelTarget->screenToModel(width/2+10, contextHeight_/2, gui.mainCanvas()->currentDrawDepth()); + r -= modelTarget->screenToModel(width/2, contextHeight_/2, gui.mainCanvas()->currentDrawDepth()); + dx = 10.0 / r.magnitude(); + + halfw = width / 2.0; + i = int( halfw / dx); + skip = 1; + while ( (i/skip) > 5) + { + skip += (skip == 1 ? 4 : 5); + } + for (n = -i; n <= i; n ++) + { + if ((n%skip) != 0) continue; + painter.drawLine(halfw + n*dx, 20, halfw + n*dx, 10); + painter.drawLine(halfw + n*dx, contextHeight_-20, halfw + n*dx, contextHeight_-10); + if (n != i) + { + painter.drawLine(halfw + (n+0.5*skip)*dx, contextHeight_-15, halfw + (n+0.5*skip)*dx, contextHeight_-10); + painter.drawLine(halfw + (n+0.5*skip)*dx, contextHeight_-15, halfw + (n+0.5*skip)*dx, contextHeight_-10); + } + } + painter.drawLine(halfw - i*dx, 10, halfw + i*dx, 10); + painter.drawLine(halfw - i*dx, contextHeight_-10, halfw + i*dx, contextHeight_-10); + for (n = -i; n <= i; n++) + { + if ((n%skip) != 0) continue; + painter.drawText(halfw + n*dx - (n < 0 ? 8 : 3), contextHeight_, itoa(n)); + } + break; + default: + break; + } +} // Render addition elements related to selected/active UserActions -void RenderEngine::renderUserActions(Model *source, TCanvas *canvas) +void RenderEngine::renderUserActions(Model *source) { Matrix A; Atom *i, *j, tempj; @@ -39,6 +116,9 @@ void RenderEngine::renderUserActions(Model *source, TCanvas *canvas) Dnchar text; Fragment *frag; + if (!gui.exists()) return; + TCanvas *canvas = gui.mainCanvas(); + // Draw on the selection highlights (for atoms in the canvas' pickedAtoms list) for (Refitem *ri = canvas->pickedAtoms(); ri != NULL; ri = ri->next) { @@ -52,7 +132,7 @@ void RenderEngine::renderUserActions(Model *source, TCanvas *canvas) radius_i = prefs.atomStyleRadius(style_i); if (style_i == Atom::ScaledStyle) radius_i *= elements().el[i->element()].atomicRadius; A.applyScaling(radius_i, radius_i, radius_i); - renderPrimitive(RenderEngine::MiscObject, primitives_[Q_].selectedAtom_, colour, A, GL_LINE); + renderPrimitive(RenderEngine::GuiObject, primitives_[set_].selectedAtom_, colour, A, GL_LINE); } // Active user actions @@ -122,7 +202,7 @@ void RenderEngine::renderUserActions(Model *source, TCanvas *canvas) A.applyTranslation(pos); // Render new (temporary) bond - renderBond(A, v, i, style_i, colour, radius_i, j, style_j, colour_j, radius_j, bt, prefs.selectionScale()); + renderBond(GuiObject, GuiObject, A, v, i, style_i, colour, radius_i, j, style_j, colour_j, radius_j, bt, prefs.selectionScale()); // Draw text showing distance text.sprintf("r = %f ", v.magnitude()); @@ -149,11 +229,11 @@ void RenderEngine::renderUserActions(Model *source, TCanvas *canvas) A.setIdentity(); A.applyTranslation(pos); // Did we find a valid anchor point? - if (m != NULL) renderModel(m, A); + if (m != NULL) renderAtomsAndBonds(m, A, TRUE); else { prefs.copyColour(Prefs::TextColour, colour); - renderPrimitive(RenderEngine::MiscObject, &primitives_[Q_].crossedCube_, FALSE, colour, A, GL_LINE, 2.0); + renderPrimitive(RenderEngine::GuiObject, &primitives_[set_].crossedCube_, FALSE, colour, A, GL_LINE, 2.0); } } else @@ -162,9 +242,10 @@ void RenderEngine::renderUserActions(Model *source, TCanvas *canvas) // Get drawing point origin, translate to it, and render the stored model if (canvas->activeMode() == UserAction::DrawFragmentAction) pos = source->screenToModel(canvas->rMouseDown().x, canvas->rMouseDown().y, prefs.drawDepth()); else pos = source->screenToModel(canvas->rMouseLast().x, canvas->rMouseLast().y, prefs.drawDepth()); - A.setIdentity();; + A.setIdentity(); A.applyTranslation(pos); - renderModel(frag->orientedModel(), A); + A.print(); + renderAtomsAndBonds(frag->orientedModel(), A, TRUE); } break; } diff --git a/src/render/engine_windows.cpp b/src/render/engine_windows.cpp index c539bf83e..f5a6ff108 100644 --- a/src/render/engine_windows.cpp +++ b/src/render/engine_windows.cpp @@ -87,17 +87,17 @@ void RenderEngine::renderWindowExtras(Model *source) // Move to endpoint A.applyTranslation(0.0,0.0,rij*0.9); A.applyScaling(rij*0.02,rij*0.02,rij*0.1); - renderPrimitive(RenderEngine::MiscObject, primitives_[Q_].cones_, colour, A, GL_LINE); + renderPrimitive(RenderEngine::GuiObject, primitives_[set_].cones_, colour, A, GL_LINE); } else { // Draw cylinder A.applyScaling(rij*0.05,rij*0.05,rij*0.9); - renderPrimitive(RenderEngine::MiscObject, primitives_[Q_].cylinders_, colour, A, GL_FILL); + renderPrimitive(RenderEngine::GuiObject, primitives_[set_].cylinders_, colour, A, GL_FILL); // Move to endpoint A.applyTranslation(0.0,0.0,1.0); A.applyScaling(2.0,2.0,0.1/0.9); - renderPrimitive(RenderEngine::MiscObject, primitives_[Q_].cones_, colour, A, GL_FILL); + renderPrimitive(RenderEngine::GuiObject, primitives_[set_].cones_, colour, A, GL_FILL); } } } @@ -156,7 +156,7 @@ void RenderEngine::renderWindowExtras(Model *source) glPushMatrix(); glTranslated(translate.x, translate.y, translate.z); glScaled(scale.x, scale.y, scale.z); - primitives_[Q_].wireCube_.sendToGL(); + primitives_[set_].wireCube_.sendToGL(); glPopMatrix(); } } @@ -264,7 +264,7 @@ void RenderEngine::renderWindowExtras(Model *source) colour[3] = 0.75; // Partition surfaces will have already been constructed, so just display them - renderPrimitive(RenderEngine::MiscObject, &pd->gridPrimitive().primaryPrimitive(), TRUE, colour, mat, GL_FILL); + renderPrimitive(RenderEngine::GuiObject, &pd->gridPrimitive().primaryPrimitive(), TRUE, colour, mat, GL_FILL); // Increase colour counter ++colcount; diff --git a/src/render/primitive.cpp b/src/render/primitive.cpp index da6853425..c709285e0 100644 --- a/src/render/primitive.cpp +++ b/src/render/primitive.cpp @@ -273,6 +273,12 @@ void Primitive::setNoInstances() useInstances_ = FALSE; } +// Return whether this primitive uses instances +bool Primitive::useInstances() +{ + return useInstances_; +} + // Push instance of primitive void Primitive::pushInstance(const QGLContext *context) { @@ -355,8 +361,18 @@ void Primitive::popInstance(const QGLContext *context) } else glDeleteLists(pi->id(),1); } + instances_.removeLast(); } - instances_.removeLast(); + else printf("Internal Error: Tried to pop an instance for context %p when one didn't exist.\n", context); +} + +// Return context associated to topmost primitive on stack +const QGLContext *Primitive::topContext() +{ + if (!useInstances_) return NULL; + PrimitiveInstance *pi = instances_.last(); + if (pi != NULL) return pi->context(); + else printf("Internal Error: Tried to get context for topmost instance in primitive '%s', but no instances exist.\n", name_.get()); } // Send to OpenGL (i.e. render) diff --git a/src/render/primitive.h b/src/render/primitive.h index e27406f0f..6f4003154 100644 --- a/src/render/primitive.h +++ b/src/render/primitive.h @@ -121,10 +121,14 @@ class Primitive bool colouredVertexData(); // Flag that this primitive should not use instances (rendering will use vertex arrays) void setNoInstances(); + // Return whether this primitive uses instances + bool useInstances(); // Push instance layer from current vertex chunk list void pushInstance(const QGLContext *context); // Pop topmost instance layer void popInstance(const QGLContext *context); + // Return context associated to topmost primitive on stack + const QGLContext *topContext(); // Set name of primitive void setName(const char *s); // Return name of primitive diff --git a/src/render/textprimitive.cpp b/src/render/textprimitive.cpp index f567496ed..a5a019441 100644 --- a/src/render/textprimitive.cpp +++ b/src/render/textprimitive.cpp @@ -95,21 +95,14 @@ void TextPrimitiveChunk::add(int x, int y, const char *text, QChar addChar, bool } // Render all primitives in chunk -void TextPrimitiveChunk::renderAll(QPainter &painter, TCanvas *canvas) +void TextPrimitiveChunk::renderAll(QPainter &painter, int verticalOffset) { - // Grab contextHeight QRect rect; - int height = canvas->contextHeight(); - int pointsize = prefs.labelSize(); - if (prefs.useNiceText()) + for (int n=0; nrenderText(textPrimitives_[n].x(), height-textPrimitives_[n].y()-pointsize, textPrimitives_[n].text()); } /* @@ -139,8 +132,8 @@ void TextPrimitiveList::add(int x, int y, const char *text, QChar addChar, bool } // Render all primitives in list -void TextPrimitiveList::renderAll(QPainter &painter, TCanvas *canvas) +void TextPrimitiveList::renderAll(QPainter &painter, int verticalOffset) { - for (TextPrimitiveChunk *chunk = textPrimitives_.first(); chunk != NULL; chunk = chunk->next) chunk->renderAll(painter, canvas); + for (TextPrimitiveChunk *chunk = textPrimitives_.first(); chunk != NULL; chunk = chunk->next) chunk->renderAll(painter, verticalOffset); } diff --git a/src/render/textprimitive.h b/src/render/textprimitive.h index b01769086..ced3df127 100644 --- a/src/render/textprimitive.h +++ b/src/render/textprimitive.h @@ -79,7 +79,7 @@ class TextPrimitiveChunk // Add primitive to list void add(int x, int y, const char *text, QChar addChar = 0, bool rightAlign = FALSE); // Render all primitives in chunk - void renderAll(QPainter& painter, TCanvas* canvas); + void renderAll(QPainter& painter, int verticalOffset); }; // Text Primitive List @@ -101,7 +101,7 @@ class TextPrimitiveList // Add primitive to list void add(int x, int y, const char *text, QChar addChar = 0, bool rightAlign = FALSE); // Render all primitives in list - void renderAll(QPainter& painter, TCanvas* canvas); + void renderAll(QPainter& painter, int verticalOffset); }; #endif diff --git a/src/render/triangles.cpp b/src/render/triangles.cpp index ef49b7341..67bab5934 100644 --- a/src/render/triangles.cpp +++ b/src/render/triangles.cpp @@ -88,7 +88,7 @@ void TriangleChopper::storeTriangles(PrimitiveInfo* pinfo, Matrix& worldtransfor Primitive *prim = pinfo->primitive(); if (prim == NULL) prim = pinfo->primitive(worldtransform); if (prim->nDefinedVertices() == 0) return; - + // For speed, different loops depending on type of vertexData... if (prim->colouredVertexData()) {