# HG changeset patch # User Teemu Piippo # Date 1582799022 -7200 # Node ID b7841cd31fb7f532c202b53fcc545fe5cce4b01c # Parent 5c0005f633192deb06a9f6ecfa158be9a8f92e08 use glm::project instead of figuring out the conversion manually... diff -r 5c0005f63319 -r b7841cd31fb7 src/geometry.cpp --- 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 geom::linePlaneIntersection(const geom::Line& line, const geom::Plane& plane) +std::optional 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 {}; } diff -r 5c0005f63319 -r b7841cd31fb7 src/geometry.h --- 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 linePlaneIntersection(const Line& line, const Plane& plane); + std::optional linePlaneIntersection(const Line& line, const Plane& plane, const float epsilon = 1e-6); } diff -r 5c0005f63319 -r b7841cd31fb7 src/gl/partrenderer.cpp --- 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 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 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 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( diff -r 5c0005f63319 -r b7841cd31fb7 src/gl/partrenderer.h --- 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 screenToModelCoordinates(const QPoint& point); + std::optional screenToModelCoordinates(const QPoint& point, const geom::Plane& plane); QPointF modelToScreenCoordinates(const glm::vec3& point); glm::vec3 unproject(const glm::vec3& win); private: diff -r 5c0005f63319 -r b7841cd31fb7 src/ui/canvas.cpp --- 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);