local empty = {}
local vBufferSize = 16000
local vBufferN = 0

local vBuffer1 = ffi.new("m_vert[?]", vBufferSize)
local vBuffer2 = ffi.new("m_vert[?]", vBufferSize)

local vData
local vPointer
vData = vBuffer1

local vBufferStart = 0
local tempTerrain = { tris = {} }
local terrainVerts = 0
local vBuf
local tv1, tv2, tv3, tv4 = vec2(), vec2(), vec2(), vec2()
local tmpPt1, tmpPt2, tmpPt3 = vec3(), vec3(), vec3()
local tempBase = { 0, 0, 0, 0 }
local tempColor = { 1, 1, 1 }
local tempHeights = { 0, 0 }
local tempHeights2 = { 0, 0, 0, 0 }
local tempFaceRemovals = { false, false, false, false }
local cos, sin, sqrt = math.cos, math.sin, math.sqrt

local totalSteps = 0
local currentStep = 0

-- print("bufsize",(ffi.sizeof("m_vert")*vBufferSize)/(1024),"kb")

local function unpackColor(c)
	return c[1], c[2], c[3], 1, 0, 0, 0, c[4]
end

local function colorTable(color)
	return color.r and { color.r, color.g, color.b, color.pattern, color.scale, color.rotation }
		or { r = color[1], g = color[2], b = color[3] }
end

local function setTable2(t, ...)
	for i = 1, #t, 1 do
		t[i] = arg[i]
	end
	return t
end

local function startVerts(alt)
	vBuf = alt and vBuffer2 or vBuffer1
  if ctx.editing then 
  	vBufferN = 0
  end
	vBufferStart = vBufferN + 1
	for k, v in pairs(tempTerrain) do
		tempTerrain[k] = nil
	end
end

local function setValues(t, v1, v2, v3, v4)
	t[1], t[2], t[3], t[4] = v1, v2, v3, v4
	return t
end

local function setValues2(a, b)
	a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15], a[16] =
		b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15], b[16]
end

local function insertValues(t, n, v1, v2, v3, v4)
	t[n] = t[n] or {}
	setValues(t[n], v1, v2, v3, v4)
end

local function vRange()
	return { vBufferStart, vBufferN }
end

local function vBufInsert(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15)
	vBufferN = vBufferN + 1

	vBuf[vBufferN].x = v0
	vBuf[vBufferN].y = v1
	vBuf[vBufferN].z = v2
	vBuf[vBufferN].w = v3
	vBuf[vBufferN].u = v4
	vBuf[vBufferN].v = v5
	vBuf[vBufferN].r = v6
	vBuf[vBufferN].g = v7
	vBuf[vBufferN].b = v8
	vBuf[vBufferN].a = v9
	vBuf[vBufferN].nx = 0
	vBuf[vBufferN].ny = 0
	vBuf[vBufferN].nz = 0
end

local slice = {}
local function setVerticesFromTableSlice(mesh, table, first, last)
	-- print("setVerticesFromTableSlice", mesh, table, first, last)
	mesh:setVertices(vData, first, last)
end

local lineV1, lineV2, lineV3, lineV4 = vec2(), vec2(), vec2(), vec2()
local tempLine, tempLineN = {}, 0
local lineCross, lineDir = vec3(), vec3()
local tempVert = vec3()

local function specialWave(t)
	if t < 0.11 then
		return sin(t * 10)
	elseif t < 0.75 then
		return cos(t)
	else
		return t
	end
end
local baluster = { height = 0, width = 0 }
local function insertThickLine(p1, p2, width, color, style, keepShape, keepAngle)
	local style = style or empty
	local segs = style.segs or 8
	local steps = style.steps or 1
	local wave = style.wave
	local length = vec3.dist(p1, p2)
	local line = tempLine
	local w, pw = width
	local v1, v2, v3, v4 = lineV1, lineV2, lineV3, lineV4
	local vertical = false --(p1.x==p2.x) and (p1.z==p2.z)
	if not keepShape then
		local j, n = 0, 0
		if vertical then -- MODEL
			if not baluster.verts then
				baluster.verts = obj_loader("models/architecture/balusters/baluster1.obj")
				for v = 1, #baluster.verts, 1 do
					baluster.height = baluster.verts[v][2] < baluster.height and baluster.verts[v][2] or baluster.height
					baluster.width = baluster.verts[v][1] < baluster.width and baluster.verts[v][1] or baluster.width
				end
			end
			local sx, sy, sz = (width / baluster.width), (length / math.abs(baluster.height))
			n = #baluster.verts

			if style.roll then
				local c = cos(style.roll)
				local s = sin(style.roll)
				local x
				local y
				for i = n, 1, -1 do
					x = baluster.verts[i][1] * sx
					y = baluster.verts[i][3] * sx
					insertValues(line, i, c * x - s * y, baluster.verts[i][2] * sy, s * x + c * y)
				end
			else
				for i = 1, n do
					insertValues(
						line,
						i,
						baluster.verts[i][1] * sx,
						baluster.verts[i][2] * sy,
						baluster.verts[i][3] * sx
					)
				end
			end
		else
			pw = wave and wave(0) * width or width
			for s = 1, steps, 1 do
				local y1, y2 = (s - 1) * (-length / steps), s * (-length / steps)
				if wave then
					w = wave((s / steps)) * width
				end
				for i = 1, segs, 1 do
					j = i == segs and 1 or i + 1
					v1:set(vec2.from_cartesian2(pw / 2, (i / segs) * pi * 2))
					v2:set(vec2.from_cartesian2(pw / 2, (j / segs) * pi * 2))
					v3:set(vec2.from_cartesian2(w / 2, (i / segs) * pi * 2))
					v4:set(vec2.from_cartesian2(w / 2, (j / segs) * pi * 2))

					n = n + 1
					insertValues(line, n, v1.x, y1, v1.y)
					n = n + 1
					insertValues(line, n, v4.x, y2, v4.y)
					n = n + 1
					insertValues(line, n, v3.x, y2, v3.y)
					n = n + 1
					insertValues(line, n, v1.x, y1, v1.y)
					n = n + 1
					insertValues(line, n, v2.x, y1, v2.y)
					n = n + 1
					insertValues(line, n, v4.x, y2, v4.y)
				end
				pw = w
			end
		end
		tempLineN = n
	end
	if not vertical and not keepAngle then
		-- local angle = axes.y:dot(lineDir)
		-- lineDir:set(vec3.sub2(p2,p1))
		-- lineCross:set(vec3.cross2(lineDir,axes.y))
		-- for i=1, tempLineN do
		--  tempVert:set(line[i][1],line[i][2],line[i][3])
		--  line[i][1],line[i][2],line[i][3] = tempVert:rotate2(pi/2,lineCross)
		-- end
	end
	for i = 1, tempLineN do
		vBufInsert(p1.x + line[i][1], p1.y + line[i][2], p1.z + line[i][3], 1, 1, 1, unpackColor(color))
	end
end

local white = { 1, 1, 1 }

local function insertWallVerts(base, y, color, remove, rc)
	local rc = rc or 0
	local remove = remove or empty
	local outer = color and (type(color[1]) == "table" and color[1] or color) or white
	local inner = color and (type(color[2]) == "table" and color[2] or color) or outer
	local r0, g0, b0 = inner[1], inner[2], inner[3]
	local r1, g1, b1 = outer[1], outer[2], outer[3]
	local r2, g2, b2 = rc == 0 and r0 or outer[1], rc == 0 and g0 or outer[2], rc == 0 and b0 or outer[3]
	local r3, g3, b3 = rc == 3 and r0 or outer[1], rc == 3 and g0 or outer[2], rc == 3 and b0 or outer[3]
	local r4, g4, b4 = rc == 4 and r0 or outer[1], rc == 4 and g0 or outer[2], rc == 4 and b0 or outer[3]
	local y1, y2, y3, y4 = y[1], y[2], y[3] or y[1], y[4] or y[2]
	vBufInsert(base[1].x, y1, base[1].y, 1, 1, 1, r1, g1, b1, 1, 0, 0, 0)
	vBufInsert(base[2].x, y3, base[2].y, 1, 1, 1, r1, g1, b1, 1, 0, 0, 0)
	vBufInsert(base[1].x, y2, base[1].y, 1, 1, 1, r1, g1, b1, 1, 0, 0, 0)
	vBufInsert(base[2].x, y3, base[2].y, 1, 1, 1, r1, g1, b1, 1, 0, 0, 0)
	vBufInsert(base[2].x, y4, base[2].y, 1, 1, 1, r1, g1, b1, 1, 0, 0, 0)
	vBufInsert(base[1].x, y2, base[1].y, 1, 1, 1, r1, g1, b1, 1, 0, 0, 0)

	vBufInsert(base[3].x, y2, base[3].y, 1, 1, 1, r2, g2, b2, 1, 0, 0, 0)
	vBufInsert(base[4].x, y3, base[4].y, 1, 1, 1, r2, g2, b2, 1, 0, 0, 0)
	vBufInsert(base[3].x, y1, base[3].y, 1, 1, 1, r2, g2, b2, 1, 0, 0, 0)
	vBufInsert(base[3].x, y2, base[3].y, 1, 1, 1, r2, g2, b2, 1, 0, 0, 0)
	vBufInsert(base[4].x, y4, base[4].y, 1, 1, 1, r2, g2, b2, 1, 0, 0, 0)
	vBufInsert(base[4].x, y3, base[4].y, 1, 1, 1, r2, g2, b2, 1, 0, 0, 0)

	if not remove[1] then
		vBufInsert(base[4].x, y4, base[4].y, 1, 1, 1, r1, g1, b1, 1, 0, 0, 0) -- right / remove 1
		vBufInsert(base[2].x, y4, base[2].y, 1, 1, 1, r1, g1, b1, 1, 0, 0, 0)
		vBufInsert(base[2].x, y3, base[2].y, 1, 1, 1, r1, g1, b1, 1, 0, 0, 0)
		vBufInsert(base[2].x, y3, base[2].y, 1, 1, 1, r1, g1, b1, 1, 0, 0, 0)
		vBufInsert(base[4].x, y3, base[4].y, 1, 1, 1, r1, g1, b1, 1, 0, 0, 0)
		vBufInsert(base[4].x, y4, base[4].y, 1, 1, 1, r1, g1, b1, 1, 0, 0, 0)
	end

	if not remove[2] then
		vBufInsert(base[1].x, y1, base[1].y, 1, 1, 1, r1, g1, b1, 1, 0, 0, 0) -- left / remove 2
		vBufInsert(base[1].x, y2, base[1].y, 1, 1, 1, r1, g1, b1, 1, 0, 0, 0)
		vBufInsert(base[3].x, y2, base[3].y, 1, 1, 1, r1, g1, b1, 1, 0, 0, 0)
		vBufInsert(base[3].x, y2, base[3].y, 1, 1, 1, r1, g1, b1, 1, 0, 0, 0)
		vBufInsert(base[3].x, y1, base[3].y, 1, 1, 1, r1, g1, b1, 1, 0, 0, 0)
		vBufInsert(base[1].x, y1, base[1].y, 1, 1, 1, r1, g1, b1, 1, 0, 0, 0)
	end

	if not remove[3] then
		vBufInsert(base[3].x, y1, base[3].y, 1, 1, 1, r3, g3, b3, 1, 0, 0, 0) -- bottom / remove 3
		vBufInsert(base[2].x, y3, base[2].y, 1, 1, 1, r3, g3, b3, 1, 0, 0, 0)
		vBufInsert(base[1].x, y1, base[1].y, 1, 1, 1, r3, g3, b3, 1, 0, 0, 0)
		vBufInsert(base[3].x, y1, base[3].y, 1, 1, 1, r3, g3, b3, 1, 0, 0, 0)
		vBufInsert(base[4].x, y3, base[4].y, 1, 1, 1, r3, g3, b3, 1, 0, 0, 0)
		vBufInsert(base[2].x, y3, base[2].y, 1, 1, 1, r3, g3, b3, 1, 0, 0, 0)
	end

	if not remove[4] then
		vBufInsert(base[1].x, y2, base[1].y, 1, 1, 1, r4, g4, b4, 1, 0, 0, 0) -- top / remove 4
		vBufInsert(base[2].x, y4, base[2].y, 1, 1, 1, r4, g4, b4, 1, 0, 0, 0)
		vBufInsert(base[3].x, y2, base[3].y, 1, 1, 1, r4, g4, b4, 1, 0, 0, 0)
		vBufInsert(base[3].x, y2, base[3].y, 1, 1, 1, r4, g4, b4, 1, 0, 0, 0)
		vBufInsert(base[2].x, y4, base[2].y, 1, 1, 1, r4, g4, b4, 1, 0, 0, 0)
		vBufInsert(base[4].x, y4, base[4].y, 1, 1, 1, r4, g4, b4, 1, 0, 0, 0)
	end
end

local edgeVec = vec2()
local edgeNorm = vec2()
local edgeNum = 0

local function insertEdge(edges, x1, y1, x2, y2)
	local edge = {
		0,
		0,
		0,
		0,
		norm = vec2(),
		p1 = vec2(),
		p2 = vec2(),
	}
	setValues(edge, x1, y1, x2, y2)
	edgeVec:set(edge[3] - edge[1], edge[4] - edge[2])
	edgeVec.x, edgeVec.y = -edgeVec.y, edgeVec.x
	edgeNorm:set(vec2.normalize2(edgeVec))
	edgeVec = edgeNorm * (edgeInset or 0)
	setValues(edge, edge[1] + edgeVec.x, edge[2] + edgeVec.y, edge[3] + edgeVec.x, edge[4] + edgeVec.y)
	edge.p1:set(edge[1], edge[2])
	edge.p2:set(edge[3], edge[4])
	edge.norm:set(edgeNorm)
	table.insert(edges, edge)
end

local smoothPairs, smoothGroups, smoothN = { {}, {} }, {}, 1
local vertsTaken
local function getSharedVerts(verts)
	local new = false
	for i = #smoothPairs[1], 1, -1 do
		local v, w = inTable(verts, smoothPairs[1][i]), inTable(verts, smoothPairs[2][i])
		if v or w then
			if not v then
				table.insert(verts, smoothPairs[1][i])
			end
			if not w then
				table.insert(verts, smoothPairs[2][i])
			end
			table.remove(smoothPairs[1], i)
			table.remove(smoothPairs[2], i)
			new = true
		end
	end
	return verts, new
end

local function averageNormals(verts, smooth, first, last)
	local smooth, first, last = smooth or 0.5, first or 0, last or #verts
	local abs = math.abs
	local sqrt = math.sqrt

	for v = first, last, 1 do
		for w = first, last, 1 do
			if
				v ~= w
				and abs(verts[w][1] - verts[v][1]) < 1
				and abs(verts[w][2] - verts[v][2]) < 1
				and abs(verts[w][3] - verts[v][3]) < 1
				and verts[v][11] * verts[w][11] + verts[v][12] * verts[w][12] + verts[v][13] * verts[w][13] > smooth
			then
				smoothPairs[1][smoothN] = v
				smoothPairs[2][smoothN] = w
				smoothN = smoothN + 1
			end
		end
	end
	-- print("\npairs")
	-- print(inspect(smoothPairs))
	local shared, new
	local retry
	while #smoothPairs[1] > 0 do
		if not retry then
			shared = getSharedVerts({ smoothPairs[1][#smoothPairs[2]], smoothPairs[2][#smoothPairs[2]] })
			if #shared > 0 then
				retry = true
			end
		else
			shared, new = getSharedVerts(shared)
			if not new then
				table.insert(smoothGroups, shared)
				retry = false
			end
		end
	end

	if #smoothGroups > 0 then
		-- print("\ngroups")
		-- print(inspect(smoothGroups))
		for g = 1, #smoothGroups, 1 do
			local nx, ny, nz = 0, 0, 0
			for i = 1, #smoothGroups[g], 1 do
				local v = smoothGroups[g][i]
				nx, ny, nz = nx + verts[v][11], ny + verts[v][12], nz + verts[v][13]
			end
			local l = sqrt(nx * nx + ny * ny + nz * nz)
			nx, ny, nz = nx / l, ny / l, nz / l
			for i = 1, #smoothGroups[g], 1 do
				local v = smoothGroups[g][i]
				verts[v][11], verts[v][12], verts[v][13] = nx, ny, nz
			end
		end
	end
end

local nx, ny, nz, ba, ca = 0, 0, 0, vec3(), vec3()
local function scene_calcVertNormals(verts, first, last, smooth, offset)
	-- print( "normals" )
	local t, l = 0
	local first, last = first or (type(verts) == "cdata" and 0 or 1), last or #verts
	local sqrt = math.sqrt
	smoothPairs = smooth and { {}, {} } or smoothPairs
	smoothGroups = smooth and {} or smoothGroups
	vertsTaken = smooth and {} or vertsTaken
	smoothN = 1

	for v = first, last - 2, 3 do
		ba.x, ba.y, ba.z = verts[v + 1].x - verts[v].x, verts[v + 1].y - verts[v].y, verts[v + 1].z - verts[v].z
		ca.x, ca.y, ca.z = verts[v + 2].x - verts[v].x, verts[v + 2].y - verts[v].y, verts[v + 2].z - verts[v].z

		nx, ny, nz = ba.y * ca.z - ba.z * ca.y, ba.z * ca.x - ba.x * ca.z, ba.x * ca.y - ba.y * ca.x

		l = sqrt(nx * nx + ny * ny + nz * nz)
		nx, ny, nz = nx / l, ny / l, nz / l
		-- print(nx,ny,nz)
		verts[v].nx, verts[v].ny, verts[v].nz = nx, ny, nz
		verts[v + 1].nx, verts[v + 1].ny, verts[v + 1].nz = nx, ny, nz
		verts[v + 2].nx, verts[v + 2].ny, verts[v + 2].nz = nx, ny, nz
	end

	if not ctx.editing and smooth then
		-- averageNormals(verts,smooth,first,last)
	end
end

local function insertTerrain(entity, t)
	bench.push("insertTerrain")
	local t = t or tempTerrain
	local verts = vBuf
	local first, last = vBufferStart,vBufferN
	local range = last - first + 1

	local model, insert
  if ctx.editing then 
  	for k, v in pairs(entity.terrain.models) do
  		if
  			v.isFloor == t.isFloor
  			and v.isRoof == t.isRoof
  			and v.isEdge == t.isEdge
  			and v.isWall == t.isWall
  			and v.index == t.index
  			and v.id == t.id
  		then
  			model, insert = v, k
  		end
  	end

  	scene_calcVertNormals(verts, first, last, t.smooth)

  	-- print("shared",sharedFound)
  	local makeNew = false

  	if insert and entity.terrain.models[insert] then
  		if range == 0 then
  			if entity.terrain.models[insert].mesh then
  				entity.terrain.models[insert].mesh:release()
  				entity.terrain.models[insert].mesh = nil
  			end
  			table.insert(entity.terrainRemovals, insert)
  		else
  			if entity.terrain.models[insert].mesh and entity.terrain.models[insert].nverts == range then
  				setVerticesFromTableSlice(entity.terrain.models[insert].mesh, verts, first, last)
  			else
  				if entity.terrain.models[insert].mesh then
  					entity.terrain.models[insert].mesh:release()
  				end
  				entity.terrain.models[insert].mesh =
  					gfx.newMesh(zformat, range, t.triMode or "triangles", ctx.editing and "stream" or "static")
  				setVerticesFromTableSlice(entity.terrain.models[insert].mesh, verts, first, last)
  			end
  		end
  	else
  		if range > 0 then
  			makeNew = true
  		end
  	end

  	if makeNew then
  		table.insert(entity.terrain.models, {
  			mesh = gfx.newMesh(zformat, range, t.triMode or "triangles", ctx.editing and "stream" or "static"),
  		})
  		setVerticesFromTableSlice(entity.terrain.models[#entity.terrain.models].mesh, verts, first, last)
  	end
  	terrainVerts = terrainVerts + range

  	local index = insert or (makeNew and #entity.terrain.models or 0)

  	if entity.terrain.models[index] then
  		for k, v in pairs(t) do
  			if k ~= "verts" then
  				entity.terrain.models[index][k] = v
  			end
  		end
  		if t.texture then
  			t.texture:setWrap("repeat", "repeat")
  		end
  		if entity.terrain.models[index].mesh then
  			-- gfx.setTexture(entity.terrain.models[index].mesh,patterns.atlas)
  		end
  		entity.terrain.models[index].nverts = range
  		entity.terrain.models[index].newbb = true
  		entity.terrain.models[index].isTerrain = true
  		entity.terrain.models[index].bbox = entity.terrain.models[index].bbox
  			or scene_calcBBOX(entity.terrain.models[index])
  	end

  end

	bench.pop("insertTerrain")
end

local function dc(path, inherit)
	local copy = deepcopy(path.color)
	copy.inherit = inherit
	return copy
end

local function defaultColors(path, parts)
	local color = path.colors
	path.inner, path.outer = (path.inner or deepcopy(path.color)), (path.outer or deepcopy(path.color))
	color.color = colorTable(path.color)
	color.inner = colorTable(path.inner)
	color.outer = colorTable(path.outer)
	for p, part in ipairs(parts) do
		path[part].color = path[part].color or dc(path)
		path[part].inner = path[part].inner or dc(path)
		path[part].outer = path[part].outer or dc(path)
		color[part] = color[part] or {}
		color[part].color = colorTable(path[part].color)
		color[part].outer = colorTable(path[part].outer)
		color[part].inner = colorTable(path[part].inner)
	end
	return color
end

local function lerp(a, b, t)
	return (1 - t) * a + t * b
end

local function gradientStep(steps, gradient, color)
	currentStep = currentStep + 1
	totalSteps = totalSteps + 1
	setValues(
		color.ramp,
		lerp(gradient[1].floor.inner[1], gradient[2].floor.inner[1], currentStep / steps),
		lerp(gradient[1].floor.inner[2], gradient[2].floor.inner[2], currentStep / steps),
		lerp(gradient[1].floor.inner[3], gradient[2].floor.inner[3], currentStep / steps)
	)
end

local function checkSnap(floor, startPoint, endPoint)
	local index = select(
		2,
		lume.match(floor, function(x)
			return x.id == (startPoint and startPoint.snapPoint.id or endPoint.snapPoint.id)
		end)
	)
	return floor[index].moved or floor[index == #floor and 1 or index + 1].moved
end

local function cutWall(floor, point)
	local cutWall
	local anyCuts = false

	if not floor.railing.enabled and floor.cuts then
		local baseSection
		if type(point) == "table" then
			baseSection = { 0, 0, point.dist, 0, point.dist, floor.wall.height, 0, floor.wall.height }
		end

		-- for c,cut in ipairs(floor.cuts) do
		--  if cut.index == point.index  and cut.shape then
		--  end
		-- end
	end
	return cutWall, anyCuts
end

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

local vp1, vp2 = vec3(), vec3()
local nextPoint, prevPoint
local midPoint = vec2()
local sines, cosines = {}, {}
local lPoints, rPoints = {}, {}
local ilPoints, irPoints = {}, {}
local blPoints, brPoints = {}, {}
local midPoints = {}
local angs, dists = {}, {}
local summedInner, summedOuter = {}, {}
local bar, bar2 = {}, {}
local railOffset = vec3()
local railInset = vec2()
local railSet = vec2()
local railv1, railv2 = vec3(), vec3()
local post1, post2 = vec3(), vec3()
local wallOffset = vec2()
local floors = {}
local ramps = {}
local cornice = {}

local function addPaths(entity)
	local blankmat = mat4()
	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 game.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 function addFloors(entity)
	edgeNum = 0

	local removals = {}
	bench.push("floors")
	floors = toArray(entity.floors, floors)
	for _ = 1, #floors, 1 do
		local floor = floors[_]
		local f = floor.id

		if
			game.time == 0
			or (pathsPanel.changed and pathsPanel.changed[1] == "floors" and pathsPanel.changed[2] == floor.id)
			or (pathsPanel.cutting and pathsPanel.cutTarget[2] == floor.id)
			or editor.debug.force_build
		then
		else
			goto next
		end

		bench.push("1")

		local y = floor.points[1].y
		floor.isFloor = true
		floor.shape = {}
		floor.shape2 = {}
		floor.edges = { nocopy = true }
		floor.group = #entity.nextPlanes + 1

		local dist, ang, ang0
		local edgeVerts, wallVerts, wallVerts2 = {}, {}, {}
		local midLerp = vec3()

		floor.wall = floor.wall or { height = 0.3, enabled = false }
		floor.roof = floor.roof or { height = 0.3, enabled = false }
		floor.floor = floor.floor or { enabled = true }
		floor.cornice = floor.cornice or { style = 1, enabled = false }
		floor.railing = floor.railing
			or { count = 0.3, width = 5, rails = { enabled = false, count = 1 }, enabled = false }
		floor.railing.rails = floor.railing.rails or { enabled = false, count = 1 }
		floor.railing.width = floor.railing.width or 5
		floor.thick = true
		floor.thickness = 8

		local color = defaultColors(floor, { "floor", "wall", "roof" })
		local wallHeight = floor.wall.height
		local makeThick = floor.enabled and floor.thick

		bench.pop("1")

		bench.push("2")

		local inter, inter2, sect, sect2
		for p, point in ipairs(floor.points) do
			point.index = p

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

			point.dist = math.dist(point.x, point.z, nextPoint.x, nextPoint.z)
			dist = math.dist(prevPoint.x, prevPoint.z, nextPoint.x, nextPoint.z)
			ang0 = math.angle(point.x, point.z, nextPoint.x, nextPoint.z)
			ang = ang0 + pi / 2

			angs[p] = ang
			dists[p] = math.dist(point.x, point.z, nextPoint.x, nextPoint.z)
			sines[p] = math.sin(ang - pi / 2)
			cosines[p] = math.cos(ang - pi / 2)
			summedOuter[p] = (p > 1 and summedOuter[p - 1] + dists[p - 1] or 0)

			if not midPoints[p] then
				midPoints[p] = vec2()
			end
			if not rPoints[p] then
				rPoints[p] = vec2()
			end
			if not lPoints[p] then
				lPoints[p] = vec2()
			end
			if not irPoints[p] then
				irPoints[p] = vec2()
			end
			if not ilPoints[p] then
				ilPoints[p] = vec2()
			end
			if not brPoints[p] then
				brPoints[p] = vec2()
			end
			if not blPoints[p] then
				blPoints[p] = vec2()
			end

			-- OUTER
			midLerp:set(vec3.lerp2(point, nextPoint, 0.5))
			midPoints[p]:set(
				midLerp.x + math.cos(ang) * floor.thickness * 0.5,
				midLerp.z + math.sin(ang) * floor.thickness * 0.5
			)
			midPoint:set(midLerp.x + math.cos(ang) * floor.thickness, midLerp.z + math.sin(ang) * floor.thickness)

			rPoints[p]:set(midPoint:add2(vec2.from_cartesian2(dist, ang - pi / 2)))
			lPoints[p]:set(midPoint:sub2(vec2.from_cartesian2(dist, ang - pi / 2)))

			if lPoints[p - 1] and rPoints[p - 1] then
				inter, sect = math.intersect(lPoints[p], rPoints[p], lPoints[p - 1], rPoints[p - 1])
				if inter then
					lPoints[p]:set(sect)
					rPoints[p - 1]:set(sect)
				end
			end

			if p == #floor.points then
				inter, sect = math.intersect(lPoints[p], rPoints[p], lPoints[1], rPoints[1])
				if inter then
					rPoints[p]:set(sect)
					lPoints[1]:set(sect)
				end
			end

			-- INNER
			midLerp:set(vec3.lerp2(point, nextPoint, 0.5))
			midPoint:set(midLerp.x + math.cos(ang) * 0, midLerp.z + math.sin(ang) * 0)

			ilPoints[p]:set(midPoint:sub2(vec2.from_cartesian2(dist, ang - pi / 2)))
			irPoints[p]:set(midPoint:add2(vec2.from_cartesian2(dist, ang - pi / 2)))

			if ilPoints[p - 1] and irPoints[p - 1] then
				inter, sect = math.intersect(ilPoints[p], irPoints[p], ilPoints[p - 1], irPoints[p - 1])
				inter2, sect2 = math.intersect(ilPoints[p], irPoints[p], lPoints[p - 1], rPoints[p - 1])
				if inter then
					ilPoints[p]:set(sect)
					irPoints[p - 1]:set(sect)
				end
				if inter2 then
					blPoints[p]:set(sect2)
					brPoints[p - 1]:set(sect2)
				end
			end

			if p == #floor.points then
				inter, sect = math.intersect(ilPoints[p], irPoints[p], ilPoints[1], irPoints[1])
				if inter then
					irPoints[p]:set(sect)
					ilPoints[1]:set(sect)
				end
			end

			summedInner[p] = (p > 1 and summedInner[p - 1] + vec2.dist(ilPoints[p - 1], irPoints[p - 1]) or 0)
		end

		bench.pop("2")

		bench.push("3")
		-- print("floor.points[1].y", floor.points[1].x, floor.points[1].y, floor.points[1].z)
		for p, point in ipairs(floor.points) do
			table.insert(floor.shape, point.x)
			table.insert(floor.shape, point.z)
			table.insert(floor.shape2, point.x)
			table.insert(floor.shape2, point.z)

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

			vp1.x, vp1.y, vp1.z = point.x, point.y, point.z
			vp2.x, vp2.y, vp2.z = nextPoint.x, nextPoint.y, nextPoint.z

			local wall = floor.walled == "closed"
				or (floor.walled == "gaps" and (point.walled == true or type(point.walled) == "nil"))

			if wall then
				insertEdge(floor.edges, point.x, point.z, nextPoint.x, nextPoint.z)
			end

			local wallCornice

			if ctx.editing or floor.wall.enabled then
				bench.push("3.1")
				if floor.wall.enabled and not floor.railing.enabled and wall then
					table.insert(floor.shape2, midPoints[p].x)
					table.insert(floor.shape2, midPoints[p].y)
				end

				if ctx.editing then
					startVerts()
					local r, g, b = unpackColor(color.floor.inner)
					vBufInsert(ilPoints[p].x, y - 0.25, ilPoints[p].y, 1, 1, 1, r, g, b, 1)
					vBufInsert(irPoints[p].x, y - 0.25, irPoints[p].y, 1, 1, 1, r, g, b, 1)
					vBufInsert(rPoints[p].x, y - 0.25, rPoints[p].y, 1, 1, 1, r, g, b, 1)
					vBufInsert(lPoints[p].x, y - 0.25, lPoints[p].y, 1, 1, 1, r, g, b, 1)

					local edgeVisible = not (floor.wall.enabled and wall and not floor.railing.enabled)
					-- {floor.color.r*1.1,floor.color.g*1.1,floor.color.b*1.1,1}

					-- print("EDGE")
					insertTerrain(entity, {
						isEdge = true,
						mode = "floors",
						visible = edgeVisible,
						triMode = "fan",
						index = p,
						id = floor.id,
						layer = -2,
					})
				end
				bench.pop("3.1")

				startVerts()
				if floor.wall.enabled and wall then
					bench.push("3.2")
					local isPrevWalled = floor.walled == "closed"
						or (floor.walled == "gaps" and (prevPoint.walled == true or prevPoint.walled == nil))
					local isNextWalled = floor.walled == "closed"
						or (floor.walled == "gaps" and (nextPoint.walled == true or nextPoint.walled == nil))

					local cutWall, anyCuts = cutWall(floor, point)
					local success
					local triangulated
					local offset = wallOffset

					if floor.railing.enabled or makeThick then
						offset:set(vec2.from_cartesian2(floor.thickness, angs[p]))
					end

					bench.pop("3.2")

					if anyCuts then
						bench.push("cut walls")
						local ang0 = math.angle(point.x, point.z, nextPoint.x, nextPoint.z)
						local sinA, cosA = math.sin(ang0), math.cos(ang0)
						local first = vBufferN + 1

						local r, g, b = unpackColor(color.floor.outer)
						for c, cut in ipairs(cutWall) do
							success, triangulated = pcall(mth.triangulate, cut)
							if success then
								for t, tri in ipairs(triangulated) do
									vBufInsert(
										point.x + cosA * tri[1],
										point.y - tri[2],
										point.z + sinA * tri[1],
										1,
										0,
										0,
										r,
										g,
										b,
										1
									)
									vBufInsert(
										point.x + cosA * tri[3],
										point.y - tri[4],
										point.z + sinA * tri[3],
										1,
										0,
										0,
										r,
										g,
										b,
										1
									)
									vBufInsert(
										point.x + cosA * tri[5],
										point.y - tri[6],
										point.z + sinA * tri[5],
										1,
										0,
										0,
										r,
										g,
										b,
										1
									)
								end
							end
						end

						local last = vBufferN

						if makeThick then
							local r, g, b = unpackColor(color.wall.inner)

							for v = first, last, 3 do
								vBufInsert(
									vBuffer1[v + 2].x + offset.x,
									vBuffer1[v + 2].y,
									vBuffer1[v + 2].z + offset.y,
									1,
									0,
									0,
									r,
									g,
									b,
									1
								)
								vBufInsert(
									vBuffer1[v + 1].x + offset.x,
									vBuffer1[v + 1].y,
									vBuffer1[v + 1].z + offset.y,
									1,
									0,
									0,
									r,
									g,
									b,
									1
								)
								vBufInsert(
									vBuffer1[v].x + offset.x,
									vBuffer1[v].y,
									vBuffer1[v].z + offset.y,
									1,
									0,
									0,
									r,
									g,
									b,
									1
								)
							end

							local r, g, b = unpackColor(color.wall.outer)
							for c, cut in ipairs(cutWall) do
								for v = 1, #cut, 2 do
									vn = v == #cut - 1 and 1 or v + 2
									vBufInsert(
										point.x + cosA * cut[v] + offset.x,
										point.y - cut[v + 1],
										point.z + sinA * cut[v] + offset.y,
										1,
										0,
										0,
										r,
										g,
										b,
										1
									)
									vBufInsert(
										point.x + cosA * cut[vn],
										point.y - cut[vn + 1],
										point.z + sinA * cut[vn],
										1,
										0,
										0,
										r,
										g,
										b,
										1
									)
									vBufInsert(
										point.x + cosA * cut[v],
										point.y - cut[v + 1],
										point.z + sinA * cut[v],
										1,
										0,
										0,
										r,
										g,
										b,
										1
									)

									vBufInsert(
										point.x + cosA * cut[v] + offset.x,
										point.y - cut[v + 1],
										point.z + sinA * cut[v] + offset.y,
										1,
										0,
										0,
										r,
										g,
										b,
										1
									)
									vBufInsert(
										point.x + cosA * cut[vn] + offset.x,
										point.y - cut[vn + 1],
										point.z + sinA * cut[vn] + offset.y,
										1,
										0,
										0,
										r,
										g,
										b,
										1
									)
									vBufInsert(
										point.x + cosA * cut[vn],
										point.y - cut[vn + 1],
										point.z + sinA * cut[vn],
										1,
										0,
										0,
										r,
										g,
										b,
										1
									)
								end
							end
						end

						bench.pop("cut walls")
					else
						if floor.railing.enabled then
							bench.push("railing")

							local segs = 8
							local v1, v2 = railv1, railv2
							local bars = mf((dists[p] / 25) * floor.railing.count) + 1
							local i, j = 1 / bars
							post1:set((ilPoints[p].x + lPoints[p].x) / 2, vp1.y, (ilPoints[p].y + lPoints[p].y) / 2)
							post2:set((irPoints[p].x + rPoints[p].x) / 2, vp1.y, (irPoints[p].y + rPoints[p].y) / 2)

							railInset:set(vec2.from_cartesian2(floor.railing.width, angs[p]))
							floor.railing.width = floor.railing.width or floor.thickness
							for b = 0, bars, 1 do
								j = b * i
								if j == 0 then
									railOffset:set(post1)
									railSet:set(0, 0)
								elseif j == 1 then
									railOffset:set(post2)
									railSet:set(0, 0)
								else
									railOffset:set(vec3.lerp2(vp1, vp2, j))
									railSet:set(railInset)
								end
								v1:set(railOffset.x + railSet.x, railOffset.y, railOffset.z + railSet.y)
								v2:set(railOffset.x + railSet.x, railOffset.y - wallHeight, railOffset.z + railSet.y)
								insertThickLine(v1, v2, floor.railing.width, color.wall.outer, { roll = angs[p] })
							end

							if not floor.roof.enabled then
								local b = 5
								insertWallVerts(
									{ ilPoints[p], irPoints[p], lPoints[p], rPoints[p] },
									{ y - wallHeight, y - wallHeight - b },
									{ color.inner, color.inner }
								)
							end
							if floor.railing.rails.enabled then
								floor.railing.rails.width = floor.railing.rails.width or 5
								local mr = lume.round(floor.railing.rails.count)
								local step = wallHeight / (mr + 1)
								for m = 1, mr, 1 do
									local yShift = step * m
									v1:set(post1.x, post1.y - yShift, post1.z)
									v2:set(post2.x, post2.y - yShift, post2.z)
									insertThickLine(v1, v2, floor.railing.rails.width, color.wall.outer)
								end
							end
							bench.pop("railing")
						elseif makeThick then
							bench.push("3.2.2.4")
							insertWallVerts(
								{ ilPoints[p], irPoints[p], lPoints[p], rPoints[p] },
								{ y, y - wallHeight },
								{ color.wall.outer, color.wall.inner },
								{ isNextWalled, isPrevWalled, floor.floor.enabled, floor.roof.enabled }
							)
							bench.pop("3.2.2.4")
						else
							bench.push("3.2.2.5")
							local r, g, b = unpackColor(color.wall.color)
							vBufInsert(point.x, y, point.z, 1, 0, 0, r, g, b, 1)
							vBufInsert(nextPoint.x, y, nextPoint.z, 1, 0, 0, r, g, b, 1)
							vBufInsert(point.x, y - wallHeight, point.z, 1, 1, 1, r, g, b, 1)
							vBufInsert(nextPoint.x, y - wallHeight, nextPoint.z, 1, 1, 1, r, g, b, 1)
							bench.pop("3.2.2.5")
						end
					end

					if not floor.railing.enabled then
						cornice.height = cornice.height or 0
						cornice.width = cornice.width or 0
						cornice.depth = cornice.depth or 0
						cornice.dists = cornice.dists or {}

						bench.push("3.3.1")
						if floor.cornice.enabled then
							-- if not cornice.verts then
							--  local sqrt = math.sqrt
							--  cornice.verts =  obj_loader("models/architecture/cornices/cornice_11.obj")
							--  for v=1,#cornice.verts,1 do
							--    cornice.verts[v][1]=-cornice.verts[v][1]
							--    cornice.verts[v][2]=-cornice.verts[v][2]
							--    --cornice.verts[v][3]=-cornice.verts[v][3]
							--    cornice.width=cornice.verts[v][1]<cornice.width and cornice.verts[v][1] or cornice.width
							--    cornice.height=cornice.verts[v][2]<cornice.height and cornice.verts[v][2] or cornice.height
							--    cornice.depth=cornice.verts[v][3]<cornice.depth and cornice.verts[v][3] or cornice.depth
							--  end

							--  cornice.width = math.abs(cornice.width)
							--  cornice.height = math.abs(cornice.height)
							--  cornice.depth = math.abs(cornice.depth)

							--  for v=1,#cornice.verts,1 do
							--    cornice.verts[v][1]=cornice.verts[v][1]+cornice.width
							--    cornice.verts[v][2]=cornice.verts[v][2]+cornice.height
							--    cornice.verts[v][3]=cornice.verts[v][3]+cornice.depth+floor.thickness
							--  end
							--  scene_calcVertNormals(cornice.verts,nil,nil,nil)--,0.5)
							--  cornice.mesh=gfx.newMesh(zformat,cornice.verts,"triangles")
							-- end
							-- wallCornice =  {
							--  posL=vec3(point.x,point.y-wallHeight,point.z),
							--  posR=vec3(nextPoint.x,point.y-wallHeight,nextPoint.z),
							--  dir = vec3(math.cos(angs[p]+pi/2),0,math.sin(angs[p]+pi/2)),
							--  model=cornice,
							--  ang=angs[p],
							--  dist=dists[p],
							--  color = {unpackColor(color.inner)},
							--  lslice=vec2.sub(ilPoints[p],lPoints[p]):rotate(pi/2),
							--  rslice=vec2.sub(irPoints[p],rPoints[p]):rotate(-pi/2)
							-- }
							-- wallCornice.vec=wallCornice.dir*wallCornice.model.width
						end
						bench.pop("3.3.1")

						bench.push("3.3.2")
						-- if     (floor.wall.inner.pattern and floor.wall.inner.pattern>0)
						--    or (floor.wall.outer.pattern and floor.wall.outer.pattern>0)  then
						--  for v=vBufferStart,vBufferN,1 do
						--    -- print(vBuffer[v][14])
						--    if  floor.wall.inner.pattern>0 and (useData and vBuffer1[v].pi or vBuffer1[v][14]) == floor.wall.inner.pattern then
						--      local x,y,w,h = unpack(patterns.size[floor.wall.inner.pattern])
						--      local s = 10
						--              if useData then
						--          vBuffer1[v].u=((summedInner[p]+math.dist(vBuffer1[v].x,vBuffer1[v].z,point.x,point.z)))/(w/s)
						--          vBuffer1[v].v=((-vBuffer1[v].y))/(h/s)
						--              else
						--                vBuffer1[v][5]=((summedInner[p]+math.dist(vBuffer1[v][1],vBuffer1[v][3],point.x,point.z)))/(w/s)
						--                vBuffer1[v][6]=((-vBuffer1[v][2]))/(h/s)
						--              end
						--      -- vBuffer[v][5],vBuffer[v][6] = vec2(vBuffer[v][5],vBuffer[v][6]):rotate(pi/3):unpack()
						--    end
						--  end
						-- end
						bench.pop("3.3.2")
					end

					local makeNew = false

					terrainVerts = terrainVerts + vBufferN - vBufferStart + 1
				end

				insertTerrain(entity, {
					isWall = true,
					mode = "floors",
					index = p,
					id = floor.id,
					smooth = 0.4,
					cornice = wallCornice,
				})
			end
		end
		bench.pop("3")

		bench.push("trifloor")
		table.insert(entity.nextPlanes, floor)

		local tris = {}
		local thicc = floor.thickness
		local triangulated = mth.triangulate(floor.shape)
		if not triangulated then
			bench.pop("trifloor")
			goto next
		end
		local triangulated2 = triangulated
		local tried, tris2 = pcall(mth.triangulate, floor.shape2)
		if tried then
			triangulated2 = tris2
		end
    if not triangulated2 then 
      goto next
    end

		bench.pop("trifloor")

		-- FLOOR
		bench.push("4")
		startVerts()
		if floor.floor.enabled then
			for t, tri in ipairs(triangulated2) do
				vBufInsert(tri[1], y, tri[2], 1, 0, 0, unpackColor(color.floor.inner))
				vBufInsert(tri[3], y, tri[4], 1, 0, 0, unpackColor(color.floor.inner))
				vBufInsert(tri[5], y, tri[6], 1, 0, 0, unpackColor(color.floor.inner))
			end

			if makeThick then
				for t, tri in ipairs(triangulated) do
					vBufInsert(tri[5], y + thicc, tri[6], 1, 0, 0, unpackColor(color.floor.outer))
					vBufInsert(tri[3], y + thicc, tri[4], 1, 0, 0, unpackColor(color.floor.outer))
					vBufInsert(tri[1], y + thicc, tri[2], 1, 0, 0, unpackColor(color.floor.outer))
				end
				for p, point in ipairs(floor.points) do
					nextPoint = floor.points[p == #floor.points and 1 or p + 1]
					vBufInsert(point.x, point.y + thicc, point.z, 1, 0, 0, unpackColor(color.floor.outer))
					vBufInsert(nextPoint.x, nextPoint.y, nextPoint.z, 1, 0, 0, unpackColor(color.floor.outer))
					vBufInsert(point.x, point.y, point.z, 1, 0, 0, unpackColor(color.floor.outer))

					vBufInsert(point.x, point.y + thicc, point.z, 1, 0, 0, unpackColor(color.floor.outer))
					vBufInsert(nextPoint.x, nextPoint.y + thicc, nextPoint.z, 1, 0, 0, unpackColor(color.floor.outer))
					vBufInsert(nextPoint.x, nextPoint.y, nextPoint.z, 1, 0, 0, unpackColor(color.floor.outer))
				end
			end
		end
		bench.pop("4")

		bench.push("4.1")
		insertTerrain(entity, {
			isFloor = true,
			mode = "floors",
			id = f,
		})
		bench.pop("4.1")

		-- ROOF
		bench.push("5")
		startVerts()
		if floor.roof.enabled then
			for t, tri in ipairs(triangulated) do
				vBufInsert(tri[1], y - wallHeight - thicc, tri[2], 1, 0, 0, unpackColor(color.roof.outer))
				vBufInsert(tri[3], y - wallHeight - thicc, tri[4], 1, 0, 0, unpackColor(color.roof.outer))
				vBufInsert(tri[5], y - wallHeight - thicc, tri[6], 1, 0, 0, unpackColor(color.roof.outer))
			end
			if makeThick then
				for t, tri in ipairs(triangulated2) do
					vBufInsert(tri[5], y - wallHeight, tri[6], 1, 0, 0, unpackColor(color.roof.inner))
					vBufInsert(tri[3], y - wallHeight, tri[4], 1, 0, 0, unpackColor(color.roof.inner))
					vBufInsert(tri[1], y - wallHeight, tri[2], 1, 0, 0, unpackColor(color.roof.inner))
				end
				for p, point in ipairs(floor.points) do
					nextPoint = floor.points[p == #floor.points and 1 or p + 1]
					vBufInsert(point.x, point.y - wallHeight, point.z, 1, 0, 0, unpackColor(color.roof.outer))
					vBufInsert(
						nextPoint.x,
						nextPoint.y - wallHeight - thicc,
						nextPoint.z,
						1,
						0,
						0,
						unpackColor(color.roof.outer)
					)
					vBufInsert(point.x, point.y - wallHeight - thicc, point.z, 1, 0, 0, unpackColor(color.roof.outer))

					vBufInsert(point.x, point.y - wallHeight, point.z, 1, 0, 0, unpackColor(color.roof.outer))
					vBufInsert(
						nextPoint.x,
						nextPoint.y - wallHeight,
						nextPoint.z,
						1,
						0,
						0,
						unpackColor(color.roof.outer)
					)
					vBufInsert(
						nextPoint.x,
						nextPoint.y - wallHeight - thicc,
						nextPoint.z,
						1,
						0,
						0,
						unpackColor(color.roof.outer)
					)
				end
			end
		end
		bench.pop("5")

		bench.push("5.1")
		insertTerrain(entity, {
			isTerrain = true,
			isRoof = true,
			mode = "floors",
			id = f,
		})
		bench.pop("5.1")
		::next::
	end

	bench.pop("floors")
end

local function addRamps(entity)
	local cut = 0
	local cut2 = 0
	local l1, l2, m1, m2 = {}, {}, {}, {}
	local l2y, l1y
	-- if true then return end
	bench.push("ramps")
	ramps = toArray(entity.ramps, ramps)
	for _ = 1, #ramps, 1 do
		local ramp = ramps[_]
		local r = ramp.id

		local startPoint = ramp.points[1]
		local endPoint = ramp.points[#ramp.points]
		local model, insert
		for i, v in ipairs(entity.terrain.models) do
			if v.id == ramp.key then
				insert, model = i, v
				break
			end
		end
		if
			not model
			or editor.debug.force_build
			or game.time == 0
			or (pathsPanel and pathsPanel.changed and pathsPanel.changed[1] == "ramps" and pathsPanel.changed[2] == f)
			or (
				pathsPanel
					and pathsPanel.changed
					and (pathsPanel.changed[1] == "ramps" and pathsPanel.changed[2] == ramp.id)
				or (endPoint and endPoint.snapPoint and entity.floors and entity.floors[endPoint.snapPoint.floor] and pathsPanel.changed and pathsPanel.changed[1] == "floors" and pathsPanel.changed[2] == endPoint.snapPoint.floor and endPoint.snapPoint.id and checkSnap(
					entity.floors[endPoint.snapPoint.floor].points,
					nil,
					endPoint
				))
				or (
					startPoint
					and startPoint.snapPoint
					and entity.floors
					and entity.floors[startPoint.snapPoint.floor]
					and pathsPanel.changed
					and pathsPanel.changed[1] == "floors"
					and pathsPanel.changed[2] == startPoint.snapPoint.floor
					and startPoint.snapPoint.id
					and checkSnap(entity.floors[startPoint.snapPoint.floor].points, startPoint, nil)
				)
			)
		then
			bench.push("1")

			ramp.thick = true

			ramp.floor = ramp.floor or { enabled = true }
			ramp.wall = ramp.wall or { height = 0.3, enabled = false }
			ramp.roof = ramp.roof or { height = 0.3, enabled = false }
			ramp.railing = ramp.railing or { count = 0.3, enabled = false }
			ramp.cornice = { style = 1, enabled = false }
			ramp.steps = ramp.steps or { count = 0.3, height = 1, enabled = true }
			ramp.thick = true
			ramp.thickness = 8

			local color = defaultColors(ramp, { "floor", "wall", "roof" })

			local wallHeight = ramp.wall.height

			ramp.pointsL = {}
			ramp.pointsR = {}

			local rEnds
			local lEnds
			local firstAng = 0

			local destination
			local startShape
			local endShape

			local gradient = {}

			bench.pop("1")

			bench.push("2")
			if ramp.dest then
				addLink(entity, endPoint, ramp.dest)
			elseif entity.links[endPoint.id] then
				entity.links[endPoint.id] = nil
			end

			if ramp.source then
				addLink(entity, startPoint, ramp.source)
			elseif entity.links[startPoint.id] then
				entity.links[startPoint.id] = nil
			end

			if startPoint and startPoint.snapPoint then
				if entity.floors and entity.floors[startPoint.snapPoint.floor] then
					local floor = entity.floors[startPoint.snapPoint.floor]
					local l, p
					for i, v in ipairs(floor.points) do
						if v.id == startPoint.snapPoint.id then
							l, p = v, i
							break
						end
					end
					if l then
						gradient[1] = floor.colors
						local r = floor.points[p + 1] and floor.points[p + 1] or floor.points[1]
						ramp.points[1].y = l.y
						ramp.points[1].x = (l.x + r.x) / 2
						ramp.points[1].z = (l.z + r.z) / 2
						startShape = { l = l, r = r }
					end
				end
			end

			if endPoint and endPoint.snapPoint then
				if entity.floors and entity.floors[endPoint.snapPoint.floor] then
					local floor = entity.floors[endPoint.snapPoint.floor]
					local l, p
					for i, v in ipairs(floor.points) do
						if v.id == endPoint.snapPoint.id then
							l, p = v, i
							break
						end
					end
					if l then
						gradient[2] = floor.colors
						local r = floor.points[p + 1] and floor.points[p + 1] or floor.points[1]
						ramp.points[#ramp.points].y = l.y
						ramp.points[#ramp.points].x = (l.x + r.x) / 2
						ramp.points[#ramp.points].z = (l.z + r.z) / 2
						endShape = { l = l, r = r }
					end
				end
			end

			totalSteps = 0
			currentStep = 0

			bench.pop("2")

			verts = {}

			bench.push("3")
			startVerts(ramp.thick and ramp.steps.enabled)

			for p, point in ipairs(ramp.points or {}) do
				if p < #ramp.points or p == 1 then
					local link = (p < #ramp.points - 1)

					local plane = {
						isRamp = true,
						y = math.max(ramp.points[p].y, ramp.points[p + 1].y),
						p = ramp.points[p],
						p1 = ramp.points[p],
						p2 = ramp.points[p + 1],
						p3 = ramp.points[p + 1],
						group = #entity.nextPlanes + 1,
					}

					plane.length = math.dist(plane.p1.x, plane.p1.z, plane.p2.x, plane.p2.z)
					plane.ang = math.angle(plane.p1.x, plane.p1.z, plane.p2.x, plane.p2.z)

					local ang2 = plane.ang * 1
					firstAng = ang2
					if link then
						ang2 = math.angle(
							ramp.points[p + 1].x,
							ramp.points[p + 1].z,
							ramp.points[p + 2].x,
							ramp.points[p + 2].z
						)
						cut2 = math.abs(math.sin(math.abs(plane.ang - ang2))) * ramp.width
					else
						cut2 = 0
					end

					plane.shape = {
						plane.p1.x + math.cos(plane.ang - pi / 2) * ramp.width,
						plane.p1.z + math.sin(plane.ang - pi / 2) * ramp.width,
						plane.p2.x + math.cos(plane.ang - pi / 2) * ramp.width,
						plane.p2.z + math.sin(plane.ang - pi / 2) * ramp.width,
						plane.p2.x + math.cos(plane.ang + pi / 2) * ramp.width,
						plane.p2.z + math.sin(plane.ang + pi / 2) * ramp.width,
						plane.p1.x + math.cos(plane.ang + pi / 2) * ramp.width,
						plane.p1.z + math.sin(plane.ang + pi / 2) * ramp.width,
					}

					local p1, p2, p3 = tmpPt1, tmpPt2, tmpPt3
					tmpPt1:set(plane.p1.x + math.cos(plane.ang) * cut, 0, plane.p1.z + math.sin(plane.ang) * cut)
					tmpPt2:set(plane.p2.x - math.cos(plane.ang) * cut2, 0, plane.p2.z - math.sin(plane.ang) * cut2)
					tmpPt3:set(plane.p3.x + math.cos(ang2) * cut2, 0, plane.p3.z + math.sin(ang2) * cut2)

					p1.y = floors_evaluateRamp(plane, p1.x, p1.z)
					p2.y = floors_evaluateRamp(plane, p2.x, p2.z)
					p3.y = link
							and lerp(
								ramp.points[p + 1].y,
								ramp.points[p + 2].y,
								cut2 / math.dist(plane.p2.x, plane.p2.z, ramp.points[p + 2].x, ramp.points[p + 2].z)
							)
						or p2.y

					plane.p1 = p1
					plane.p2 = p2

					plane.length = math.dist(p1.x, p1.z, p2.x, p2.z)
					plane.yDiff = math.abs(p1.y - p2.y)

					plane.incline = (math.abs(p2.y - p1.y) / plane.length) * math.abs(p2.y - p1.y)

					plane.shape = {
						p1.x + math.cos(plane.ang - pi / 2) * ramp.width,
						p1.z + math.sin(plane.ang - pi / 2) * ramp.width,
						p2.x + math.cos(plane.ang - pi / 2) * ramp.width,
						p2.z + math.sin(plane.ang - pi / 2) * ramp.width,
						p2.x + math.cos(plane.ang + pi / 2) * ramp.width,
						p2.z + math.sin(plane.ang + pi / 2) * ramp.width,
						p1.x + math.cos(plane.ang + pi / 2) * ramp.width,
						p1.z + math.sin(plane.ang + pi / 2) * ramp.width,
					}

					if p == 1 then
						if startShape then
							plane.shape[1] = startShape.l.x
							plane.shape[2] = startShape.l.z
							plane.shape[7] = startShape.r.x
							plane.shape[8] = startShape.r.z
						end
						if ramp.source then
							plane.source = ramp.source
							ramp.source.line = {
								lerp(plane.shape[1], plane.shape[3], 0.1),
								lerp(plane.shape[2], plane.shape[4], 0.1),
								lerp(plane.shape[7], plane.shape[5], 0.1),
								lerp(plane.shape[8], plane.shape[6], 0.1),
								lerp(p1.y, p2.y, 0.1),
							}
						end
					end

					if p == #ramp.points - 1 then
						if endShape then
							plane.shape[5] = endShape.l.x
							plane.shape[6] = endShape.l.z
							plane.shape[3] = endShape.r.x
							plane.shape[4] = endShape.r.z
						end
						if ramp.dest then
							plane.dest = ramp.dest
							ramp.dest.line = {
								lerp(plane.shape[3], plane.shape[1], 0.1),
								lerp(plane.shape[4], plane.shape[2], 0.1),
								lerp(plane.shape[5], plane.shape[7], 0.1),
								lerp(plane.shape[6], plane.shape[8], 0.1),
								lerp(p2.y, p1.y, 0.1),
							}
						end
					end

					if not ctx.editing and ramp.walled ~= "open" then
						plane.edges = { nocopy = true }
						insertEdge(plane.edges, plane.shape[1], plane.shape[2], plane.shape[3], plane.shape[4])
						insertEdge(
							plane.edges,
							plane.shape[1 + 4],
							plane.shape[2 + 4],
							plane.shape[3 + 4],
							plane.shape[4 + 4]
						)
					end

					local steps = mu((vec3.dist(p1, p2) / 100) * 4) --mu((ramp.steps+0.01)*16)
					local stepHeight = math.abs(p1.y - p2.y) / steps

					if p == 1 then
						lEnds = { { x = plane.shape[1], y = p1.y, z = plane.shape[2], m = stepHeight } }
						rEnds = { { x = plane.shape[7], y = p1.y, z = plane.shape[8], m = stepHeight } }
					end
					lEnds[2] = { x = plane.shape[3], y = p2.y, z = plane.shape[4], m = stepHeight }
					rEnds[2] = { x = plane.shape[5], y = p2.y, z = plane.shape[6], m = stepHeight }

					table.insert(entity.nextPlanes, plane)

					if ramp.steps.enabled then
						vBufInsert(plane.shape[7], p1.y, plane.shape[8], 1, 0, 0, unpackColor(color.ramp))
						vBufInsert(plane.shape[1], p1.y, plane.shape[2], 1, 0, 0, unpackColor(color.ramp))
						local step
						local stepSize = 1 / steps
						for i = 1, steps, 1 do
							step = i * stepSize
							if not ramp.thick then
								--gradientStep()
							end

							vBufInsert(
								lerp(plane.shape[7], plane.shape[5], step),
								lerp(p1.y, p2.y, (i - 1) * stepSize),
								lerp(plane.shape[8], plane.shape[6], step),
								1,
								0,
								0,
								unpackColor(color.ramp)
							)
							vBufInsert(
								lerp(plane.shape[1], plane.shape[3], step),
								lerp(p1.y, p2.y, (i - 1) * stepSize),
								lerp(plane.shape[2], plane.shape[4], step),
								1,
								0,
								0,
								unpackColor(color.ramp)
							)
							vBufInsert(
								lerp(plane.shape[7], plane.shape[5], step),
								lerp(p1.y, p2.y, step),
								lerp(plane.shape[8], plane.shape[6], step),
								1,
								0,
								0,
								unpackColor(color.ramp)
							)
							vBufInsert(
								lerp(plane.shape[1], plane.shape[3], step),
								lerp(p1.y, p2.y, step),
								lerp(plane.shape[2], plane.shape[4], step),
								1,
								0,
								0,
								unpackColor(color.ramp)
							)
						end
						vBufInsert(plane.shape[3], p2.y, plane.shape[4], 1, 0, 0, unpackColor(color.ramp))
						vBufInsert(plane.shape[5], p2.y, plane.shape[6], 1, 0, 0, unpackColor(color.ramp))
					else
						vBufInsert(plane.shape[1], p1.y, plane.shape[2], 1, 0, 0, unpackColor(color.ramp))
						vBufInsert(plane.shape[7], p1.y, plane.shape[8], 1, 0, 0, unpackColor(color.ramp))
						vBufInsert(plane.shape[3], p2.y, plane.shape[4], 1, 0, 0, unpackColor(color.ramp))
						vBufInsert(plane.shape[5], p2.y, plane.shape[6], 1, 0, 0, unpackColor(color.ramp))
					end

					bench.push("corners")
					if link then
						local corner = {
							edges = { nocopy = true },
							round = true,
							isRamp = true,
							isLink = true,
							y = math.max(
								floors_evaluateRamp(plane, p2.x, p2.z),
								floors_evaluateRamp(plane, p3.x, p3.z)
							),
							p1 = p2,
							p2 = p3,
							group = #entity.nextPlanes + 1,
						}

						corner.height = math.abs(corner.p2.y - corner.p1.y)

						corner.incline = corner.height / math.dist(corner.p1.x, corner.p1.z, corner.p2.x, corner.p2.z)

						local ang3 = (plane.ang + ang2) / 2 + (plane.ang - ang2 > 0 and pi / 2 or -pi / 2)

						l1 = {
							plane.shape[3],
							plane.shape[4],
							p3.x + math.cos(ang2 - pi / 2) * ramp.width,
							p3.z + math.sin(ang2 - pi / 2) * ramp.width,
						}
						l2 = {
							p3.x + math.cos(ang2 + pi / 2) * ramp.width,
							p3.z + math.sin(ang2 + pi / 2) * ramp.width,
							plane.shape[5],
							plane.shape[6],
						}
						m1 = {
							x = (l1[1] + l1[3]) / 2,
							y = (corner.p1.y + corner.p2.y) / 2,
							z = (l1[2] + l1[4]) / 2,
						}
						m2 = {
							x = (l2[1] + l2[3]) / 2,
							y = (corner.p1.y + corner.p2.y) / 2,
							z = (l2[2] + l2[4]) / 2,
						}

						corner.testShape = { l2[3], l2[4], l1[1], l1[2], l1[3], l1[4], l2[1], l2[2] }

						if corner.round then
							local adiff = math.abs(plane.ang - ang2)
							local cut3 =
								lume.smooth(0, cut2, (adiff > pi / 2 and adiff / (pi * 2.5) or adiff) / (pi / 2))
							local cut4 = (l1[1] - l1[3]) ^ 2 + (l1[2] - l1[4]) ^ 2 > (l2[1] - l2[3]) ^ 2 + (l2[2] - l2[4]) ^ 2 and cut3
								or 0
							local cut5 = cut4 == 0 and cut3 or 0
							local len1, len2 =
								math.dist(l1[1], corner.p1.y, l1[3], corner.p2.y, l1[2], l1[4]),
								math.dist(l2[1], corner.p1.y, l2[3], corner.p2.y, l2[2], l2[4])

							l1 = lume.concat({ l1[1], l1[2] }, {
								(l1[1] + l1[3]) / 2 + math.cos(ang3) * cut4,
								(l1[2] + l1[4]) / 2 + math.sin(ang3) * cut4,
							}, { l1[3], l1[4] })
							l1y = lume.concat(
								{ l1[1], corner.p1.y },
								{ (l1[1] + l1[3]) / 2, (corner.p1.y + corner.p2.y) / 2 },
								{ l1[3], corner.p2.y }
							)
							l2 = lume.concat({ l2[1], l2[2] }, {
								(l2[1] + l2[3]) / 2 + math.cos(ang3) * cut5,
								(l2[2] + l2[4]) / 2 + math.sin(ang3) * cut5,
							}, { l2[3], l2[4] })
							l2y = lume.concat(
								{ l2[1], corner.p2.y },
								{ (l2[1] + l2[3]) / 2, (corner.p1.y + corner.p2.y) / 2 },
								{ l2[3], corner.p1.y }
							)

							l1 = mth.newBezierCurve(l1)
							l1y = mth.newBezierCurve(l1y)
							l2 = mth.newBezierCurve(l2)
							l2y = mth.newBezierCurve(l2y)

							local xL, yL, zL
							local xR, yR, zR
							local detail = mu(math.max(len1, len2) / 50)

							local nx, nz
							local pL, pR
							local dL, dR = 0, 0
							for c = 0, detail, 1 do
								xL, zL = l1:evaluate(c / detail)
								_, yL = l1y:evaluate(c / detail)
								xR, zR = l2:evaluate((detail - c) / detail)
								_, yR = l2y:evaluate((detail - c) / detail)

								if ramp.steps.enabled and pL and pR then
									vBufInsert(xR, pR[2], zR, 1, 0, 0, unpackColor(color.ramp))
									vBufInsert(xL, pL[2], zL, 1, 0, 0, unpackColor(color.ramp))
								end
								vBufInsert(xR, yR, zR, 1, 0, 0, unpackColor(color.ramp))
								table.insert(
									ramp.pointsR,
									1,
									{ x = xR, y = yR, z = zR, n = 1, m = corner.height / detail }
								)
								if c < detail and not ctx.editing and ramp.walled ~= "open" then
									nx, nz = l2:evaluate((detail - c - 1) / detail)
									insertEdge(corner.edges, xR, zR, nx, nz)
								end

								vBufInsert(xL, yL, zL, 1, 0, 0, unpackColor(color.ramp))
								table.insert(
									ramp.pointsL,
									{ x = xL, y = yL, z = zL, n = 1, m = corner.height / detail }
								)
								if c > 0 then
								end
								if c < detail and not ctx.editing and ramp.walled ~= "open" then
									nx, nz = l1:evaluate((c + 1) / detail)
									insertEdge(corner.edges, xL, zL, nx, nz)
								end

								pL = { xL, yL, zL }
								pR = { xR, yR, zR }
							end

							l1 = l1:render(2)
							l2 = l2:render(2)
							corner.shape = lume.concat(l1, l2)
						else
							if not ctx.editing and ramp.walled ~= "open" then
								insertEdge(corner.edges, l1[1], l1[2], l1[3], l1[4])
								insertEdge(corner.edges, l2[1], l2[2], l2[3], l2[4])
							end
							corner.shape = corner.testShape
						end

						table.insert(entity.nextPlanes, corner)
					end
					bench.pop("corners")

					cut = cut2
				end
			end

			bench.pop("3")

			if rEnds and #rEnds == 2 then
				table.insert(ramp.pointsR, rEnds[1])
				table.insert(ramp.pointsR, 1, rEnds[2])
				table.insert(ramp.pointsL, lEnds[2])
				table.insert(ramp.pointsL, 1, lEnds[1])
			end

			bench.push("4")
			if ramp.thick and ramp.steps.enabled then
				local y, h, h2
				local first, last = vBufferStart + 3, vBufferN
				local cutSteps = ramp.wall.enabled and ramp.floor.enabled
				startVerts()

				if ramp.steps.height and ramp.steps.height < 0.9 then
					h2 = (ramp.steps.height + 0.3) * 6
				end
				local grad = (gradient[1] and gradient[2] and ramp.totalSteps and ramp.gradient) and gradientStep
					or nothing
				local steps = ramp.totalSteps

				for i = first, last - 2, 2 do
					grad(steps, gradient, color)
					y = vBuffer2[i + 1] and vBuffer2[i + 1].y or vBuffer2[i - 4].y
					h = math.abs(vBuffer2[i].y - y)
					y = math.max(vBuffer2[i].y, y)

					insertWallVerts(
						setValues(
							tempBase,
							tv1:set(vBuffer2[i - 3].x, vBuffer2[i - 3].z),
							tv2:set(vBuffer2[i - 2].x, vBuffer2[i - 2].z),
							tv3:set(vBuffer2[i - 1].x, vBuffer2[i - 1].z),
							tv4:set(vBuffer2[i].x, vBuffer2[i].z)
						),
						setValues(tempHeights, h2 and y - h + h2 or y, y - h),
						color.ramp,
						setValues(tempFaceRemovals, cutSteps, cutSteps, cutSteps)
					)
				end
			end
			bench.pop("4")

			ramp.totalSteps = totalSteps

			bench.push("5")
			if ramp.wall.enabled then
				local offset, offset2
				local side
				for i = 1, 2, 1 do
					side = i == 1 and ramp.pointsR or ramp.pointsL
					for i = 1, #side - 1, 1 do
						offset = vec2.from_cartesian(
							ramp.thickness,
							math.angle(side[i].x, side[i].z, side[i + 1].x, side[i + 1].z) + pi / 2
						)
						offset2 = vec2.from_cartesian(
							-0.1,
							math.angle(side[i].x, side[i].z, side[i + 1].x, side[i + 1].z) + pi / 2
						)
						insertWallVerts(
							setValues(
								tempBase,
								tv1:set(side[i].x + offset2.x, side[i].z + offset2.y),
								tv2:set(side[i + 1].x + offset2.x, side[i + 1].z + offset2.y),
								tv3:set(side[i].x + offset.x, side[i].z + offset.y),
								tv4:set(side[i + 1].x + offset.x, side[i + 1].z + offset.y)
							),
							setValues(
								tempHeights2,
								side[i].y + 6,
								side[i].y - wallHeight,
								side[i + 1].y + 6,
								side[i + 1].y - wallHeight
							),
							setValues(tempColor, color.wall.outer, color.wall.inner)
						)
					end
				end
			end
			bench.pop("5")

			bench.push("6")
			if ramp.roof.enabled then
				local wL, wR = ramp.pointsL, ramp.pointsR
				local j = #ramp.pointsR
				for i = 1, #ramp.pointsL - 1, 1 do
					insertWallVerts(
						setValues(
							tempBase,
							tv1:set(wL[i].x, wL[i].z),
							tv2:set(wL[i + 1].x, wL[i + 1].z),
							tv3:set(wR[j].x, wR[j].z),
							tv4:set(wR[j - 1].x, wR[j - 1].z)
						),
						setValues(
							tempHeights2,
							wL[i].y - wallHeight + 6,
							wL[i].y - wallHeight - 1.4,
							wL[i + 1].y - wallHeight + 6,
							wL[i + 1].y - wallHeight - 1.4
						),
						setValues(tempColor, color.roof.outer, color.roof.inner),
						setValues(tempFaceRemovals, i < #wL - 1, i > 1),
						3
					)
					j = j - 1
				end
			end
			bench.pop("6")

			insertTerrain(entity, {
				color = { ramp.color.r, ramp.color.g, ramp.color.b, 1 },
				isRamp = true,
				id = r,
				mode = "ramps",
				smooth = 0.4,
				transform = mat4(),
				parent = entity,
			})
		end
	end
	bench.pop("ramps")
end

function floors_addPlanes(entity)
	-- makingTerrain = false

	local add = false
	if cmdDown and input_keyPressed("r") then
		game.time = 0
		add = true
	end
	if editor.setUpdateLinks then
		add = true
	end
	if pathsPanel.changed or game.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")
	addPaths(entity)
	addFloors(entity)
	addRamps(entity)
	bench.pop("build")

  -- if not ctx.editing then 
    -- entity.terrain.mesh = gfx.newMesh(zformat,100000,"triangles")
  -- end

	table.sort(entity.terrainRemovals)
	for i, r in lume.ripairs(entity.terrainRemovals) do
		table.remove(entity.terrain.models, r)
	end
	if not lfs.isFused() and #entity.terrain.models > 0 then
		trace("terrain models", #entity.terrain.models)
	end
	entity.terrainRemovals = {}
end

function floors_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 floors_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 }
	game.time = -1
	path.refresh = true
end

function floors_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
