TEST(HitPointInterval, ShouldComputeIntersectionOfEmptyAndNonEmptyIntervals) { HitPointInterval interval1, interval2; HitPoint hitPoint(box, 5, Vector3d(), Vector3d()); interval2.add(hitPoint); HitPointInterval intersectionInterval = interval1 & interval2; ASSERT_TRUE(intersectionInterval.min() == HitPoint::undefined()); ASSERT_TRUE(intersectionInterval.max() == HitPoint::undefined()); }
TEST(HitPointInterval, ShouldComputeUnionOfEmptyAndNonEmptyIntervals) { HitPointInterval interval1, interval2; HitPoint hitPoint(box, 5, Vector3d(), Vector3d()); interval2.add(hitPoint); HitPointInterval unionInterval = interval1 | interval2; ASSERT_TRUE(unionInterval.min() == hitPoint); ASSERT_TRUE(unionInterval.max() == hitPoint); }
TEST(HitPointInterval, ShouldTransformInterval) { HitPointInterval interval; HitPoint hitPoint1(box, 2, Vector3d(1, 0, 0), Vector3d(0, 1, 0)); HitPoint hitPoint2(box, 3, Vector3d(2, 0, 0), Vector3d(0, 1, 0)); interval.add(hitPoint1, hitPoint2); Matrix4d pointMatrix = Matrix3d::rotateZ(1_radians); Matrix3d normalMatrix = Matrix3d::rotateX(1_radians); HitPointInterval transformed = interval.transform(pointMatrix, normalMatrix); ASSERT_EQ(Vector3d(pointMatrix * Vector4d(1, 0, 0)), transformed.min().point()); }
TEST(HitPointInterval, ShouldComputeCompositeOfIntervals) { HitPointInterval interval1, interval2; HitPoint hitPoint1(box, 2, Vector3d(), Vector3d()); HitPoint hitPoint2(box, 3, Vector3d(), Vector3d()); HitPoint hitPoint3(box, 4, Vector3d(), Vector3d()); HitPoint hitPoint4(box, 5, Vector3d(), Vector3d()); interval1.add(hitPoint1, hitPoint2); interval2.add(hitPoint3, hitPoint4); HitPointInterval compositeInterval = interval1 + interval2; ASSERT_TRUE(compositeInterval.min() == hitPoint1); ASSERT_TRUE(compositeInterval.max() == hitPoint4); }
TEST(HitPointInterval, ShouldReturnEmptyIntersectionIfIntervalsDontOverlap) { HitPointInterval interval1, interval2; HitPoint hitPoint1(box, 2, Vector3d(), Vector3d()); HitPoint hitPoint2(box, 3, Vector3d(), Vector3d()); HitPoint hitPoint3(box, 4, Vector3d(), Vector3d()); HitPoint hitPoint4(box, 5, Vector3d(), Vector3d()); interval1.add(hitPoint1, hitPoint2); interval2.add(hitPoint3, hitPoint4); HitPointInterval intersectionInterval = interval1 & interval2; ASSERT_TRUE(intersectionInterval.min() == HitPoint::undefined()); ASSERT_TRUE(intersectionInterval.max() == HitPoint::undefined()); }
TEST(HitPointInterval, ShouldComputeIntersectionOfTwoNonEmptyIntervals) { HitPointInterval interval1, interval2; HitPoint hitPoint1(box, 2, Vector3d(), Vector3d()); HitPoint hitPoint2(box, 5, Vector3d(), Vector3d()); HitPoint hitPoint3(box, 4, Vector3d(), Vector3d()); HitPoint hitPoint4(box, 7, Vector3d(), Vector3d()); interval1.add(hitPoint1, hitPoint2); interval2.add(hitPoint3, hitPoint4); HitPointInterval intersectionInterval = interval1 & interval2; ASSERT_TRUE(intersectionInterval.min() == hitPoint3); ASSERT_TRUE(intersectionInterval.max() == hitPoint2); }
TEST(HitPointInterval, ShouldComputeMergedInterval) { HitPointInterval interval; HitPoint hitPoint1(box, 2, Vector3d(), Vector3d()); HitPoint hitPoint2(box, 3, Vector3d(), Vector3d()); HitPoint hitPoint3(box, 4, Vector3d(), Vector3d()); HitPoint hitPoint4(box, 5, Vector3d(), Vector3d()); interval.add(hitPoint1, hitPoint2); interval.add(hitPoint3, hitPoint4); interval = interval.merged(); ASSERT_EQ(2ul, interval.points().size()); ASSERT_TRUE(interval.min() == hitPoint1); ASSERT_TRUE(interval.max() == hitPoint4); }
const Primitive* ConvexOperation::intersect(const Rayd& ray, HitPointInterval& hitPoints, State& state) const { if (!boundingBoxIntersects(ray)) { state.miss("ConvexOperation, bounding box miss"); return nullptr; } // unlike the intersection methods for box, sphere, etc, convexIntersect() // only returns a single (closest) hitpoint. So we need to shoot a ray in the // opposite direction as well. // calculate hitpoint in ray direction if (convexIntersect(ray, hitPoints)) { // if it's a hit, do it again in the opposite direction HitPointInterval opposite; // If we double the hit distance and take the longest possible length in // the bounding box, we should be beyond the other side of the object Rayd oppositeRay( ray.at(hitPoints.min().distance() * 2.0 + boundingBox().size().length()), -ray.direction() ); // fire! convexIntersect(oppositeRay, opposite); HitPoint oppositePoint = opposite.min(); // now, we have to project that point on the opposite side back to the // original ray to find the real distance value oppositePoint.setDistance(ray.projectedDistance(oppositePoint.point())); // add it to the interval hitPoints.add(oppositePoint); // and merge both points into a single interval. we can do that, since this // is by definition a convex object, so there can be only a single interval hitPoints = hitPoints.merged(); auto hitPoint = hitPoints.minWithPositiveDistance(); if (hitPoint.isUndefined()) { return nullptr; state.miss("ConvexOperation, ray miss"); } else { state.hit("ConvexOperation"); hitPoints.setPrimitive(this); return this; } } state.miss("ConvexOperation, ray miss"); return nullptr; }
TEST(HitPointInterval, ShouldComputeIntersectionOfTwoEmptyIntervals) { HitPointInterval interval1, interval2; HitPointInterval intersectionInterval = interval1 & interval2; ASSERT_TRUE(intersectionInterval.min() == HitPoint::undefined()); ASSERT_TRUE(intersectionInterval.max() == HitPoint::undefined()); }
TEST(HitPointInterval, ShouldSetClosestAndFarthestHitPointWhenOnlyOneHitPointIsAdded) { HitPointInterval interval; HitPoint hitPoint(box, 5, Vector3d(), Vector3d()); interval.add(hitPoint); ASSERT_TRUE(interval.min() == interval.max()); }
TEST(HitPointInterval, ShouldReturnClosestHitPoint) { HitPointInterval interval; HitPoint hitPoint(box, 5, Vector3d(), Vector3d()); interval.add(hitPoint); ASSERT_TRUE(hitPoint == interval.min()); }
TEST(HitPointInterval, ShouldReturnUndefinedClosestHitPointOnEmptyInterval) { HitPointInterval interval; const HitPoint& i = interval.min(); ASSERT_TRUE(i == HitPoint::undefined()); }