Thu, 27 Feb 2020 12:23:42 +0200
use glm::project instead of figuring out the conversion manually...
src/geometry.cpp | file | annotate | diff | comparison | revisions | |
src/geometry.h | file | annotate | diff | comparison | revisions | |
src/gl/partrenderer.cpp | file | annotate | diff | comparison | revisions | |
src/gl/partrenderer.h | file | annotate | diff | comparison | revisions | |
src/ui/canvas.cpp | file | annotate | diff | comparison | revisions |
--- a/src/geometry.cpp Thu Feb 27 11:56:41 2020 +0200 +++ b/src/geometry.cpp Thu Feb 27 12:23:42 2020 +0200 @@ -17,10 +17,10 @@ * @param plane * @return point of intersection. Does not return a value if the line is in parallel to the plane. */ -std::optional<glm::vec3> geom::linePlaneIntersection(const geom::Line& line, const geom::Plane& plane) +std::optional<glm::vec3> geom::linePlaneIntersection(const geom::Line& line, const geom::Plane& plane, const float epsilon) { const float denominator = glm::dot(line.direction, plane.normal); - if (std::abs(denominator) < 1e-8f) + if (std::abs(denominator) < epsilon) { return {}; }
--- a/src/geometry.h Thu Feb 27 11:56:41 2020 +0200 +++ b/src/geometry.h Thu Feb 27 12:23:42 2020 +0200 @@ -30,5 +30,5 @@ Line lineFromPoints(const glm::vec3& point_1, const glm::vec3 point_2); Plane planeFromTriangle(const Triangle& triangle); glm::vec3 normalVector(const Triangle& triangle); - std::optional<glm::vec3> linePlaneIntersection(const Line& line, const Plane& plane); + std::optional<glm::vec3> linePlaneIntersection(const Line& line, const Plane& plane, const float epsilon = 1e-6); }
--- a/src/gl/partrenderer.cpp Thu Feb 27 11:56:41 2020 +0200 +++ b/src/gl/partrenderer.cpp Thu Feb 27 12:23:42 2020 +0200 @@ -286,24 +286,49 @@ return viewport(point, this->width(), this->height()); } -std::optional<glm::vec3> PartRenderer::screenToModelCoordinates(const QPoint& point) +/** + * @brief Converts the specified on the screen into the 3D world. The point is unprojected twice into 3D and the + * intersection of the resulting line with the specified plane is returned. If the intersection point lies behind + * the camera, no value is returned. + * @param point 2D window co-ordinates to convert. + * @param plane Plane to raycast against + * @return world co-ordinates, or no value if the point is behind the camera. + */ +std::optional<glm::vec3> PartRenderer::screenToModelCoordinates(const QPoint& point, const geom::Plane& plane) { auto p1 = this->unproject({point.x(), point.y(), 0}); auto p2 = this->unproject({point.x(), point.y(), 1}); geom::Line line = geom::lineFromPoints(p1, p2); - return geom::linePlaneIntersection(line, geom::XY); + std::optional<glm::vec3> result; + // If the dot product between the line direction and plane normal is negative, the point + // of intersection lies behind the camera. + if (glm::dot(line.direction, plane.normal) > 0) + { + result = geom::linePlaneIntersection(line, plane, 0.01); + } + return result; } +/** + * @brief Converts the specified point to 2D window coordinates, with Y-coordinate inverted for Qt + * @param point Point to unproject + * @return screen coordinates + */ QPointF PartRenderer::modelToScreenCoordinates(const glm::vec3& point) { - glm::vec4 p = {point, 1}; - p = glm::mat4_cast(this->modelQuaternion) * p; - p = this->viewMatrix * p; - p = this->projectionMatrix * p; - glm::vec2 pp = this->viewport({p.x / p.w, p.y / p.w, 1}); - return toQPointF(pp); + const glm::vec3 projected = glm::project( + point, + this->viewMatrix * glm::mat4_cast(this->modelQuaternion), + this->projectionMatrix, + this->viewportVector); + return {projected.x, this->height() - projected.y}; } +/** + * @brief Unprojects the specified window coordinates to model coordinates + * @param win Window coordinates to project. Z-coordinate indicates depth + * @return model coordinates + */ glm::vec3 PartRenderer::unproject(const glm::vec3& win) { return glm::unProject(
--- a/src/gl/partrenderer.h Thu Feb 27 11:56:41 2020 +0200 +++ b/src/gl/partrenderer.h Thu Feb 27 12:23:42 2020 +0200 @@ -38,7 +38,7 @@ const ldraw::ColorTable& colorTable; gl::Compiler* const compiler; ldraw::Id highlighted = ldraw::NULL_ID; - std::optional<glm::vec3> screenToModelCoordinates(const QPoint& point); + std::optional<glm::vec3> screenToModelCoordinates(const QPoint& point, const geom::Plane& plane); QPointF modelToScreenCoordinates(const glm::vec3& point); glm::vec3 unproject(const glm::vec3& win); private:
--- a/src/ui/canvas.cpp Thu Feb 27 11:56:41 2020 +0200 +++ b/src/ui/canvas.cpp Thu Feb 27 12:23:42 2020 +0200 @@ -27,7 +27,7 @@ this->highlighted = id; this->totalMouseMove += (event->pos() - this->lastMousePosition).manhattanLength(); this->lastMousePosition = event->pos(); - this->worldPosition = this->screenToModelCoordinates(this->lastMousePosition); + this->worldPosition = this->screenToModelCoordinates(this->lastMousePosition, geom::XY); if (this->worldPosition.has_value()) { this->worldPosition = glm::round(*this->worldPosition);