Difference between revisions of "Seamshot"

From P2SR Wiki

(my brain hurty ouch)
 
m (codeblock lmao)
 
(One intermediate revision by the same user not shown)
Line 20: Line 20:
 
* if <code>d1</code> is negative and <code>d2</code> is positive, the ray goes out of the plane, so the plane is considered an '''exit plane'''.
 
* if <code>d1</code> is negative and <code>d2</code> is positive, the ray goes out of the plane, so the plane is considered an '''exit plane'''.
 
Then, an intersection fraction is calculated using formula <code>d1 / (d1 - d2)</code>. Once every plane is checked, the largest fraction among entry planes is compared to the smallest fraction among exit planes - if it's smaller, then we know ray hit the brush.
 
Then, an intersection fraction is calculated using formula <code>d1 / (d1 - d2)</code>. Once every plane is checked, the largest fraction among entry planes is compared to the smallest fraction among exit planes - if it's smaller, then we know ray hit the brush.
 +
 
However, to make a similar resizing as in a simple brush logic, <code>DIST_EPSILON</code> is added to fraction equation. In fact, entry and exit planes have different equations:
 
However, to make a similar resizing as in a simple brush logic, <code>DIST_EPSILON</code> is added to fraction equation. In fact, entry and exit planes have different equations:
 
* <code>(d1 - DIST_EPSILON) / (d1 - d2)</code> for entry plane (for entering the brush we want to intersect closer, hence why subtraction),
 
* <code>(d1 - DIST_EPSILON) / (d1 - d2)</code> for entry plane (for entering the brush we want to intersect closer, hence why subtraction),
 
* <code>(d1 + DIST_EPSILON) / (d1 - d2)</code> for exit plane (for exiting the brush we want to intersect further, hence why addition).
 
* <code>(d1 + DIST_EPSILON) / (d1 - d2)</code> for exit plane (for exiting the brush we want to intersect further, hence why addition).
But because for exit planes <code>d1</code> is already negative, adding DIST_EPSILON brings the fraction closer to zero instead of further as it should. Because of that, intersection with exit planes happens too close, allowing some of the rays to pass through the brush without the collision being detected. This behaviour is what ultimately allows the seamshotting.
+
But because for exit planes <code>d1</code> is already negative, adding <code>DIST_EPSILON</code> brings the fraction closer to zero instead of further as it should. Because of that, intersection with exit planes happens too close, allowing some of the rays to pass through the brush without the collision being detected. This behaviour is what ultimately allows the seamshotting.
  
 
===BSP tree leaves===
 
===BSP tree leaves===

Latest revision as of 17:29, 2 January 2022

Seamshot

Overview

A corner simple-complex seamshot. The faded out red polygon represents complex brush, and the bright red polygon represents what collision algorithm "sees".

Seamshot is a glitch that allows you to shoot through some of the seams between two walls, even though no gap between then can be seen. It is known to exist in both Portal 2 and Portal.

Technical Explanation

Seamshot comes from an incorrect behavior of ray collision with brushes - convex shapes that are used to create basic level geometry in Source engine. In terms of ray collision, we can distinguish two types of brushes:

  • axis aligned boxes (simple brushes),
  • any other convex shape that’s not an axis aligned box (complex brushes).

Both of these types have a unique algorithm for determining the intersection point of a ray with a brush. What both of these have in common is attempting to resize a brush by a value of DIST_EPSILON (equal to 0.03125 units). Presumably it was made to prevent objects having a direct contact with the brush, which could lead to z-fighting or objects being stuck in it. The way this distance offset is implemented in both of these algorithms, however, is very different, which later sections will attempt to explain.

Simple brush (box) collision

As explained above, the ray doesn’t actually collide with the original box, but instead an enlarged version of it, with each face moved out by a value of DIST_EPSILON. However, this change would cause the ray to hit the brush near its corner, even though the ray itself doesn’t go through the original brush. In order to fix this, the ray is first tested against the brush with the original size, and then an enlarged version is used for finding a collision point. This way the hit point is offset from the brush properly and it only yields a hit when we’re looking at the original brush.

Complex brush collision

Ray collision with a complex brush works as follows: for each plane forming a brush, a signed distance of the start and end point of the ray to the plane is calculated - d1 and d2 for start and end point distance respectively. Then, using these numbers, a couple of checks are made:

  • if both d1 and d2 are positive, the entire ray is in front of the plane, meaning the ray cannot possibly hit the brush;
  • if both d1 and d2 are negative, the entire ray is behind the plane, so it cannot possibly intersect the plane, so the plane is skipped;
  • if d1 is positive and d2 is negative, the ray goes into the plane, so the plane is considered an entry plane;
  • if d1 is negative and d2 is positive, the ray goes out of the plane, so the plane is considered an exit plane.

Then, an intersection fraction is calculated using formula d1 / (d1 - d2). Once every plane is checked, the largest fraction among entry planes is compared to the smallest fraction among exit planes - if it's smaller, then we know ray hit the brush.

However, to make a similar resizing as in a simple brush logic, DIST_EPSILON is added to fraction equation. In fact, entry and exit planes have different equations:

  • (d1 - DIST_EPSILON) / (d1 - d2) for entry plane (for entering the brush we want to intersect closer, hence why subtraction),
  • (d1 + DIST_EPSILON) / (d1 - d2) for exit plane (for exiting the brush we want to intersect further, hence why addition).

But because for exit planes d1 is already negative, adding DIST_EPSILON brings the fraction closer to zero instead of further as it should. Because of that, intersection with exit planes happens too close, allowing some of the rays to pass through the brush without the collision being detected. This behaviour is what ultimately allows the seamshotting.

BSP tree leaves

Normally, most of wall and corner seams created by two complex brushes are impossible to shoot through. This is because if planes forming the seam are overlapping, once a plane forming a seam in one brush is considered an exit plane, the plane forming a seam in another brush automatically becomes an entry plane, which shifts it away from the brush, covering the seam. However, this can be avoided if brushes forming the seam are in separate BSP tree leaves. When that's a case, ray can aim in a way so it's never going through the leaf of the second brush, so it's no longer covering the brush. This is the case for Conversion Intro Least Portals seamshot.

Types of seams

We can distinguish several types of seamshots depending on:

  • types of brushes forming the seam:
    • simple - complex - the most commonly used type of seam. Thanks to simple brush not blocking the virtual gap created by a faulty code, it's possible to shoot through them in almost any case.
    • complex - complex - more complicated type of seam, requiring additional "weirdness" to be able to shoot through it (misaligned planes forming the seam, separate BSP leaves) .
    • simple - simple - given current knowledge, these seams are impossible to shoot through.
  • placement of brushes in relation to each other:
    • corner seamshots
    • wall seamshots

TODO: complete definitions

Examples

TBW

Trivia

TBW