diff --git a/examples/hexgrid.cpp b/examples/hexgrid.cpp index 4153c741..223a1c10 100644 --- a/examples/hexgrid.cpp +++ b/examples/hexgrid.cpp @@ -41,14 +41,15 @@ int main() // Make some dummy data (a sine wave) to make an interesting surface std::vector data(hg.num(), 0.0f); - for (unsigned int ri=0; ri1 + for (unsigned int ri = 0; ri < hg.num(); ++ri) { + data[ri] = 0.05f + 0.05f * std::sin (20.0f * hg.d_x[ri]) * std::sin (10.0f * hg.d_y[ri]) ; // Range 0->1 } // Add a HexGridVisual to display the HexGrid within the sm::Visual scene sm::vec offset = { 0.0f, -0.05f, 0.0f }; auto hgv = std::make_unique>(&hg, offset); v.bindmodel (hgv); + hgv->wireframe (true); hgv->cm.setType (mplot::ColourMapType::Ice); hgv->setScalarData (&data); hgv->hexVisMode = mplot::HexVisMode::HexInterp; // Or sm::HexVisMode::Triangles for a smoother surface plot diff --git a/mplot/VisualBase.h b/mplot/VisualBase.h index 79e85206..6bb78ec1 100644 --- a/mplot/VisualBase.h +++ b/mplot/VisualBase.h @@ -115,7 +115,17 @@ namespace mplot * If true, then turn on the bounding box for the VM about which we are rotating and turn * the others off (ignoring the value of 'showBoundingBoxes') */ - highlightRotationVM + highlightRotationVM, + /*! + * If true, the view of the scene follows a model translation (one of the VisualModels in + * the scene has to be nominated as the 'model to follow'. Useful for top-down views. The + * selected model to follow is in a member attribute followedModel + */ + viewFollowsVMTranslations, + /*! + * The view 'camera' rotates with the selected VM (followedModel) + */ + viewFollowsVMRotations, }; //! Whether to render with perspective or orthographic (or even a cylindrical projection) @@ -270,6 +280,17 @@ namespace mplot return rtn; } + void setFollowedVM (const mplot::VisualModel* vm_to_follow) + { + for (unsigned int modelId = 0; modelId < this->vm.size(); ++modelId) { + if (this->vm[modelId].get() == vm_to_follow) { + this->followedVM = this->vm[modelId].get(); + this->followedLastViewMatrix = this->followedVM->getViewMatrix(); + break; + } + } + } + /*! * VisualModel Getter * @@ -787,6 +808,7 @@ namespace mplot this->sceneview_tr = sv_tr * this->savedSceneview_tr; } + // This is called every time render() is called void computeSceneview() { if (std::abs(this->scenetrans_delta.sum()) > 0.0f || this->rotation_delta.is_zero_rotation() == false) { @@ -798,12 +820,35 @@ namespace mplot this->scenetrans_delta.zero(); this->state.reset (visual_state::scrolling); } + + if (this->options.test (visual_options::viewFollowsVMTranslations) + && this->followedVM != nullptr + && this->followedLastViewMatrix != this->followedVM->getViewMatrix()) { + + // Move camera the difference between followedLastViewMatrix and + // followedVM->getViewMatrix() in the screen frame of reference. + sm::vec fol_screenframe = (this->sceneview * followedLastViewMatrix.translation() + - this->sceneview * followedVM->getViewMatrix().translation()).less_one_dim(); + + this->sceneview.pretranslate (fol_screenframe); + this->sceneview_tr.pretranslate (fol_screenframe); + this->savedSceneview.pretranslate (fol_screenframe); + this->savedSceneview_tr.pretranslate (fol_screenframe); + + this->followedLastViewMatrix = this->followedVM->getViewMatrix(); + } } //! A vector of pointers to all the mplot::VisualModels (HexGridVisual, //! ScatterVisual, etc) which are going to be rendered in the scene. std::vector>> vm; + //! If the view should follow a model (options viewFollowsVMTranslations and ...Rotations), this is the one. + mplot::VisualModel* followedVM = nullptr; + + //! Holds the viewmatrix of the followedVM the last time we called render + sm::mat44 followedLastViewMatrix; + // Initialize OpenGL shaders, set some flags (Alpha, Anti-aliasing), read in any external // state from json, and set up the coordinate arrows and any VisualTextModels that will be // required to render the Visual. diff --git a/mplot/VisualModelBase.h b/mplot/VisualModelBase.h index 4c262ecd..d5191689 100644 --- a/mplot/VisualModelBase.h +++ b/mplot/VisualModelBase.h @@ -64,6 +64,7 @@ namespace mplot postVertexInitRequired, twodimensional, // If true, then this VisualModel should always be viewed in a plane - it's a 2D model hide, // If true, then calls to VisualModel::render should return + wireframe, // If true, draw in GL's polygon mode show_bb, // If true, draw vertices/indices for the bounding box frame compute_bb // For some models, it's not useful to compute the bounding box (e.g. coordinate arrows) }; @@ -746,6 +747,7 @@ namespace mplot _flags.set (vm_bools::postVertexInitRequired, false); _flags.set (vm_bools::twodimensional, false); _flags.set (vm_bools::hide, false); + _flags.set (vm_bools::wireframe, false); _flags.set (vm_bools::show_bb, false); _flags.set (vm_bools::compute_bb, true); return _flags; @@ -765,6 +767,9 @@ namespace mplot void twodimensional (const bool val) { this->flags.set (vm_bools::twodimensional, val); } bool twodimensional() const { return this->flags.test (vm_bools::twodimensional); } + void wireframe (const bool val) { this->flags.set (vm_bools::wireframe, val); } + bool wireframe() const { return this->flags.test (vm_bools::wireframe); } + //! Getter for vertex positions (for mplot::NormalsVisual) std::vector getVertexPositions() { return this->vertexPositions; } //! Getter for vertex normals (for mplot::NormalsVisual) diff --git a/mplot/VisualModelImplMX.h b/mplot/VisualModelImplMX.h index b752d3f1..06bc2175 100644 --- a/mplot/VisualModelImplMX.h +++ b/mplot/VisualModelImplMX.h @@ -215,6 +215,14 @@ namespace mplot _glfn->UseProgram (this->get_gprog(this->parentVis)); if (!this->indices.empty()) { + + // Enable/disable wireframe mode per-model on each render call + if (this->flags.test (vm_bools::wireframe)) { + _glfn->PolygonMode (GL_FRONT_AND_BACK, GL_LINE); + } else { + _glfn->PolygonMode (GL_FRONT_AND_BACK, GL_FILL); + } + // It is only necessary to bind the vertex array object before rendering // (not the vertex buffer objects) _glfn->BindVertexArray (this->vao); @@ -252,6 +260,7 @@ namespace mplot mplot::gl::Util::checkError (__FILE__, __LINE__, _glfn); // Now render any VisualTextModels + _glfn->PolygonMode (GL_FRONT_AND_BACK, GL_FILL); auto ti = this->texts.begin(); while (ti != this->texts.end()) { (*ti)->render(); ti++; } diff --git a/mplot/VisualModelImplNoMX.h b/mplot/VisualModelImplNoMX.h index 70f236c3..749c0a7c 100644 --- a/mplot/VisualModelImplNoMX.h +++ b/mplot/VisualModelImplNoMX.h @@ -204,6 +204,14 @@ namespace mplot glUseProgram (this->get_gprog(this->parentVis)); if (!this->indices.empty()) { + + // Enable/disable wireframe mode per-model on each render call + if (this->flags.test (vm_bools::wireframe)) { + glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); + } else { + glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + } + // It is only necessary to bind the vertex array object before rendering // (not the vertex buffer objects) glBindVertexArray (this->vao); @@ -241,6 +249,7 @@ namespace mplot mplot::gl::Util::checkError (__FILE__, __LINE__); // Now render any VisualTextModels + glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); auto ti = this->texts.begin(); while (ti != this->texts.end()) { (*ti)->render(); ti++; }