diff --git a/README.md b/README.md index 4eab8b3..696c1a2 100644 --- a/README.md +++ b/README.md @@ -8,4 +8,4 @@ World portals inspired by Overv's Portal Project and Portal 2's linked_portal_do * Rendering: almost complete * Weapon and trace diversion: just started -* Entity teleportation: not started +* Entity teleportation: just started \ No newline at end of file diff --git a/gmod_worldportal.fgd b/gmod_worldportals.fgd similarity index 71% rename from gmod_worldportal.fgd rename to gmod_worldportals.fgd index 2eb68dc..d521dc1 100644 --- a/gmod_worldportal.fgd +++ b/gmod_worldportals.fgd @@ -1,6 +1,6 @@ //============================================================================= // -// Purpose: Garry's Mod world portal declarations +// Garry's Mod world portal declarations // //============================================================================= @@ -8,7 +8,9 @@ @BaseClass = LinkedPortalDoor : "An entity that can be linked to another door and create a passage between them dynamically." [ - input SetPair(integer) : "Changes the pair of portals this portal belongs to" + partnername(target_destination) : "Linked Partner" : : "Another 'prop_linked_portal_door' entity which will link to this one." + + input SetPartner(string) : "Set a new partner door." input Open(void) : "Open the door and cause the portal to activate." input Close(void) : "Close the door and cause the portal to deactivate." @@ -20,11 +22,11 @@ output OnPlayerTeleportToMe(void) : "When the player is teleported from this linked partner to the portal." ] -@PointClass base(Targetname, Parentname, Angles, LinkedPortalDoor) studio("models/editor/axis_helper_thick.mdl") = linked_portal_door : +@PointClass base(Targetname, Parentname, Angles, LinkedPortalDoor) sphere(DisappearDist) studio("models/editor/axis_helper_thick.mdl") = linked_portal_door : "A door which is linked by a portal to another 'linked_portal_door' entity." [ - pair(integer) : "Pair" : 1 : "Unique identifier for a set of portals." - width(integer) : "Width" : 128 : "Width of the desired portal." - height(integer) : "Height" : 128 : "Height of the desired portal." + width(integer) : "Width" : 128 : "Width of the desired portal. Measured from the center" + height(integer) : "Height" : 128 : "Height of the desired portal. Measured from the center" startactive(integer) : "Start Active" : 1 : "Whether to start the linkage as active from the start." + DisappearDist(integer) : "Disappear Distance" : -1 : "Distance at which the portal will stop rendering" ] \ No newline at end of file diff --git a/lua/autorun/client/init.lua b/lua/autorun/client/init.lua deleted file mode 100644 index 41fd86d..0000000 --- a/lua/autorun/client/init.lua +++ /dev/null @@ -1,136 +0,0 @@ - -local portals -local matView = CreateMaterial( - "UnlitGeneric", - "GMODScreenspace", - { - ["$basetexturetransform"] = "center .5 .5 scale -1 -1 rotate 0 translate 0 0", - ["$texturealpha"] = "0", - ["$vertexalpha"] = "1", - } -) -local matDummy = Material( "debug/white" ) - - --- Render the portal views -local drawing = false - -hook.Add( "RenderScene", "WorldPortalRenderHook", function( plyOrigin, plyAngles) - - if ( not portals ) then return end - if ( drawing ) then return end - - local oldWepColor = LocalPlayer():GetWeaponColor() - LocalPlayer():SetWeaponColor( Vector(0, 0, 0) ) --no more phys gun glaw or beam - - for _, portal in ipairs( portals ) do - -- Render view from portal - local oldRT = render.GetRenderTarget() - render.SetRenderTarget( portal.texture ) - render.Clear( 0, 0, 0, 255 ) - render.ClearDepth() - render.ClearStencil() - - render.EnableClipping(true) - render.PushCustomClipPlane(portals[portal.exit].forward, portals[portal.exit].forward:Dot(portals[portal.exit].pos) ) - - local rotation = portals[portal.exit].forward:Angle() - portal.forward:Angle() - rotation = rotation + Angle( 0, 180, 0) - local offset = LocalPlayer():EyePos() - portal.pos - offset:Rotate( rotation ) - local camPos = portals[portal.exit].pos + offset - - local camAngles = plyAngles + rotation - - drawing = true - render.RenderView( { - x = 0, - y = 0, - w = ScrW(), - h = ScrH(), - origin = camPos, - angles = camAngles, - drawpostprocess = true, - drawhud = false, - drawmonitors = false, - drawviewmodel = false, - } ) - drawing = false - - render.PopCustomClipPlane() - render.EnableClipping(false) - render.SetRenderTarget( oldRT ) - end - - LocalPlayer():SetWeaponColor( oldWepColor ) -end ) -hook.Add( "PreDrawOpaqueRenderables", "WorldPortalRenderHook", function() - - render.UpdateScreenEffectTexture() - - if ( not portals ) then return end - if ( drawing ) then return end - - for _, portal in ipairs( portals ) do - - -- Draw view over portal - render.ClearStencil() - render.SetStencilEnable( true ) - - render.SetStencilWriteMask( 1 ) - render.SetStencilTestMask( 1 ) - - render.SetStencilFailOperation( STENCILOPERATION_KEEP ) - render.SetStencilZFailOperation( STENCILOPERATION_KEEP ) - render.SetStencilPassOperation( STENCILOPERATION_REPLACE ) - render.SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_ALWAYS ) - render.SetStencilReferenceValue( 1 ) - - render.SetMaterial( matDummy ) - render.SetColorModulation( 1, 1, 1 ) - - render.DrawQuadEasy( portal.pos, portal.forward, portal.width, portal.height, Color( 255, 255, 255, 255) ) - - render.SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_EQUAL ) - render.SetStencilPassOperation( STENCILOPERATION_REPLACE ) - render.SetStencilReferenceValue( 1 ) - - matView:SetTexture( "$basetexture", portal.texture ) - render.SetMaterial( matView ) - render.DrawScreenQuad() - - render.SetStencilEnable( false ) - end -end ) - --- Receive portal info -net.Receive("WorldPortalUpdate", function() - portals = {} - - for i = 1, net.ReadInt( 16 ) do - portals[i] = {} - portals[i].pos = net.ReadVector() - portals[i].width = net.ReadInt( 16 ) - portals[i].height = net.ReadInt( 16 ) - portals[i].forward = net.ReadVector() - portals[i].exit = net.ReadInt( 16 ) - portals[i].texture = GetRenderTarget("portal" .. i, - ScrW(), - ScrH(), - false - ) - end - -end ) - --- Set it to draw the player while rendering portals --- Calling Start3D to fix this is incredibly hacky - -hook.Add( "PostDrawEffects", "WorldPortalPotentialFix", function() - cam.Start3D( EyePos(), EyeAngles() ) - cam.End3D() -end) - -hook.Add( "ShouldDrawLocalPlayer", "WorldPortalRenderHook", function() - return drawing -end) diff --git a/lua/autorun/server/init.lua b/lua/autorun/server/init.lua deleted file mode 100644 index 8456518..0000000 --- a/lua/autorun/server/init.lua +++ /dev/null @@ -1,34 +0,0 @@ - - --- Send portal render code -AddCSLuaFile( "autorun/client/init.lua" ) - --- Precache network string -util.AddNetworkString( "WorldPortalUpdate" ) - --- Send info about portals to clients -local function WorldPortalUpdate( ply ) - - ply:DrawViewModel(false) - local portals = ents.FindByClass( "linked_portal_door" ) - - net.Start( "WorldPortalUpdate" ) - net.WriteInt( #portals, 16 ) - - for _, portal in ipairs( portals ) do - net.WriteVector( portal:GetPos() ) - net.WriteInt( portal.width, 16 ) - net.WriteInt( portal.height, 16 ) - net.WriteVector( portal.forward ) - net.WriteInt( table.KeyFromValue( portals, portal.exit ), 16 ) - end - net.Send( ply ) -end -hook.Add( "PlayerInitialSpawn", "WorldPortalUpdateTransmission", WorldPortalUpdate ) - --- Set up visibility through portals -hook.Add( "SetupPlayerVisibility", "WorldPortalVisibility", function( ply, ent ) - for _, portal in ipairs( ents.FindByClass( "linked_portal_door" ) ) do - AddOriginToPVS( portal:GetPos() ) - end -end ) diff --git a/lua/autorun/worldportals_init.lua b/lua/autorun/worldportals_init.lua new file mode 100644 index 0000000..5dda0f7 --- /dev/null +++ b/lua/autorun/worldportals_init.lua @@ -0,0 +1,15 @@ + +worldportals = {} + +-- Load required files +if SERVER then + + include( "worldportals/render_sv.lua" ) + + AddCSLuaFile( "worldportals/render_cl.lua" ) + +else + + include( "worldportals/render_cl.lua" ) + +end diff --git a/lua/entities/linked_portal_door/cl_init.lua b/lua/entities/linked_portal_door/cl_init.lua new file mode 100644 index 0000000..7a7dc10 --- /dev/null +++ b/lua/entities/linked_portal_door/cl_init.lua @@ -0,0 +1,40 @@ + +include( "shared.lua" ) + +AccessorFunc( ENT, "texture", "Texture" ) + + +--Draw world portals +function ENT:Draw() + + --self:DrawModel() + + if ( worldportals.drawing ) then return end + + render.ClearStencil() + render.SetStencilEnable( true ) + + render.SetStencilWriteMask( 1 ) + render.SetStencilTestMask( 1 ) + + render.SetStencilFailOperation( STENCILOPERATION_KEEP ) + render.SetStencilZFailOperation( STENCILOPERATION_KEEP ) + render.SetStencilPassOperation( STENCILOPERATION_REPLACE ) + render.SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_ALWAYS ) + render.SetStencilReferenceValue( 1 ) + + render.SetMaterial( worldportals.matDummy ) + render.SetColorModulation( 1, 1, 1 ) + + render.DrawQuadEasy( self:GetPos(), self:GetForward(), self:GetWidth(), self:GetHeight(), Color( 255, 255, 255, 255), self:GetAngles().roll ) + + render.SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_EQUAL ) + render.SetStencilPassOperation( STENCILOPERATION_REPLACE ) + render.SetStencilReferenceValue( 1 ) + + worldportals.matView:SetTexture( "$basetexture", self:GetTexture() ) + render.SetMaterial( worldportals.matView ) + render.DrawScreenQuad() + + render.SetStencilEnable( false ) +end \ No newline at end of file diff --git a/lua/entities/linked_portal_door/init.lua b/lua/entities/linked_portal_door/init.lua index a120302..1fbeb13 100644 --- a/lua/entities/linked_portal_door/init.lua +++ b/lua/entities/linked_portal_door/init.lua @@ -1,30 +1,26 @@ -ENT.Type = "point" - - --- Find paired entity --- not the most efficient implementation, but good enough for now -function ENT:Think() - -- Determine exit portal - if ( !self.exit ) then - for _, ent in ipairs( ents.FindByClass( "linked_portal_door" ) ) do - if ( ent ~= self and ent.pair == self.pair ) then - self.exit = ent - end - end - end -end +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include( "shared.lua" ) + +AccessorFunc( ENT, "partnername", "PartnerName" ) -- Collect properties function ENT:KeyValue( key, value ) - if ( key == "pair" ) then - self.pair = tonumber( value ) + + if ( key == "partnername" ) then + self:SetPartnerName( value ) + self:SetExit( ents.FindByName( value )[1] ) elseif ( key == "width" ) then - self.width = tonumber( value ) + self:SetWidth( tonumber(value) *2 ) elseif ( key == "height" ) then - self.height = tonumber( value ) + self:SetHeight( tonumber(value) *2 ) + + elseif ( key == "DisappearDist" ) then + self:SetDisappearDist( tonumber(value) ) elseif ( key == "angles" ) then local args = value:Split( " " ) @@ -33,7 +29,6 @@ function ENT:KeyValue( key, value ) args[k] = tonumber(arg) end - self.forward = Angle( unpack(args) ):Forward() - print( value, forward ) + self:SetAngles( Angle( unpack(args) ) ) end -end \ No newline at end of file +end diff --git a/lua/entities/linked_portal_door/shared.lua b/lua/entities/linked_portal_door/shared.lua new file mode 100644 index 0000000..41e5b8b --- /dev/null +++ b/lua/entities/linked_portal_door/shared.lua @@ -0,0 +1,50 @@ + +ENT.Type = "anim" +ENT.Spawnable = false +ENT.AdminOnly = false +ENT.Editable = false + + +--ENT.Model = Model("models/props/cs_office/microwave.mdl") + +function ENT:Initialize() + + local mins = Vector(-1, -self:GetWidth() /2, -self:GetHeight() /2) + local maxs = -mins + + if CLIENT then + + self:SetTexture( GetRenderTarget("portal" .. self:EntIndex(), + ScrW(), + ScrH(), + false + ) ) + + self:SetRenderBounds( mins, maxs ) + + end + + --[[self:SetModel(self.Model) + + self:PhysicsInit(SOLID_VPHYSICS) + self:SetMoveType(MOVETYPE_VPHYSICS) + self:SetSolid(SOLID_BBOX) + + local b = 32 + self:SetCollisionBounds(Vector(-b, -b, -b), Vector(b,b,b)) + + local phys = self:GetPhysicsObject() + --phys:EnableMotion( true ) + --phys:Wake()]]-- + +end + + +function ENT:SetupDataTables() + + self:NetworkVar( "Entity", 0, "Exit" ) + self:NetworkVar( "Int", 1, "Width" ) + self:NetworkVar( "Int", 2, "Height" ) + self:NetworkVar( "Int", 2, "DisappearDist" ) + +end diff --git a/lua/worldportals/render_cl.lua b/lua/worldportals/render_cl.lua new file mode 100644 index 0000000..9d4e4c1 --- /dev/null +++ b/lua/worldportals/render_cl.lua @@ -0,0 +1,95 @@ + +--Setup variables +worldportals.matView = CreateMaterial( + "UnlitGeneric", + "GMODScreenspace", + { + ["$basetexturetransform"] = "center .5 .5 scale -1 -1 rotate 0 translate 0 0", + ["$texturealpha"] = "0", + ["$vertexalpha"] = "1", + } +) +worldportals.matDummy = Material( "debug/white" ) + + +--POTENTIAL IMPROVEMENT +--add portals when they're created +--remove portals when they're destroyed +--this would be slightly better than just finding all of them every frame +--hook.Add( "OnEntityCreated", "WorldPortalRenderHook", function( ent ) + +local portals +worldportals.drawing = false + +-- Render views from the portals +hook.Add( "RenderScene", "WorldPortals_Render", function( plyOrigin, plyAngles) + + portals = ents.FindByClass( "linked_portal_door" ) + + if ( not portals ) then return end + if ( worldportals.drawing ) then return end + + --Disable phys gun glow and beam + local oldWepColor = LocalPlayer():GetWeaponColor() + LocalPlayer():SetWeaponColor( Vector(0, 0, 0) ) + + for _, portal in pairs( portals ) do + + local distance = plyOrigin:Distance( portal:GetPos() ) /8 --divide to match distance in hammer + local exitPortal = portal:GetExit() + + if not (portal:GetDisappearDist() < 0) and distance > portal:GetDisappearDist() then continue end + if not IsValid( exitPortal ) then continue end + + + local oldRT = render.GetRenderTarget() + render.SetRenderTarget( portal:GetTexture() ) + render.Clear( 0, 0, 0, 255 ) + render.ClearDepth() + render.ClearStencil() + + render.EnableClipping(true) + render.PushCustomClipPlane( exitPortal:GetForward(), exitPortal:GetForward():Dot(exitPortal:GetPos() ) ) + + local localOrigin = portal:WorldToLocal( plyOrigin ) + local localAngles = portal:WorldToLocalAngles( plyAngles ) + + localOrigin:Rotate( Angle(0, 180, 0) ) + localAngles:RotateAroundAxis( Vector(0, 0, 1), 180) + + local camOrigin = exitPortal:LocalToWorld( localOrigin ) + local camAngles = exitPortal:LocalToWorldAngles( localAngles ) + + worldportals.drawing = true + render.RenderView( { + x = 0, + y = 0, + w = ScrW(), + h = ScrH(), + origin = camOrigin, + angles = camAngles, + drawpostprocess = true, + drawhud = false, + drawmonitors = false, + drawviewmodel = false, + } ) + worldportals.drawing = false + + render.PopCustomClipPlane() + render.EnableClipping(false) + render.SetRenderTarget( oldRT ) + end + + LocalPlayer():SetWeaponColor( oldWepColor ) +end ) + +-- Set it to draw the player while rendering portals +-- Calling Start3D to fix this is incredibly hacky +hook.Add( "PostDrawEffects", "WorldPortals_PlayerDrawFix", function() + cam.Start3D( EyePos(), EyeAngles() ) + cam.End3D() +end) + +hook.Add( "ShouldDrawLocalPlayer", "WorldPortals_PlayerDraw", function() + return worldportals.drawing +end) diff --git a/lua/worldportals/render_sv.lua b/lua/worldportals/render_sv.lua new file mode 100644 index 0000000..5b87075 --- /dev/null +++ b/lua/worldportals/render_sv.lua @@ -0,0 +1,18 @@ + +-- Add all portal visleafs to server's potentially visible set +hook.Add( "SetupPlayerVisibility", "WorldPortals_AddPVS", function( ply, ent ) + for _, portal in ipairs( ents.FindByClass( "linked_portal_door" ) ) do + AddOriginToPVS( portal:GetPos() ) + end +end ) + + +-- Make sure that all portals have found their exit +-- Sometimes the entrance portal will be initialized before the exit +hook.Add( "InitPostEntity", "WorldPortals_PairWithExits", function( ply, ent ) + for _, portal in ipairs( ents.FindByClass( "linked_portal_door" ) ) do + if not IsValid( portal:GetExit() ) then + portal:SetExit( ents.FindByName( portal:GetPartnerName() )[1] ) + end + end +end ) \ No newline at end of file diff --git a/maps/spacial_bullshit.bsp b/maps/spacial_bullshit.bsp index 50488cd..56a19e5 100644 Binary files a/maps/spacial_bullshit.bsp and b/maps/spacial_bullshit.bsp differ