function scene.computePath(entity)
	--bench.push("computePath")
	--if (editingLevel and pathsPanel.open) or ltimer.fourstep==2 then 
		local preBounds = {0,0,0,0}
		for m, mode in pairs(pathModes) do
			for k,path in pairs(entity[mode] or {}) do
				
				path.verts=nil
				path.smoothing 	= path.smoothing or 0
				path.clean 		= false 
				path.curSpeed 	= path.speed
				path.normPos 	= path.startPos
				path.loop 		= mode=="floors" and "loop" or path.loop
				path.walled 	= path.walled or "gaps"
				path.made  		= nil
				path.id 			= path.key
				path.mode 		= mode
				
				if not path.color.h then 
					--path.color.h,path.color.s,path.color.v = rgbToHsv(path.color.r,path.color.g,path.color.b)
				end

				if #path.points>1 then
					local totalDistance = 0
					local preCurve  	= {}
					local preLine   	= {}
					local preDists  	= {}
					
					-- PRECALC TOTAL DISTANCE (OPTIMIZE THIS)
					for p,point in ipairs(path.points) do 
						point.i=p

						local curve

						if type(point.walled) == "nil" then
							point.walled = true
						end 
						

						if #path.points<2 or (p==#path.points and path.loop~="loop") then 
							curve = {  point.x,        point.y, 
									   point.x,        point.y,
									   point.x,        point.y,}
						else
							local prevPoint = p>1              and path.points[p-1] or (path.loop=="loop" and path.points[#path.points]   or  point )
							local nextPoint = p<#path.points   and path.points[p+1] or (path.loop=="loop" and path.points[1]              or  point )
							local pointDist = math.dist3(point.x,point.y,point.z,nextPoint.x,nextPoint.y,nextPoint.z)
							totalDistance = totalDistance + pointDist
							table.insert(preDists,pointDist)

							local pPoint = {    
								x = point.x+(point.x-prevPoint.x)/2,
								y = point.y+(point.y-prevPoint.y)/2 
							}
							pPoint.x = (nextPoint.x+point.x)/2--(pPoint.x+(nextPoint.x+point.x)/2)/2
							pPoint.y = (nextPoint.y+point.y)/2--(pPoint.y+(nextPoint.y+point.y)/2)/2

							curve =      {  
								point.x,        point.y, 
								pPoint.x,       pPoint.y, 
								nextPoint.x,    nextPoint.y, 
							}

							if path.smoothing>pathsPanel.smoothCutoff then 
								local smoothLevel = mf(  ( (1-path.smoothing)^2 )  *  10  *  (pointDist/200)  )
								if smoothLevel>0 then
									for i=1, smoothLevel do
										local lpp = {   x = lerp(point.x,     pPoint.x, 1-i/smoothLevel), 
														y = lerp(point.y,     pPoint.y, 1-i/smoothLevel)}
										local lnp = {   x = lerp(nextPoint.x, pPoint.x, 1-i/smoothLevel), 
														y = lerp(nextPoint.y, pPoint.y, 1-i/smoothLevel)}
										curve=lume.concat( curve,   {lpp.y,       lpp.x}     ,true)
										curve=lume.concat( curve,   {lnp.x,       lnp.y}     ,false)
									end
								end
							end
						end

						-- BOUNDS OF PATHS
						preBounds[2] = (point.y<preBounds[2]) and point.y or preBounds[2]
						preBounds[4] = (point.y>preBounds[4]) and point.y or preBounds[4]
						preBounds[1] = (point.x<preBounds[1]) and point.x or preBounds[1]
						preBounds[3] = (point.x>preBounds[3]) and point.x or preBounds[3]

						preCurve=lume.concat(preCurve, curve)
						preLine =lume.concat(preLine, {point.x,point.y,point.z})
					end
					
					if path.loop=="loop"  then
						preLine=lume.concat(preLine,  {path.points[1].x,path.points[1].y,path.points[1].z})
					end

					-- path.curve      	= love.math.newBezierCurve(preCurve)
					path.line       	= preLine
					path.dists      	= preDists
					path.pcurve     	= preCurve
					
					--path.smoothLine 	= path.curve:render(path.smoothing>1.7 and 2 or 1) 
					path.rcurve     	= (pathsPanel~=nil and path.smoothing > pathsPanel.smoothCutoff) and path.smoothLine or path.line
					
					entity.pathBounds   = preBounds
					path.length         = totalDistance
				end
			end
		end
	--end
	--bench.pop("computePath")
end




function scene.addPlanes(entity)

	makingTerrain = false

	local add = false 
	if cmdDown and keypressed("r") then 			scene.time=0			add = true end
	if editor.updateLinks then 											add = true end
	if pathsPanel.changed or scene.time==0 then 	add = true end 

	if not (add or editor.debug.force_build) then return end

	entity.terrain 					= entity.terrain 			 or {}
	entity.terrain.nocopy 	= true
	entity.terrain.models 	= entity.terrain.models or {}
	entity.terrainRemovals  = {}


	terrainVerts = 0
	makingTerrain = true
	
	bench.push("build")
	scene.addPaths(entity)
	scene.addFloors(entity)
	scene.addRamps(entity)
	bench.pop("build")

	table.sort(entity.terrainRemovals)
	for i,r in lume.ripairs(entity.terrainRemovals) do
		table.remove(entity.terrain.models,r)
		log('rm r')
	end
	trace("termods",#entity.terrain.models)
	entity.terrainRemovals = {}
	
end

function scene.setPatterns()
	grassTexture = grassTexture or  gfx.newImage("images/Textures/grass.png")
	if LOVE then grassTexture:setFilter("nearest","nearest")end
	local dims,imgs,imgs2,size,img = {},{},{},{}
	local x,y,w,h 			= 0,0,0,0
	local width,tallest = 0,0
	local n = 32

	for i=1,n,1 do 
		imgs[i] =  {}
		imgs[i].img = gfx.newImage(string.format("images/Patterns/pattern_%i.png",i))
		imgs[i].w   = imgs[i].img:getWidth()
		imgs[i].h   = imgs[i].img:getHeight()
		imgs[i].i   = i
		width=width+imgs[i].w
	end

	imgs = sortBy(imgs,"h")

	-- print(inspect(imgs))

	width= width/1.9
	height = 0
	for i=1,n,1 do 
		img = imgs[i].img
		w   = imgs[i].w
		h   = imgs[i].h
		y=x+w>width and y+h or y
		x=x+w>width and 0 or x
		dims[imgs[i].i] = {x, y, w, h }
		size[imgs[i].i] = {x, y, w, h ,x=x, y=y, w=w, h=h }
		x=x+w
		imgs2[imgs[i].i]=img
		height = y+h
		--width=width+w
		--tallest=math.max(h,tallest)
	end

	

	gfx.push("all")
		gfx.reset()
		patterns.w,patterns.h = width,height
		patterns.atlas= patterns.atlas or newCanvas(width,height,{format="rgb565"})
		gfx.setCanvas(patterns.atlas)
		for i=1,n,1 do 
			gfx.draw(imgs2[i],dims[i][1],dims[i][2])
			dims[i][1]=dims[i][1]/width
			dims[i][2]=dims[i][2]/height
			dims[i][3]=dims[i][3]/width
			dims[i][4]=dims[i][4]/height
			imgs2[i]:release()
		end
	gfx.pop()

	-- patterns.imgs = imgs
	patterns.size = size
	patterns.dims = dims
	patterns.atlas:setFilter("nearest","nearest",0) 
	-- shaders.forwardRender:send("patterns",unpack(patterns.dims))
	-- shaders.deferredStatic:send("patterns",unpack(patterns.dims))
end

function smoothLine(line,smoothing,draw)
	local preCurve = {}
	local smoothed
	local curve
	local point 	
	local prevPoint 
	local nextPoint 
	local pointDist 
	local pPoint
	local smoothLevel
	local lpp
	local lnp
	local noSmooth  = (#line<3 or smoothing<=0)
	if noSmooth then 
		--preCurve = line
	else
		for p=1,#line-3,2 do
			prevPoint = p>1         and {x=line[p-2],y=line[p-1]} or point 
			point 	= {	x=line[p],	y=line[p+1]	}
			nextPoint = p<#line-2   and {x=line[p+2],y=line[p+3]} or point 
			pointDist = math.dist(point.x,point.y,nextPoint.x,nextPoint.y)
			if pointDist>2 then 
				pPoint = { 
					x = (nextPoint.x+point.x)/2,
					y = (nextPoint.y+point.y)/2 
				}

				curve =      {  
					point.x,        point.y, 
					pPoint.x,       pPoint.y, 
					nextPoint.x,    nextPoint.y, 
				}

				smoothLevel = 8---mc( 0, mf(pointDist/8) ,2)
				print(pointDist.."->"..smoothLevel)
				if smoothLevel>0 then
					for i=1, smoothLevel do
						lpp = {   	x = lerp(point.x,     pPoint.x, 1-i/smoothLevel), 
									y = lerp(point.y,     pPoint.y, 1-i/smoothLevel)}
						lnp = {   	x = lerp(nextPoint.x, pPoint.x, 1-i/smoothLevel), 
									y = lerp(nextPoint.y, pPoint.y, 1-i/smoothLevel)}
						 curve= lume.concat(  {lpp.x,       lpp.y} , curve     )
						 curve= lume.concat( curve,   {lnp.x,       lnp.y}     )
					end
				end

				 preCurve= lume.concat(preCurve, curve)
			end
		end
		print(#preCurve)
		if #preCurve>6 then 
			-- smoothed =love.math.newBezierCurve(preCurve):render(1)
			-- simplifyLine(smoothed)
			-- simplifyLine(smoothed)
			-- simplifyLine(smoothed)
			-- table.remove(smoothed,#smoothed)
			-- table.remove(smoothed,#smoothed)
		end
	end

	if draw then 
		gfx.line(noSmooth and line or smoothed)
	end
	if smoothed then 
		print("in "..tostring(#line))
		print("out "..tostring(#smoothed))
		return smoothed
	else 
		return line 
	end
end

function simplifyLine(line)
	for i,v in lume.ripairs(line) do
		if i%4==1 then 
			table.remove(line,i)
			table.remove(line,i)
		end
	end
end


local planeT = mat4():translate(mat4(),vec3(0,0.1,0))
local planeM = mat4():transpose(planeT)

function scene.grass(floor,tris)
	local grassVerts = {}
 
	local bbox 			= bboxLine(toLine2Dz(floor.points))
	local sw,sh 		= bbox.w,bbox.h
	local num,spread,height,width = 	mf(floor.grass*math.sqrt(sw*sh))*2,200,16,16
	local scale 		= 1
	local variants 	= grassTexture:getWidth()/width

	local  out = function(x,z) 
		local inTri = false 
		for i,t in ipairs(tris) do 
			if point_in_triangle({t[1].x,t[1].z},{t[2].x,t[2].z},{t[3].x,t[3].z},{x,z}) then 
				inTri = true
			end
		end
		return not inTri
	end

	local placed = 0
	local i = 0
	while placed<num do
		local y = floor.y
		local bx,bz,br = bbox[1]+frandom(i)*sw,bbox[2]+frandom(i*2+10)*sh,frandom(i/3+3)*2*pi
		i = i+1
		if not out(bx,bz) then 
			placed = placed+1
			local ox,oz = math.sin(br)*16,math.cos(br)*16
			local px,pz = math.sin(br+pi/2)*16,math.cos(br+pi/2)*16
			local sway = 2
			local height = height*0.75 + height * 0.25* (math.sin(bx+bz))
			local grassVariant = 1

			table.insert(grassVerts,{ bx-ox/2,y,bz-oz/2,1,  				0,1,  1,1,1,1, 0,0,0, 	0,0,0})
			table.insert(grassVerts,{ bx+ox/2,y,bz+oz/2,1,  				1,1,  1,1,1,1, 0,0,0, 	0,0,0})
			table.insert(grassVerts,{ bx-ox/2,y-height,bz-oz/2,1,  	0,0,  1,1,1,1, 0,0,0, 	sway,sway/8,sway})
			table.insert(grassVerts,{ bx+ox/2,y,bz+oz/2,1,  				1,1,  1,1,1,1, 0,0,0, 0,0,0})
			table.insert(grassVerts,{ bx-ox/2,y-height,bz-oz/2,1,  	0,0,  1,1,1,1, 0,0,0, sway,sway/8,sway})
			table.insert(grassVerts,{ bx+ox/2,y-height,bz+oz/2,1, 	1,0,  1,1,1,1, 0,0,0, sway,sway/8,sway})

			table.insert(grassVerts,{ bx-px/2,y,bz-pz/2,1,  				0,1,  1,1,1,1, 0,0,0, 0,0,0})
			table.insert(grassVerts,{ bx+px/2,y,bz+pz/2,1,  				1,1,  1,1,1,1, 0,0,0, 0,0,0})
			table.insert(grassVerts,{ bx-px/2,y-height,bz-pz/2,1,  	0,0,  1,1,1,1, 0,0,0, sway,sway/8,sway})
			table.insert(grassVerts,{ bx+px/2,y,bz+pz/2,1,  				1,1,  1,1,1,1, 0,0,0, 0,0,0})
			table.insert(grassVerts,{ bx-px/2,y-height,bz-pz/2,1,  	0,0,  1,1,1,1, 0,0,0, sway,sway/8,sway})
			table.insert(grassVerts,{ bx+px/2,y-height,bz+pz/2,1, 	1,0,  1,1,1,1, 0,0,0, sway,sway/8,sway})
		end
	end 


	trace("placed:"..placed.."/"..num)

	if next(grassVerts) then 
		local grassMesh = gfx.newMesh(zformat,grassVerts,"triangles")
		gfx.setTexture(grassMesh,grassTexture)	

		return grassMesh
	else
		return cubeMesh
	end
end


local blankmat = mat4()
function scene.addPaths(entity)
	local angle,angle2
	local angles = {}
	local angledPoint
	local angledCircles = {}
	local nextCircle = {}
	local nextCirc,nextCirc2
	local nextPoint
	local prevPoint

	local circle = {}
	local rotmat = mat4()
	local verts,tris

	local n,a,d = 8,0,3
	for i=1,n,1 do 
		a = (i/n)*2*pi
		table.insert(circle, vec3(math.cos(a)*d,math.sin(a)*d,0))
	end


	for q,path in pairs(entity.paths or {}) do

		local model,insert = lume.match(entity.terrain.models, function(x) return x.id==q end) 

		if not model  
			or scene.time==0
			or (pathsPanel and pathsPanel.changed and pathsPanel.changed[1]=="paths" and pathsPanel.changed[2]==q) 
			or (model and (
						not multicheckAND({path.color.r,path.color.g,path.color.b,1},model.color) 
						or 	path.loop ~= model.loop
						or 	#path.verts ~= #model.verts  )) then 
			

			verts 	= {}
			tris 	= {}
			angles 			= {}


			-- angles first
			for p,point in ipairs(path.points) do 

				nextPoint = path.points[p==#path.points and 1 or p+1]
				prevPoint = path.points[p==1 and #path.points or p-1]

				local xz = vec2(nextPoint.x,nextPoint.z) - vec2(prevPoint.x,prevPoint.z)
				local y = nextPoint.y - prevPoint.y

				angle 	= pi/2-vec2.angle_to(vec2(0,0),vec2(xz:len(),y))
				angle2  = pi/2-vec2.angle_to(vec2(0,0),xz)
				--angle2  = vec2.angle_to(vec2(0,0),xz)-vec2.angle_between(vec2(nextPoint.x,nextPoint.z)-vec2(point.x,point.z),vec2(prevPoint.x,prevPoint.z)-vec2(point.x,point.z))

				rotmat:rotate(blankmat, angle, vec3.unit_x)
				rotmat:rotate(blankmat, angle2, axes.y)

				angledCircles[p] = {}
				for c,circ in ipairs(circle) do 
					angledPoint = rotmat*circ--vec3.rotate(circ,pi/2,axes.y) --angles[p]*circ
	     		table.insert( angledCircles[p],	{angledPoint.x+point.x,angledPoint.y+point.y,angledPoint.z+point.z,1,0,0})
	     	end
			end

			-- make verts
			for p,point in ipairs(path.points) do 
				if not (path.loop~="loop" and p==#path.points) then 
					nextPoint = path.points[p==#path.points and 1 or p+1]
					prevPoint = path.points[p==1 and #path.points or path.points[p-1]]
					nextCircle = angledCircles[p==#path.points and 1 or p+1]

					

					--table.insert( verts,	{ point.x,	point.y,	point.z, 1 ,0,0})
		     --	table.insert( verts,	{ point.x,point.y,point.z, 1 ,0,0})
		     	--table.insert( verts,	{ nextPoint.x,nextPoint.y,nextPoint.z, 1 ,0,0})

		     	for c,circ in ipairs(angledCircles[p]) do 
		     		nextCirc = nextCircle[c]
		     		nextCirc2 = nextCircle[c==n and 1 or c+1]
		     		nextCirc3 = angledCircles[p][c==n and 1 or c+1]
		     		--table.insert( verts,	circ)
		     		--table.insert( verts,	angledCircles[p][c==n and 1 or c+1])
		     		--table.insert( verts,	circ)

						table.insert( verts,	circ)
		     		table.insert( verts,	nextCirc)
						table.insert( verts,	nextCirc2)

		     		table.insert( verts,	circ)
		     		table.insert( verts,	nextCirc2)
		     		table.insert( verts,	nextCirc3)

		     		--table.insert( tris, 	
			     	--	{
			      -- 		vec3(circ.x,circ.y,circ.z),
			      -- 		vec3(nextCirc.x,nextCirc.y,nextCirc.z),
			      -- 		vec3(nextCirc2.x,nextCirc2.y,nextCirc2.z)
			      -- 	}
			      -- )
		     		--table.insert( tris, 
			      -- 	{
			      -- 		vec3(circ.x,circ.y,circ.z),
			      -- 		vec3(nextCirc2.x,nextCirc2.y,nextCirc2.z),
			      -- 		vec3(nextCirc3.x,nextCirc3.y,nextCirc3.z)
			      -- 	}
			     	--)
		     	end
		    end
			end

			
			if insert then 
				if #entity.terrain.models[insert].verts == #verts then 
					entity.terrain.models[insert].mesh:setVertices( verts, 1 )
				else 
					entity.terrain.models[insert].mesh:release()
					entity.terrain.models[insert].mesh = gfx.newMesh(zformat, verts,"triangles")
				end
				entity.terrain.models[insert].verts = verts
				entity.terrain.models[insert].color = {path.color.r,path.color.g,path.color.b,1}
			else
				table.insert(entity.terrain.models,{
		   		isTerrain=true,
		   		id 			= q,
		   		mode 		= "paths",
		   		loop 		= path.loop,
		     	verts 	= verts,
		     	--tris 		= tris,
					color 	= {path.color.r,path.color.g,path.color.b,1},
					mesh 		= gfx.newMesh(zformat, verts,"triangles"),
					transform= blankMat,
					parent 	=blankMat,
				})
			end
			path.verts = #verts
		end
	end
end

local modelCheck
function scene.checkModels(models,id,p,k)
	modelCheck = nil 
	for i=1,#models,1 do 
		if models[i].id==id and models[i].index==p and models[i][k] then 
			modelCheck = i
		end
	end
	return modelCheck
end

function reverseVertWinding(verts)
	for v=1,#verts,3 do 
		out[v],out[v+1],out[v+2] = verts[v+2],verts[v+1],verts[v]
	end
	return verts
end







function calcBBOX(verts)
	local min,max = math.min,math.max
	local mn,mx=vec3(verts[1]),vec3(verts[1])
	for i=1,#verts,1 do 
		mn.x = min(verts[i][1],mn.x)
		mn.y = min(verts[i][2],mn.y)
		mn.z = min(verts[i][3],mn.z)
		mx.x = max(verts[i][1],mx.x)
		mx.y = max(verts[i][2],mx.y)
		mx.z = max(verts[i][3],mx.z)
	end
	return {max=mx,min=mn}
end

function calcBBOX2(tris)
	local min,max = math.min,math.max
	if not tris[1] then 
		return 
	end
	local mn,mx=tris[1][1]:clone(),tris[1][1]:clone()
	for i=1,#tris,1 do 
		for v=1,3,1 do 
			mn.x = min(tris[i][v].x,mn.x)
			mn.y = min(tris[i][v].y,mn.y)
			mn.z = min(tris[i][v].z,mn.z)

			mx.x = max(tris[i][v].x,mx.x)
			mx.y = max(tris[i][v].y,mx.y)
			mx.z = max(tris[i][v].z,mx.z)
		end
	end
	return {max=mx,min=mn}
end

function calcBBOXFromMesh(mesh)
	local min,max = math.min,math.max
	local mn,mx=vec3(mesh:getVertex(1)),vec3(mesh:getVertex(1))
	for i=1,mesh:getVertexCount(),1 do 
		local x,y,z = mesh:getVertex(i)
		-- print("x",x)
		-- print("mx",mn)
		mn.x = min(x,mn.x)
		mn.y = min(y,mn.y)
		mn.z = min(z,mn.z)
		mx.x = max(x,mx.x)
		mx.y = max(y,mx.y)
		mx.z = max(z,mx.z)
	end
	return {max=mx,min=mn}
end

function boxFromBounds(bounds,mesh)
	local mn,mx = bounds.min,bounds.max
	local verts = {
		{mn.x,mn.y,mn.z,1},
		{mx.x,mn.y,mn.z,1},
		{mx.x,mn.y,mx.z,1},
		{mn.x,mn.y,mx.z,1},
		{mn.x,mx.y,mn.z,1},
		{mx.x,mx.y,mn.z,1},
		{mx.x,mx.y,mx.z,1},
		{mn.x,mx.y,mx.z,1},
	}
	if mesh then 
		mesh:setVertices(verts,1)
		return mesh
	end
	local map = {
		1,2,5, 5,2,6,
		2,3,6, 6,3,7,
		3,4,7, 7,4,8,
		4,1,8, 8,1,5,
	}
	local mesh = gfx.newMesh(zformat2,verts,"triangles","dynamic")
	mesh:setVertexMap(map)
	return mesh
end

function stripToTriangles(verts)
	local verts2 = {}
	for v=3,#verts,1 do
		if v%2==1 then 
			table.insert(verts2,lume.clone(verts[v-2]))
			table.insert(verts2,lume.clone(verts[v-1]))
			table.insert(verts2,lume.clone(verts[v]))
		else
			table.insert(verts2,lume.clone(verts[v]))
			table.insert(verts2,lume.clone(verts[v-1]))
			table.insert(verts2,lume.clone(verts[v-2]))
		end
	end
	return verts2
end

function editor.startWallCut(path,index,style,multi)
	pathsPanel.changed = {"floors",path.key}
	pathsPanel.cutTarget={"floors",path.key}
	pathsPanel.wallCutting = true

	pathsPanel.wallAngle = vec2.angle_to(
		vec2(
			path.points[index].x,
			path.points[index].z),
		vec2(
			path.points[index==#path.points and 1 or index+1].x,
			path.points[index==#path.points and 1 or index+1].z))

	pathsPanel.wallMat = mat4.scale(mat4(),mat4(),vec3(2))
	pathsPanel.wallMat:rotate(pathsPanel.wallMat, pathsPanel.wallAngle, vec3.unit_y)
	pathsPanel.wallMat:translate(pathsPanel.wallMat, -vec3(path.points[index]) )

	--pathsPanel.wallOffset = vec3(path.points[index])

	log("point",inspect(path.points[index]))
	log("mat  ",inspect(pathsPanel.wallMat*vec3()))

	pathsPanel.cutStyle = style
	pathsPanel.cutPath = path
	pathsPanel.cutPoint = index
	pathsPanel.cutPlane = {
		position 	= vec3(path.points[index]), 
		tri 			= {
			vec3(path.points[index]),
			vec3(path.points[index==#path.points and 1 or index+1]),
			vec3(path.points[index])-vec3(0,100,0)
		}
	}
	pathsPanel.cutPlane.normal 		= cpml.mesh.normal(pathsPanel.cutPlane.tri)
	path.cuts = path.cuts or {}
	--{index=index,bounds={-1,80},shape={-10,20, 60,20, 60,40, -10,40}},
	
end

function editor.removeWallCuts(path,index)
	if index then 
		for c,cut in lume.ripairs(path.cuts) do
			if cut.index == index then 
				table.remove(path.cuts,c)
			end
		end
	else 
		path.cuts={}
	end
	pathsPanel.changed = {path.mode,path.key}
	scene.time=-1
	path.refresh=true
end

function editor.addLink(point,dest)
	room.links[point.id] = room.links[point.id] or {point,dest}
end

function scene.evaluateRamp(plane,x,z,t)
	if plane.isLink then 
		local dist1 = distPointToLineSegment({x,z},{plane.testShape[1],plane.testShape[2]},{plane.testShape[3],plane.testShape[4]})
		local dist2 = distPointToLineSegment({x,z},{plane.testShape[5],plane.testShape[6]},{plane.testShape[7],plane.testShape[8]})
		return lerp(plane.p1.y,plane.p2.y,dist1/(dist1+dist2))
	else
		local dist1 = distPointToLineSegment({x,z},{plane.shape[1],plane.shape[2]},{plane.shape[7],plane.shape[8]})
		local dist2 = distPointToLineSegment({x,z},{plane.shape[5],plane.shape[6]},{plane.shape[3],plane.shape[4]})
		return lerp(plane.p1.y,plane.p2.y,dist1/(dist1+dist2))
	end
end

function scene.updatePlanes(room)
	if #room.nextPlanes>0 then
		room.planes 	= lume.sort(room.nextPlanes,"y")
		room.nextPlanes = {}
	end
end

function scene.updatePath(entity)
	if lume.count(entity.paths)==0 then return end

	if editingLevel and  pathsPanel.open and pathsPanel.entity==entity then 
		scene.computePath(entity)
	end

	local transformIndex = 1
	local pathPos = {}
	
	if not (entity.parent.ptype == "Weld" or  editingLevel) then
		-- GO THROUGH EACH PATH AND TRANSFORM
		
		for p,path in pairs(entity.paths) do

			if #path.points > 1 and path.enabled then 

				pathPos = lume.count(pathPos)>0 and pathPos or {x=entity.bx, y=entity.by, z= entity.bz, r=entity.bangle}

				-- GET POSITION ON PATH AT CURRENT TTME
				if not editingLevel then 
					if 1>2 then--path.smoothing>pathsPanel.smoothCutoff and entity.onstage then
						-- PATH IF SMOOTHED 
						curvex,curvey=path.curve:evaluate(mc(0,path.normPos,1))
						pathPos={
							x=pathPos.x+curvex,
							y=pathPos.y+curvey,
							r=pathPos.r+0
						}
					else
						-- PATH IF STRAIGHT LINES
						local absPos = path.length*mc(0.001,path.normPos,0.999)
						local travelled = 0
						for i,dist in ipairs(path.dists) do
							if absPos>travelled and travelled+dist>absPos then 
								local normTrav = travelled/path.length 
								local nextTrav = (travelled+dist)/path.length 
								local currPos  = mc(0,(path.normPos-normTrav)/(nextTrav-normTrav),1)
								pathPos={
									x = pathPos.x + lerp( path.line[i*3-2]	, path.line[i*3+1] 	,currPos),
									y = pathPos.y + lerp( path.line[i*3-1]	, path.line[i*3+2]  ,currPos),
									z = pathPos.z + lerp( path.line[i*3]	, path.line[i*3+3]  ,currPos),
									r = pathPos.r + 0
								}
							end
							travelled = travelled+dist
						end 
					end
				end
				
				-- WHAT TO DO WHEN IT HITS THE END
				if path.normPos > 1 or path.normPos <0 then 
					if path.loop == "loop" then 
						path.normPos = 0
					elseif path.loop == "repeat" then 
						path.normPos = 0
					elseif path.loop == "reverse" then 
						path.curSpeed = path.curSpeed * -1
					end
				end

				path.normPos = path.normPos + (path.curSpeed/path.length)*50
				trace(path.normPos)
			end
		end
		
	end

	if lume.count(pathPos)>0 then 
		entity:setPosition 	(pathPos.x,pathPos.y,pathPos.z)
	end
	entity.oangle 		= pathPos.r
end



local tempVecTable = {}
local function tempVT(a,b,c)
	tempVecTable[1],tempVecTable[2],tempVecTable[3] = a,b,c
	return tempVecTable
end

local defMat,defMat2,defMat3 = mat4(),mat4(),mat4()
local sliceVec,sliceOffset,slicePosition = vec3(0,1,0),vec3(),vec3()
function scene.drawPaths(view,room,matrix)
	if level.isAnim then return end
	bench.push("drawPaths")
	bench.push("processPath")

	local room = room or view.room
	local snap 
	local dist,dist2 				
	local point 			
	local nextPoint 	
	local startScreen 
	local endScreen 	
	local x1,y1,x2,y2 
	local w3 		 			
	local wall 				
	local lineBox 		
	local lineHovered 
	local hoveredPath 

	-- trace("snapping disabled")
	if false and game.state=="Edit"  and not(room.particle) and ( pathsPanel.open or scene.time<1)  then
		lineHovered = 0
		hoveredPath = pathsPanel.hoveredPath or pathsPanel.current 
		for m, mode in pairs({"floors","ramps","paths"}) do
			for p, path in pairs(room[mode]) do
				for i, point in ipairs (path.points) do 

					--dist 				= path.dists[i] or 50
					nextPoint 	= i<#path.points and path.points[i+1] or ((mode~="ramps" and path.loop) and path.points[1] or point)
					startScreen = camera.project(point)
					endScreen 	= camera.project(nextPoint)
					x1,y1,x2,y2 = startScreen.x,startScreen.y,endScreen.x,endScreen.y
					w3 		 			= 1
					wall 				= false
					lineBox 		= {math.min(x1,x2),math.min(y1,y2),math.max(x1,x2),math.max(y1,y2)}

					if not editor.drawingLink and view == currentRoom then 
						if  room.key==currentRoom.key and pointInBB({mouse.x*game.resolution,mouse.y*game.resolution},lineBox) then 
							dist = distPointToLineSegment({mouse.x*game.resolution,mouse.y*game.resolution},{x1,y1},{x2,y2})
							if  dist<20 then 

								snap = true
								dist2         	= 1-math.dist(mouse.x*game.resolution,mouse.y*game.resolution,x2,y2)/math.dist(x1,y1,x2,y2)
								
								if mode=="floors"  then
									pathsPanel.snapPoint 				= pathsPanel.snapPoint or {}
									pathsPanel.snapPoint.x 			= -lerp( point.x, nextPoint.x  , 0.5)
									pathsPanel.snapPoint.y 			= -lerp( point.y, nextPoint.y  , 0.5)
									pathsPanel.snapPoint.z 			= -lerp( point.z, nextPoint.z  , 0.5)
									pathsPanel.snapPoint.id 		= point.id
									pathsPanel.snapPoint.room = room.isEntiy 	and room.uid 	or nil
									pathsPanel.snapPoint.floor 	= p
									pathsPanel.snapPoint.i 			= i
									pathsPanel.snapPoint.room 	= room.key
								end
								if (not editor.transforming) and (hoveredroom==nil) and (not multiTool.hovered)  and dist<10 then
								trace("FFFF")
									lineHovered     = i
									w3 				= 1.5
									pathsPanel.newPointPosition = {
										x = lerp( point.x-room.bx, nextPoint.x-room.bx  , dist2),
										y = lerp( point.y-room.by, nextPoint.y-room.by  , dist2),
										z = lerp( point.z-room.bz, nextPoint.z-room.bz  , dist2),
										i=i
									}
								end
							end
						end
					end

					if mode=="floors" then 
						if (type(point.walled)~=nil and point.walled==false) then
							w3 	 = w3*0.5
							wall = true
						end
					end
				end
			end
		end
		if room.key == currentRoom.key then
			pathsPanel.lineHovered      = (hoveredEntity==nil) and lineHovered or 0
		end
	end

	if not snap then 
		pathsPanel.snapPoint 	= nil
	end
	bench.pop("processPath")
		

			
			
	if  room.isRoom and room.terrain and room.terrain.models and not editor.debug.hide_terrain  then 

		bench.push("drawpath")
		gfx.push("all")

		debug.terrain_tris = 0

		local BBOX = {}
		local boxes = {}
		

		if LOVR or room.lighting.probes.rendering then 
			gfx.setShader(shaders.forwardRender) 
		else
			gfx.setShader(scene.drawingShadow and shaders.shadowStatic or shaders.deferredStatic)
		end

		local shader = gfx.getShader()

		if LOVE then 
			shaders.deferredStatic:send("modelMatrix",mat4.identity())
		end
		shader:send("MVP", "column",matrix or gfx.camera.vp)
		
		if not scene.drawingShadow then 
			shader:send("time", time) 
		end
		
		-- if LOVE then 	
		local drawn = 0
		for m=#room.terrain.models,1,-1 do 
			local model = room.terrain.models[m]

			if room[model.mode][model.id] then 
				if model.isGrass then 
					if   room[model.mode][model.id].enabled  then 
						drawModel(model)
					end
				else
					if model.visible == true or model.visible == nil then 
						model.m = m/#room.terrain.models

						gfx.setWireframe( (editor.debug.wireframe or not room[model.mode][model.id].enabled))

						if not (scene.drawingShadow or not editingLevel) or not model.isEdge then 
							if not( model.isEdge and (animationPanel.open or editState=="Cutting" or (editor.modelHovered and editor.modelHovered.isWall))) then 
								brightness=1
								if editingLevel then 
									if not scene.drawingShadow and not lmouse.getRelativeMode() and not animationPanel.open and editState~="Cutting" then 
										--brightness = (model.isEdge and (model.walled and 0.8 or 0.9) or 1) 
										brightness = (model == editor.modelHovered and 0.95 or 1)
										-- shader:send("stripe", (brightness<1 and not editor.pickingColor) and 1 or 0) 
									end
									model.hoverBrightness = model.hoverBrightness and easeTo(model.hoverBrightness,brightness,4) or 1
										gfx.setColor(gfx.brightness(colors.white,(model.brightness or 1)*(model.hoverBrightness or 1),true))
								else 
									gfx.setColor(1,1,1,1)
								end

								mode = 
									(room.lighting.probes.rendering or gfx.isWireframe()) and "none" 
										or (scene.drawingShadow and "front"
											or (room.lighting.vct.back and "back" or "front")
										)
								if LOVR then 
									mode = mode=="front" and "back" or (mode=="back" and "front" or mode)
								end
								gfx.setMeshCullMode(mode)
								if model.mesh then
									if room.lighting.vct.rendering then 
										if cpml.intersect.aabb_aabb(model.bbox, room.lighting.vct.sliceBox) then 
											drawn = drawn+1
											gfx.draw3D(model.mesh)
										end
									else
										gfx.draw3D(model.mesh)
									end
								end

								if not scene.drawingShadow and brightness<1 then 
								 	-- shader:send("stripe",0) 
								end
							end
						end

						if view == currentRoom then 
							bench.push("pick path")
							testCursorModel(model,room,false,true)
							if editor.debug.bounding_boxes then 
								if model.bbox then 
									if model.setBBmesh==nil or model.setBBmesh==true then 
										model.boundingMesh = boxFromBounds(model.bbox,model.boundingMesh)
										model.setBBmesh = false
									end
									if model.boundingMesh then 
										table.insert(boxes,model)
										
									end
								end
							end
							bench.pop("pick path")
						end

						if not room.bbox then 
							table.insert(BBOX,model.bbox)
						end

						gfx.setWireframe(false)
					end
				end

			else
				table.remove(room.terrain.models,m)
			end
		end
		if room.lighting.vct.rendering then 
			room.lighting.vct.drawCalls = room.lighting.vct.drawCalls+drawn
		end
		-- end

		bench.pop("drawpath")
		
		gfx.push("all")
			gfx.setMeshCullMode("none")
			gfx.setWireframe(true)
			-- gfx.setDepthMode(nil,true)
			for b,box in ipairs(boxes) do 
				gfx.setColor(1,box.rayBB and 0.5 or 1,box.rayBB and 0.5 or 1,1)
				gfx.draw(box.boundingMesh)
				box.rayBB = false
			end
		gfx.pop()


		gfx.pop()


		if (pathsPanel.wallCutting or pathsPanel.gridFade>0) and not scene.drawingShadow then 
			trace("cutwall",pathsPanel.wallCutting, pathsPanel.gridFade>0)
			shaders.forwardRender:send("fadePoint",{-pathsPanel.cutMouse3D.x,-pathsPanel.cutMouse3D.y,-pathsPanel.cutMouse3D.z})
			shaders.forwardRender:send("drawShadowed",false)
			drawModel({
				color = {1,1,1,pathsPanel.gridFade},
				mesh=editor.debugGrid,
				transform=pathsPanel.wallMat,
				wireframe = true,
				layer = -25,
				fadeDist = 200
			})
			shaders.forwardRender:send("drawShadowed",(drawShadowed~=nil))
		end

	
	end


	bench.pop("drawPaths")
end



function setVertAtt(v,n,a)
	for i=1,n,1 do 
		v[i]=v[i] or 0
	end
	v[n]=a
end


function stripMesh(mesh)
	local stripped = {}
	for i=1, #mesh/2 do 
		table.insert(stripped, mesh[i])
		table.insert(stripped, mesh[#mesh/2+i])
	end
	return stripped
end









