diff --git a/maths b/maths index 7d3174d9..08ed4786 160000 --- a/maths +++ b/maths @@ -1 +1 @@ -Subproject commit 7d3174d91fe1b93f76dc284d3bba9e29b312bde6 +Subproject commit 08ed4786265537294158c6e1da4a85708f0d1e6c diff --git a/mplot/VisualCommon.h b/mplot/VisualCommon.h index e20aad04..7e5f6325 100644 --- a/mplot/VisualCommon.h +++ b/mplot/VisualCommon.h @@ -9,8 +9,41 @@ #include #include #include +#include +#include #include +#include +#include +#include #include +#include + +namespace mplot +{ + // A very simple mesh struct. No textures, materials or owt + struct meshgroup + { + std::string name; + sm::mat44 transform; + sm::vvec indices; + sm::vvec> positions; + sm::vvec> normals; + sm::vvec> colours; + sm::range> object_aabb; + sm::range> world_aabb; + // Single colour is used if colours is empty + std::array single_colour = mplot::colour::grey50; + void validate() const + { + if (this->positions.size() != this->normals.size()) { + throw std::runtime_error ("meshgroup has different numbers of positions and normals"); + } + if (!this->colours.empty() && this->colours.size() != this->positions.size()) { + throw std::runtime_error ("meshgroup has different numbers of positions and colours"); + } + } + }; +} namespace mplot::visgl { diff --git a/mplot/VisualModelBase.h b/mplot/VisualModelBase.h index cd2d878e..4c262ecd 100644 --- a/mplot/VisualModelBase.h +++ b/mplot/VisualModelBase.h @@ -841,24 +841,57 @@ namespace mplot //! Push three floats onto the vector of floats \a vp void vertex_push (const float& x, const float& y, const float& z, std::vector& vp) { - sm::vec vec = { x, y, z }; - std::copy (vec.begin(), vec.end(), std::back_inserter (vp)); + vp.emplace_back (x); + vp.emplace_back (y); + vp.emplace_back (z); } //! Push array of 3 floats onto the vector of floats \a vp - void vertex_push (const std::array& arr, std::vector& vp) + template requires (N == 3 || N == 4) + void vertex_push (const std::array& arr, std::vector& vp) { - std::copy (arr.begin(), arr.end(), std::back_inserter (vp)); + vp.emplace_back (arr[0]); + vp.emplace_back (arr[1]); + vp.emplace_back (arr[2]); } //! Push sm::vec of 3 floats onto the vector of floats \a vp - void vertex_push (const sm::vec& vec, std::vector& vp) + template requires (N == 3 || N == 4) + void vertex_push (const sm::vec& vec, std::vector& vp) { - std::copy (vec.begin(), vec.end(), std::back_inserter (vp)); + vp.emplace_back (vec[0]); + vp.emplace_back (vec[1]); + vp.emplace_back (vec[2]); } //! Set up a vertex buffer object - bind, buffer and set vertex array object attribute virtual void setupVBO (GLuint& buf, std::vector& dat, unsigned int bufferAttribPosition) = 0; protected: + /*! + * Add the given meshgroup to this VisualModel + */ + void computeMeshgroup (const mplot::meshgroup& mg) + { + mg.validate(); + + bool single_colr = mg.colours.empty(); + for (uint32_t i = 0; i < mg.positions.size(); ++i) { + // We apply mg.transform *here*, rather than writing it into the viewmatrix. This is + // because other elements of this visual model may be added with the assumption of an + // identity viewmatrix. + this->vertex_push (mg.transform * mg.positions[i], this->vertexPositions); + this->vertex_push (mg.transform * mg.normals[i], this->vertexNormals); + if (single_colr) { + this->vertex_push (mg.single_colour, this->vertexColors); + } else { + this->vertex_push (mg.colours[i], this->vertexColors); + } + } + for (uint32_t i = 0; i < mg.indices.size(); ++i) { + this->indices.push_back (mg.indices[i] + this->idx); + } + this->idx += mg.positions.size(); + } + /** * START vertex/index computation code * diff --git a/mplot/compoundray/EyeVisual.h b/mplot/compoundray/EyeVisual.h index 4d3b5f96..558b266f 100644 --- a/mplot/compoundray/EyeVisual.h +++ b/mplot/compoundray/EyeVisual.h @@ -76,20 +76,23 @@ namespace mplot::compoundray //! Initialise with offset, start and end coordinates, radius and a single colour. EyeVisual (const sm::vec _offset, std::vector>* _ommData, - std::vector* _ommatidia) + std::vector* _ommatidia, + const mplot::meshgroup* _head_mesh = nullptr) { - this->init (_offset, _ommData, _ommatidia); + this->init (_offset, _ommData, _ommatidia, _head_mesh); } ~EyeVisual() {} void init (const sm::vec _offset, std::vector>* _ommData, - std::vector* _ommatidia) + std::vector* _ommatidia, + const mplot::meshgroup* _head_mesh = nullptr) { this->viewmatrix.translate (_offset); this->ommData = _ommData; this->ommatidia = _ommatidia; + this->head_mesh = _head_mesh; } void reinitColours() @@ -311,10 +314,15 @@ namespace mplot::compoundray this->computeTube (pos, pos + (0.05f * dw * dir), colour, colour, dw * 0.5f, tube_faces); // This visualizes the optical cones - if (this->show_cones == true) { + if (this->show_cones == true && this->show_fov == false) { float optical_radius = focal_point * std::tan (angle / 2.0f); // Colour comes from ommData. ringoffset is 1.0f this->computeCone (pos, ommatidial_detector_point, 0.0f, colour, optical_radius, tube_faces); + } else if (this->show_fov == true) { + // do a cone of angle 'acceptanceAngle' using user-supplied cone_length, starting FROM the disc to show field of view of the eye + sm::vec ommatidial_cone_pos = pos + dir * this->cone_length; + float radius = this->cone_length * std::tan (angle / 2.0f); + this->computeCone (ommatidial_cone_pos, pos, 0.0f, colour, radius, tube_faces); } } } else if (show_3d && this->focal_point_sum <= 0.0f) { @@ -333,7 +341,7 @@ namespace mplot::compoundray float dw = min_dist_to_other[i]; this->computeTube (pos, pos + (0.05f * dw * dir), colour, colour, dw * 0.5f, tube_faces); // We don't have a focal length to show cones, but we can still show the acceptance angle - if (this->show_cones == true) { + if (this->show_fov == true) { // do a cone of angle 'acceptanceAngle' using user-supplied cone_length sm::vec ommatidial_cone_pos = pos + dir * this->cone_length; float radius = this->cone_length * std::tan (angle / 2.0f); @@ -342,6 +350,7 @@ namespace mplot::compoundray } } + // 2D projections for (uint32_t pri = 0; pri < this->projections.size(); ++pri) { this->omm2d.clear(); if (this->projections[pri].proj_type == projection_type::cylindrical) { @@ -374,6 +383,7 @@ namespace mplot::compoundray } } + // Sphere and rays for finding a suitable 2D projection for (uint32_t pri = 0; pri < this->projections.size(); ++pri) { if (this->show_sphere) { @@ -401,6 +411,9 @@ namespace mplot::compoundray } } } + + // Optional head + if (this->head_mesh != nullptr) { this->computeMeshgroup (*this->head_mesh); } } void voronoi2d (uint32_t pri) @@ -474,13 +487,17 @@ namespace mplot::compoundray // If false, hide 3D representation (the ommatidial cones and discs) bool show_3d = true; - // Visualize in two modes "disc" mode, showing just a 2D disc for each ommatidium and - // disc+cone mode, where the acceptance angle/optical cone is displayed too. Runtime switchable. + // If true, show optical cones, if possible bool show_cones = false; + // If true, show 'field of view' cones. Overrides show_cones. Control size of cones with + // this->cone_length + bool show_fov = false; // The colours detected by each ommatidium std::vector>* ommData = nullptr; // The position and orientation of each ommatidium std::vector* ommatidia = nullptr; + // An optional head mesh + const mplot::meshgroup* head_mesh = nullptr; // If sum is 0, then we have a special case for rendering the eye as we have no focal point // offsets specified for this eye (and hence the optical radius of the ommatidium is not known) float focal_point_sum = 0.0f;