do
	local s_new_player = new_player

	print(new_player)
	function new_player(settings, ...)
		local this = s_new_player(settings, ...)

		this.portal_list = {}
		this.portal_list_va = {}
		this.portal_tf_prev = nil
		this.portal_tf_prev_delay = nil

		local s_calc_motion_trace = this.calc_motion_trace
		function this.calc_motion_trace(sec_current, sec_delta, ox, oy, oz, nx, ny, nz, ...)
			local tx1, ty1, tz1 = s_calc_motion_trace(sec_current, sec_delta, ox, oy, oz, nx, ny, nz, ...)
			if this.portal_tf_prev_delay and sec_current > this.portal_tf_prev_delay+0.02 then
				this.portal_tf_prev = nil
				this.portal_tf_prev_delay = nil
			end

			local tf = trace_portal_get_transform(math.floor(tx1), math.floor(ty1), math.floor(tz1))
			if not tf then
				return tx1, ty1, tz1
			end

			-- Prevent a jam
			if tf.idx == this.portal_tf_prev then
				return tx1, ty1, tz1
			end
			this.portal_tf_prev_delay = sec_current
			this.portal_tf_prev = tf.idx
			print("TRANSFORM", tf.idx)

			-- Apply to position + velocity
			tx1, ty1, tz1, this.vx, this.vy, this.vz = trace_portal_transform(
				tf, tx1, ty1, tz1, this.vx, this.vy, this.vz)

			-- Adjust y value so we don't jam ourselves
			-- (compare dys, if non0->0 then set y to cy plus something)
			if tf[2][5] == 0 and tf[1][5] ~= 0 then
				if this.crouching then
					ty1 = tf[2][2]
				else
					ty1 = tf[2][2] - 0.5
				end
			end

			-- Get camera direction
			local sya = math.sin(this.angy)
			local cya = math.cos(this.angy)
			local sxa = math.sin(this.angx)
			local cxa = math.cos(this.angx)
			local fwx,fwy,fwz = sya*cxa, sxa, cya*cxa

			-- Apply to camera
			_, _, _, fwx, fwy, fwz = trace_portal_transform(
				tf, 0, 0, 0, fwx, fwy, fwz, true)

			-- Apply roll
			this.sx, this.sy, this.sz = sxa*sya, -cxa, sxa*cya
			_, _, _, this.sx, this.sy, this.sz = trace_portal_transform(
				tf, 0, 0, 0, this.sx, this.sy, this.sz, true)
			local ds = math.sqrt(this.sx*this.sx + this.sy*this.sy + this.sz*this.sz)
			this.sx = this.sx / ds
			this.sy = this.sy / ds
			this.sz = this.sz / ds

			-- Set camera direction
			this.angy = math.atan2(fwx, fwz)
			this.angx = math.asin(fwy)

			return tx1, ty1, tz1
		end
		local function render_portals(render_mode)
			local i

			for i=1,2 do
				local p = this.portal_list[i]
				if p then
					local other = this.portal_list[3-i]
					local cx, cy, cz = p[1], p[2], p[3]
					local dx, dy, dz = p[4], p[5], p[6]
					local sx, sy, sz = p[7], p[8], p[9]

					local hx = dy*sz-dz*sy
					local hy = dz*sx-dx*sz
					local hz = dx*sy-dy*sx

					cx = cx + 0.5
					cy = cy + 0.5
					cz = cz + 0.5

					--print("PORTAL", cx, cy, cz, dx, dy, dz, sx, sy, sz, i)
					if not p.va then
						local cr,cg,cb
						if i == 1 then
							cr,cg,cb = 0,0.5,1
						else
							cr,cg,cb = 1,0.5,0
						end

						local doffs = 0.5+0.02
						local rh = 1.5-0.12
						local rs = 1.5-0.12

						local x1 = cx - doffs*dx - rh*hx - rs*sx
						local y1 = cy - doffs*dy - rh*hy - rs*sy
						local z1 = cz - doffs*dz - rh*hz - rs*sz
						local x2 = cx - doffs*dx + rh*hx - rs*sx
						local y2 = cy - doffs*dy + rh*hy - rs*sy
						local z2 = cz - doffs*dz + rh*hz - rs*sz
						local x3 = cx - doffs*dx - rh*hx + rs*sx
						local y3 = cy - doffs*dy - rh*hy + rs*sy
						local z3 = cz - doffs*dz - rh*hz + rs*sz
						local x4 = cx - doffs*dx + rh*hx + rs*sx
						local y4 = cy - doffs*dy + rh*hy + rs*sy
						local z4 = cz - doffs*dz + rh*hz + rs*sz

						p.va = {}
						this.portal_list_va[i] = this.portal_list_va[i] or {}
						p.va.border = common.va_make({
							{x1,y1,z1,cr,cg,cb,-dx,-dy,-dz},
							{x3,y3,z3,cr,cg,cb,-dx,-dy,-dz},
							{x2,y2,z2,cr,cg,cb,-dx,-dy,-dz},
							{x4,y4,z4,cr,cg,cb,-dx,-dy,-dz},
							{x2,y2,z2,cr,cg,cb,-dx,-dy,-dz},
							{x3,y3,z3,cr,cg,cb,-dx,-dy,-dz},
						}, this.portal_list_va[i].border, "3v,3c,3n")

						doffs = doffs+0.005
						rh = rh-0.12
						rs = rs-0.12

						x1 = cx - doffs*dx - rh*hx - rs*sx
						y1 = cy - doffs*dy - rh*hy - rs*sy
						z1 = cz - doffs*dz - rh*hz - rs*sz
						x2 = cx - doffs*dx + rh*hx - rs*sx
						y2 = cy - doffs*dy + rh*hy - rs*sy
						z2 = cz - doffs*dz + rh*hz - rs*sz
						x3 = cx - doffs*dx - rh*hx + rs*sx
						y3 = cy - doffs*dy - rh*hy + rs*sy
						z3 = cz - doffs*dz - rh*hz + rs*sz
						x4 = cx - doffs*dx + rh*hx + rs*sx
						y4 = cy - doffs*dy + rh*hy + rs*sy
						z4 = cz - doffs*dz + rh*hz + rs*sz

						doffs = doffs - 1
						local x5 = cx - doffs*dx - rh*hx - rs*sx
						local y5 = cy - doffs*dy - rh*hy - rs*sy
						local z5 = cz - doffs*dz - rh*hz - rs*sz
						local x6 = cx - doffs*dx + rh*hx - rs*sx
						local y6 = cy - doffs*dy + rh*hy - rs*sy
						local z6 = cz - doffs*dz + rh*hz - rs*sz
						local x7 = cx - doffs*dx - rh*hx + rs*sx
						local y7 = cy - doffs*dy - rh*hy + rs*sy
						local z7 = cz - doffs*dz - rh*hz + rs*sz
						local x8 = cx - doffs*dx + rh*hx + rs*sx
						local y8 = cy - doffs*dy + rh*hy + rs*sy
						local z8 = cz - doffs*dz + rh*hz + rs*sz

						cr = cr/2
						cg = cg/2
						cb = cb/2

						p.va.stencil = common.va_make({
							{x1,y1,z1},
							{x3,y3,z3},
							{x2,y2,z2},
							{x4,y4,z4},
							{x2,y2,z2},
							{x3,y3,z3},

							{x5,y5,z5},
							{x7,y7,z7},
							{x6,y6,z6},
							{x8,y8,z8},
							{x6,y6,z6},
							{x7,y7,z7},

							{x1,y1,z1},
							{x3,y3,z3},
							{x5,y5,z5},
							{x7,y7,z7},
							{x5,y5,z5},
							{x3,y3,z3},

							{x2,y2,z2},
							{x6,y6,z6},
							{x4,y4,z4},
							{x8,y8,z8},
							{x4,y4,z4},
							{x6,y6,z6},

							{x1,y1,z1},
							{x5,y5,z5},
							{x2,y2,z2},
							{x6,y6,z6},
							{x2,y2,z2},
							{x5,y5,z5},

							{x3,y3,z3},
							{x4,y4,z4},
							{x7,y7,z7},
							{x8,y8,z8},
							{x7,y7,z7},
							{x4,y4,z4},
						}, this.portal_list_va[i].stencil, "3v")
					end

					if (not p.va.box) and other and other.va then
						local radius = 10

						local x1 = cx - radius*(0+hx+sx-dx)
						local y1 = cy - radius*(0+hy+sy-dy)
						local z1 = cz - radius*(0+hz+sz-dz)
						local x2 = cx - radius*(0-hx+sx-dx)
						local y2 = cy - radius*(0-hy+sy-dy)
						local z2 = cz - radius*(0-hz+sz-dz)
						local x3 = cx - radius*(0+hx-sx-dx)
						local y3 = cy - radius*(0+hy-sy-dy)
						local z3 = cz - radius*(0+hz-sz-dz)
						local x4 = cx - radius*(0-hx-sx-dx)
						local y4 = cy - radius*(0-hy-sy-dy)
						local z4 = cz - radius*(0-hz-sz-dz)

						local x5 = cx - radius*(0+hx+sx)
						local y5 = cy - radius*(0+hy+sy)
						local z5 = cz - radius*(0+hz+sz)
						local x6 = cx - radius*(0-hx+sx)
						local y6 = cy - radius*(0-hy+sy)
						local z6 = cz - radius*(0-hz+sz)
						local x7 = cx - radius*(0+hx-sx)
						local y7 = cy - radius*(0+hy-sy)
						local z7 = cz - radius*(0+hz-sz)
						local x8 = cx - radius*(0-hx-sx)
						local y8 = cy - radius*(0-hy-sy)
						local z8 = cz - radius*(0-hz-sz)

						local fr, fg, fb, fd = client.map_fog_get()
						fr = fr / 255.0
						fg = fg / 255.0
						fb = fb / 255.0
						fr = fr / 2.0
						fg = fg / 2.0
						fb = fb / 2.0
						--print(fr, fg, fb)

						p.va.box = common.va_make({
							{x1,y1,z1,fr,fg,fb,-dx,-dy,-dz},
							{x3,y3,z3,fr,fg,fb,-dx,-dy,-dz},
							{x2,y2,z2,fr,fg,fb,-dx,-dy,-dz},
							{x4,y4,z4,fr,fg,fb,-dx,-dy,-dz},
							{x2,y2,z2,fr,fg,fb,-dx,-dy,-dz},
							{x3,y3,z3,fr,fg,fb,-dx,-dy,-dz},

							{x1,y1,z1,fr,fg,fb,sx,sy,sz},
							{x2,y2,z2,fr,fg,fb,sx,sy,sz},
							{x5,y5,z5,fr,fg,fb,sx,sy,sz},
							{x6,y6,z6,fr,fg,fb,sx,sy,sz},
							{x5,y5,z5,fr,fg,fb,sx,sy,sz},
							{x2,y2,z2,fr,fg,fb,sx,sy,sz},

							{x3,y3,z3,fr,fg,fb,-sx,-sy,-sz},
							{x7,y7,z7,fr,fg,fb,-sx,-sy,-sz},
							{x4,y4,z4,fr,fg,fb,-sx,-sy,-sz},
							{x8,y8,z8,fr,fg,fb,-sx,-sy,-sz},
							{x4,y4,z4,fr,fg,fb,-sx,-sy,-sz},
							{x7,y7,z7,fr,fg,fb,-sx,-sy,-sz},

							{x1,y1,z1,fr,fg,fb,hx,hy,hz},
							{x5,y5,z5,fr,fg,fb,hx,hy,hz},
							{x3,y3,z3,fr,fg,fb,hx,hy,hz},
							{x7,y7,z7,fr,fg,fb,hx,hy,hz},
							{x3,y3,z3,fr,fg,fb,hx,hy,hz},
							{x5,y5,z5,fr,fg,fb,hx,hy,hz},

							{x2,y2,z2,fr,fg,fb,-hx,-hy,-hz},
							{x4,y4,z4,fr,fg,fb,-hx,-hy,-hz},
							{x6,y6,z6,fr,fg,fb,-hx,-hy,-hz},
							{x8,y8,z8,fr,fg,fb,-hx,-hy,-hz},
							{x6,y6,z6,fr,fg,fb,-hx,-hy,-hz},
							{x4,y4,z4,fr,fg,fb,-hx,-hy,-hz},
						}, this.portal_list_va[i].box, "3v,3c,3n")

						-- Build front-to-back
						local x,y,z
						local r
						local l = {}
						local tf = {other, p}

						cx, cy, cz = p[1], p[2], p[3]
						local tcx, tcy, tcz = other[1], other[2], other[3]
						local tdx, tdy, tdz = other[4], other[5], other[6]
						local tsx, tsy, tsz = other[7], other[8], other[9]

						local thx = tdy*tsz-tdz*tsy
						local thy = tdz*tsx-tdx*tsz
						local thz = tdx*tsy-tdy*tsx

						hx = -hx
						hy = -hy
						hz = -hz

						tdx = -tdx
						tdy = -tdy
						tdz = -tdz

						local taxx = hx*thx + hy*tsx + hz*tdx
						local taxy = hx*thy + hy*tsy + hz*tdy
						local taxz = hx*thz + hy*tsz + hz*tdz
						local tayx = sx*thx + sy*tsx + sz*tdx
						local tayy = sx*thy + sy*tsy + sz*tdy
						local tayz = sx*thz + sy*tsz + sz*tdz
						local tazx = dx*thx + dy*tsx + dz*tdx
						local tazy = dx*thy + dy*tsy + dz*tdy
						local tazz = dx*thz + dy*tsz + dz*tdz

						--[[
						taxx, taxy, taxz = 1, 0, 0
						tayx, tayy, tayz = 0, 1, 0
						tazx, tazy, tazz = 0, 0, 1
						]]

						--[[
						taxx, taxy, taxz = -taxx, -taxy, -taxz
						tayx, tayy, tayz = -tayx, -tayy, -tayz
						tazx, tazy, tazz = -tazx, -tazy, -tazz
						]]

						local function add_block(rx, ry, rz, t, tx, ty, tz)
							local x1 = rx
							local y1 = ry
							local z1 = rz
							local x2 = rx+1
							local y2 = ry+1
							local z2 = rz+1

							local cr,cg,cb = t[2], t[3], t[4]
							cr = cr / 255.0
							cg = cg / 255.0
							cb = cb / 255.0
							cr = cr / 1.5
							cg = cg / 1.5
							cb = cb / 1.5

							-- FIXME: some dy~=0 cases fail
							local ALL_FACES = false
							if dy ~= 0 or tdy ~= 0 then
								ALL_FACES = true
							end

							if ALL_FACES or map_block_get(tx-tazx,ty-tazy,tz-tazz) == nil then
							--print("block", rx, ry, rz, cr, cg, cb)
							l[1+#l] = {x1,y1,z1,cr,cg,cb,0,0,-1}
							l[1+#l] = {x2,y1,z1,cr,cg,cb,0,0,-1}
							l[1+#l] = {x1,y2,z1,cr,cg,cb,0,0,-1}
							l[1+#l] = {x2,y2,z1,cr,cg,cb,0,0,-1}
							l[1+#l] = {x1,y2,z1,cr,cg,cb,0,0,-1}
							l[1+#l] = {x2,y1,z1,cr,cg,cb,0,0,-1}
							end

							if ALL_FACES or map_block_get(tx+tazx,ty+tazy,tz+tazz) == nil then
							l[1+#l] = {x1,y1,z2,cr,cg,cb,0,0,1}
							l[1+#l] = {x1,y2,z2,cr,cg,cb,0,0,1}
							l[1+#l] = {x2,y1,z2,cr,cg,cb,0,0,1}
							l[1+#l] = {x2,y2,z2,cr,cg,cb,0,0,1}
							l[1+#l] = {x2,y1,z2,cr,cg,cb,0,0,1}
							l[1+#l] = {x1,y2,z2,cr,cg,cb,0,0,1}
							end

							if ALL_FACES or map_block_get(tx-tayx,ty-tayy,tz-tayz) == nil then
							l[1+#l] = {x1,y1,z1,cr,cg,cb,0,-1,0}
							l[1+#l] = {x1,y1,z2,cr,cg,cb,0,-1,0}
							l[1+#l] = {x2,y1,z1,cr,cg,cb,0,-1,0}
							l[1+#l] = {x2,y1,z2,cr,cg,cb,0,-1,0}
							l[1+#l] = {x2,y1,z1,cr,cg,cb,0,-1,0}
							l[1+#l] = {x1,y1,z2,cr,cg,cb,0,-1,0}
							end

							if ALL_FACES or map_block_get(tx+tayx,ty+tayy,tz+tayz) == nil then
							l[1+#l] = {x1,y2,z1,cr,cg,cb,0,1,0}
							l[1+#l] = {x2,y2,z1,cr,cg,cb,0,1,0}
							l[1+#l] = {x1,y2,z2,cr,cg,cb,0,1,0}
							l[1+#l] = {x2,y2,z2,cr,cg,cb,0,1,0}
							l[1+#l] = {x1,y2,z2,cr,cg,cb,0,1,0}
							l[1+#l] = {x2,y2,z1,cr,cg,cb,0,1,0}
							end

							if ALL_FACES or map_block_get(tx-taxx,ty-taxy,tz-taxz) == nil then
							l[1+#l] = {x1,y1,z1,cr,cg,cb,-1,0,0}
							l[1+#l] = {x1,y2,z1,cr,cg,cb,-1,0,0}
							l[1+#l] = {x1,y1,z2,cr,cg,cb,-1,0,0}
							l[1+#l] = {x1,y2,z2,cr,cg,cb,-1,0,0}
							l[1+#l] = {x1,y1,z2,cr,cg,cb,-1,0,0}
							l[1+#l] = {x1,y2,z1,cr,cg,cb,-1,0,0}
							end

							if ALL_FACES or map_block_get(tx+taxx,ty+taxy,tz+taxz) == nil then
							l[1+#l] = {x2,y1,z1,cr,cg,cb,1,0,0}
							l[1+#l] = {x2,y1,z2,cr,cg,cb,1,0,0}
							l[1+#l] = {x2,y2,z1,cr,cg,cb,1,0,0}
							l[1+#l] = {x2,y2,z2,cr,cg,cb,1,0,0}
							l[1+#l] = {x2,y2,z1,cr,cg,cb,1,0,0}
							l[1+#l] = {x2,y1,z2,cr,cg,cb,1,0,0}
							end
						end

						for r=radius*3,1,-1 do
						for z=0,r do
							local az = math.abs(z)
							local ystep = r-az
						for y=-ystep,ystep do
							if ystep < 0 then break end
							-- Get abs values for calculation
							local ay = math.abs(y)
							local xstep = r-(ay+az)
						for x=-xstep,xstep,2*xstep do
							if xstep < 0 then break end
							local ax = math.abs(x)
							local ma = math.max(math.max(ax,ay),az)
							local _

							-- Ensure in range
							if ma <= radius and ax+ay+az == r then
							--if ax+ay+az == r then
								-- LET'S DO IT

								-- Get source coordinates
								local rx = cx + dx*z + hx*x + sx*y
								local ry = cy + dy*z + hy*x + sy*y
								local rz = cz + dz*z + hz*x + sz*y

								rx = math.floor(rx+0.5)
								ry = math.floor(ry+0.5)
								rz = math.floor(rz+0.5)

								-- Get target coordinates
								local tx = tcx + tdx*(z+1) + thx*x + tsx*y
								local ty = tcy + tdy*(z+1) + thy*x + tsy*y
								local tz = tcz + tdz*(z+1) + thz*x + tsz*y

								tx = math.floor(tx+0.5)
								ty = math.floor(ty+0.5)
								tz = math.floor(tz+0.5)
								--print("tf", tx, ty, tz, "->", rx, ry, rz)

								-- Get target block
								local b = map_block_get(tx,ty,tz)

								-- Add block if necessary
								if b then
									add_block(rx, ry, rz, b, tx, ty, tz)
								end

							end

							if xstep == 0 then break end

						end end end end

						p.va.scene = common.va_make(l,
							this.portal_list_va[i].scene, "3v,3c,3n")
					end

					this.portal_list_va[i] = p.va

					client.va_render_global(p.va.border, 0, 0, 0, 0, 0, 0, 1)

					if other and other.va and other.va.scene then
						-- Mark stencil region
						client.gfx_stencil_test(true)
						client.gfx_stencil_func("1", 2, 255)
						client.gfx_stencil_op(";;=")
						client.va_render_global(p.va.stencil, 0, 0, 0, 0, 0, 0, 1, nil, "10")

						-- Clear depth
						-- FIXME: engine needs support for glDepthFunc(GL_ALWAYS)
						-- (ATM we have to generate the block list back-to-front)
						client.gfx_stencil_func("==", 2, 255)
						client.gfx_stencil_op(";;;")
						client.gfx_depth_test(false)
						client.va_render_global(p.va.box, 0, 0, 0, 0, 0, 0, 1)
						client.va_render_global(p.va.scene, 0, 0, 0, 0, 0, 0, 1)
						client.gfx_depth_test(true)

						-- Clear stencil region
						client.gfx_stencil_func("1", 0, 255)
						client.gfx_stencil_op("===")
						client.va_render_global(p.va.stencil, 0, 0, 0, 0, 0, 0, 1, nil, "10")
						client.gfx_stencil_func("1", 0, 255)
						client.gfx_stencil_op(";;;")
						client.gfx_stencil_test(false)
					end
				end
			end

		end

		local s_render = this.render
		function this.render(render_mode, ...)
			if render_mode == nil then
				render_portals(render_mode)
			end

			local ret = s_render(render_mode, ...)

			return ret
		end

		return this
	end
end

