SilverHawk's Rad Homepage
Radness is coming

SilverHawk's Guide to Ripping a Hole Through Space

Ever since Valve released Portal in 2008, the burning question on everyone's mind is "How can I get Portals into our own, or more commonly someone else's, game?" This page aims to look at some of the challenges involved in implementing portals. It is in no way complete and is more just refrence for some of problems with portals and possible solutions or just extra complications.

This page is split up into sections of what I have found to be some of the major problems with implementing portals. More will be added when I think of them They are:

There's a hole in the sky, through which things can fly - How do we carve a great hole in the walls collision so the player and objects can enter the portal?

Speedy thing goes in, speedy thing comes out - How do we get an object from one end of a portal to the other, facing the right direction, and going the right speed?

How to half your framerate - How do you make portals see-through?

There's a hole in the sky through which things can fly

This problem had me stumped for a while. I always figured Valve modified the Source engine to allow carving a hole in a brush's collision. At some point I remembered the Aperture Science Handheld Portal Device addon for Garry's Mod, which allowed the player to stand inside portals. I decided to take a look at the lua code for it and noticed something interesting. In lua/entities/prop_portal/init.lua in the function ENT:PlayerEnterPortal(ent)

function ENT:PlayerEnterPortal(ent)
	umsg.Start( "Portal:ObjectInPortal" )
		umsg.Entity( self )
		umsg.Entity( ent )
	umsg.End()
	ent.InPortal = self
	
	self:SetupPlayerClone(ent)
	
	ent:GetPhysicsObject():EnableDrag(false)
	
	local vel = ent:GetVelocity()
	ent:SetMoveType(MOVETYPE_NOCLIP)
	ent:SetGroundEntity( self )
	-- print("noclipping")

	if ent.JustEntered then
		ent:EmitSound("weapons/portalgun/portal_enter".. self.PortalType ..".wav",80,100 + (30 * (ent:GetVelocity():Length() - 100)/1000))
		ent.JustEntered = false
	end
end

Notice the line ent:SetMoveType(MOVETYPE_NOCLIP). Everytime a player enters a portal, they are put in noclip mode and only collide with the portal. There isn't any fancy technique where you modify the collision of the surface the portal is on, you just make it so when something touches the portal, it stops colliding with the wall and only collides with a border around the portal. In this implimentation there is an issue where sometimes there is geometry other than the surface the portal is on that you need the object to continue to collide with, and being in noclip mode, a player can enter geometry as they exit a portal and get stuck in the world. It helps if in your engine you can selectivly disable collision between the object and the surface holding the portal rather than disabling collision with the whole world.

Portal and portal collision shown with brushes
In this image, the portal is represented by a grey texture while it's collision box is represented by orange. Both extend farther from the wall than they optimally would for demonstration purposes. When the player touches the portal (grey), they're collision with the wall is disabled and instead they collide with the portal's collision (orange). At no point is collision with the floor affected, as the portal is not on the floor.

Although I haven't seen the source code, I assume that iChun's portal gun mod disables the entity's collision with the two blocks behind the portal upon the player entering the blocks the portal occupies, while enabling collision with some axis aligned bounding boxes (AABBs) surrounding the portal to prevent the player passing through corners.

Portal and collisions displayed on a corner
This image displays the importants of the aformention AABBs on corners, where in Minecraft you would normally be able to rely on the collision of surrounding blocks

Speedy thing goes in, speedy thing comes out

It might seem simple enough to move an object from place to another and for the most part it is, but things aren't as simple as they seem

To understand how this problem gets difficult you need to understand that an object does not leave a portal the same way it cam in, but rather it goes out the opposite way. Sort of.

A demonstration of a plank of wood entering the left of one portal and emerging from the right of another
The plank of wood enters the left of the blue portal and emerges from the right of the orange portal. Both portals are in the same orientation.

The rotation of an object can be defined by two vectors. In this case we'll use forward and up, but any two orthagonal vectors will do. The position of an object relative to a portal is not the same as the position it has relative to the linked portal as it passed through them. The portals should simulate being back-to-back, as in, if they were to face opposite direction and be in the same position, an object passing through them would not change orientation or position.

The direction vectors of a portal and it's linked portal are shown. The green line shows forward, the red line right, and the blue line up. The axis of the linked portal are highlighted with a yellow border. Note that both portals have the same up vector, while their forward and right vectors are opposite each other.

To solve this issue we can think of every portal as having an anti-portal, a term I just made up from the normal vector of the anti-portal being equal to the anti-normal vector of the regular portal. As stated, the forward or normal vector (they're the same in this case) of the anti-portal is opposite that of the regular portal, while the up vector of the anti-portal is the same as the regular portal's up vector. In this case, the position of an object relative to the anti-portal is the same as the relative position it should have to the linked portal when passing through it.

One last thing, we need to find the exact moment an object passes past our portal's threshold. We're going to do it by projecting the difference in position from the object to the portal on the portal's forward vector. We do this by first getting subtracting the portal's position from the object's position to get the difference in position, then finding the dot product between this new difference vector and our portal's forward vector. If this is negative, the object is behind the threshold of the portal.

All the vectors in the aformention math to find if the object is behind the portal. Note that the blue projection is facing the opposite way from the forward vector it is projected on. That's what make the dot product negative, and how we can use that dot product being negative to determine that the object is behind the portal.

You've probably noticed by now that I've been very careful not to use the term relative to describe the difference in position between the object and the portal. That's becuase we're about to get into relative position, and it's a little bit more complicated than that.