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.
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.
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.
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.
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.
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.