local e_methods = {}
local e_meta = { __index = e_methods }
local temp = mat4()
local mat = mat4()
local blankMat = mat4()
local r, g, b
local tMat1, tMat2, tMat3, tMat4 = mat4(), mat4(), mat4(), mat4()
local color = { r = 0, g = 0, b = 0, a = 0 }
local selectedE
local hoveredE
local hoveredM
local blankMat = mat4.identity()
local function setValues(t, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17)
	t[1], t[2], t[3], t[4], t[5], t[6], t[7], t[8], t[9], t[10], t[11], t[12], t[13], t[14], t[15], t[16], t[17] =
		v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17
	return t
end

local function dist(x1, y1, x2, y2)
	return ((x2 - x1) ^ 2 + (y2 - y1) ^ 2) ^ 0.5
end

local function vecPalette(palette)
	local p = { { 0, 0, 0, 0 } }

	for c, color in ipairs(palette) do
		table.insert(p, { color.r, color.g, color.b, 1 })
	end

	return p
end

function e_methods.setPosition(e, x, y, z)
	if type(x) == "table" or vec3.is_vec3(x) then
		y = x.y
		z = x.z
		x = x.x
	end
	e.x, e.y, e.z = x or e.x, y or e.y, mc(-4000, z or e.z, 4000)
	if ctx.editing then
		e.ox, e.oy, e.oz = x or e.ox, y or e.oy, z or e.oz
		e.bx, e.by, e.bz = x or e.bx, y or e.by, z or e.bz
	end
	e:updateTransform()
end

function e_methods.getPosition(e)
	return ((e.selected and editor.transformTime > 1) and e.nextPosition or e.position)
end

function e_methods.setOrigin(e, x, y, z)
	e:setPosition(x and e.x + x - e.ox or e.x, y and e.y + y - e.oy or e.y, z and e.z + z - e.oz or e.z)
	e.ox, e.oy, e.oz = x or e.ox, y or e.oy, z or e.oz
end

function e_methods.setBase(e, x, y, z)
	e.bx, e.by, e.bz = x or e.bx, y or e.by, z or e.bz
end

function e_methods.move(e, x, y, z)
	if x then
		if type(x) == "number" then
			e:setPosition(e.x + x, e.y + y, e.z + (z or 0))
		else
			e:setPosition(e.x + x.x, e.y + x.y, e.z + x.z)
		end
	else
		e:setPosition(e.x + e.v.x, e.y + e.v.y, e.z + e.v.z)
	end
end

function e_methods.setVelocity(e, x, y, z)
	e.v.x = mc(e.vmin.x, x or e.v.x, e.vmax.x)
	e.v.y = mc(e.vmin.y, y or e.v.y, e.vmax.y)
	e.v.z = mc(e.vmin.z, z or e.v.z, e.vmax.z)
end

function e_methods.accelerate(e, x, y, z) end

function e_methods.changeVelocity(e, x, y, z)
	e.v = {
		x = x and e.v.x + x or e.v.x,
		y = y and e.v.y + y or e.v.y,
		z = z and e.v.z + z or e.v.z,
	}
end

function e_methods.setMaxVelocity(e, x, y, z, x2, y2, z2)
	e.vmin = { x = x2 and x or -x, y = y2 and y or -y, z = z2 and z or -z }
	e.vmax = { x = x2 or x, y = y2 or y, z = z2 or z }
end

function e_methods.setDamping(e, x, y, z)
	e.vdamp = { x = x, y = y or x, z = z or x }
end

function e_methods.damp(e, x, y, z)
	e.v.x = e.v.x * (type(x) == "table" and x.x or x or e.vdamp.x)
	e.v.y = e.v.y * (type(x) == "table" and x.y or y or x or e.vdamp.y)
	e.v.z = e.v.z * (type(x) == "table" and x.z or z or x or e.vdamp.z)
end

function e_methods.setScale(e, x, y, z)
	if y then
		e.scale:set(x, y, z)
	else
		e.scale:set(x, x, x)
	end
	e:updateTransform()
end

function e_methods.setRotation(e, angle, axis)
	local axis = type(axis) == "string" and axes[axis] or axis or axes.z
	e.orientation = quat.rotate(angle, axis)
	e:updateTransform()
end

function e_methods.rotate(e, angle, axis)
	local axis = type(axis) == "string" and axes[axis] or axis or axes.z
	e.orientation = e.orientation * quat.rotate(angle, axis)
	e:updateTransform()
end

function e_methods.transfer(e, pos, dest)
	e.nextRoom = dest
	e.nextSet = game_findSet(dest)
	table.insert(game.transfers, { entity = e, source = room.key, dest = dest, pos = pos })
end

function e_methods.updateTransform(e)
	e.position.x, e.position.y, e.position.z = e.x, e.y, e.z
	e.transform = e.transform or mat4()
	if editor.gizmo.dragging then
		e.nextPosition = vec3.add(e.position, editor.gizmo.translation)
		e.transform:identity()
		e.transform:scale(e.transform, e.scale)
		e.transform = e.transform * mat4.from_quaternion(e.orientation)
		e.transform:translate(e.transform, e.nextPosition)
	elseif editor.gizmo.rotating then
		e.nextOrientation = e.orientation * quat.rotate(editor.gizmo.angle, editor.gizmo.axis)
		e.transform:identity()
		e.transform:scale(e.transform, e.scale)
		e.transform = e.transform * mat4.from_quaternion(e.nextOrientation)
		e.transform:translate(
			e.transform,
			editor.gizmo.position - (editor.gizmo.position - e.position):rotate(-editor.gizmo.angle, editor.gizmo.axis)
		)
	elseif editor.gizmo.scaling then
		e.nextScale = e.scale * editor.gizmo.scale
		e.transform:identity()
		e.transform:scale(e.transform, e.nextScale)
		e.transform = e.transform * mat4.from_quaternion(e.orientation)
		e.transform:translate(e.transform, e.position)
	else
		e.nextScale = e.scale
		e.nextOrientation = e.orientation
		e.nextPosition = e.position
		e.transform:identity()
		e.transform:scale(e.transform, e.scale)
		e.transform:mul(mat4.from_quaternion(e.orientation), e.transform)
		e.transform:translate(e.transform, e.position)
	end

	return e.transform
end

function e_methods.applyTransform(e)
	if editor.gizmo.dragging then
		e.position = e.nextPosition
	elseif editor.gizmo.rotating then
		e.position = editor.gizmo.position
			- (editor.gizmo.position - e.position):rotate(-editor.gizmo.angle, editor.gizmo.axis)
		e.orientation = e.nextOrientation
	elseif editor.gizmo.scaling then
		e.scale = e.nextScale
	end
	e.x, e.y, e.z = e.position:unpack()
end

--e_methods.setLayerVisible
--e_methods.showLayer
--e_methods.hideLayer

function e_methods.setLayerSolo(e, layer)
	--if e.fdata then
	--  for i,l in ipairs(e.fdata) do
	--    l.visible=(l.name==layer)
	--    if (l.name==layer) then
	--      --trace(l.name)
	--    end
	--  end
	--end
end

function e_methods.setColor(e, r, g, b, a)
	e.color = { r = r, g = g, b = b, a = a or 1 }
end

function e_methods.dist(e, e2)
	return (e and e2) and math.dist3(e.x, e.y, e.z, e2.x, e2.y, e2.z) or false
end

function e_methods.floorDist(e, e2)
	return e.plane.group == e2.plane.group and math.dist(e.x, e.z, e2.x, e2.z) or math.huge
end

function e_methods.setLayerByAngle(e, a)
	local a2 = a + pi + pi / 16 + pi / 32
	local p = pi / 4
	local dirs = {
		{ 0, p, "side", true },
		{ p, p * 2.25, "south side", true },
		{ p * 2.25, p * 2.75, "south" },
		{ p * 2.75, p * 4, "south side" },
		{ p * 4, p * 5, "side" },
		{ p * 5, p * 6.25, "north side" },
		{ p * 6.25, p * 6.75, "north" },
		{ p * 6.75, p * 8, "north side", true },
	}
	local facing
	local flip = e.flip.h
	for i, dir in ipairs(dirs) do
		if a2 > dir[1] and a2 < dir[2] then
			facing = dir[3]
			flip = dir[4]
		end
	end
	if facing then
		e.flip.h = flip
	end

	e_methods.facing = facing or e.facing
	e:setLayerSolo(e.facing)
end

function e_methods.setPlaybackSpeed(e, s)
	e.playbackSpeed = s
end

function e_methods.setGhost(e, g)
	e.group = (type(g) == "nil" or g) and 0 or -1
	e.ghost = (type(g) == "nil" or g)
end

function e_methods.setPath(e, p)
	e.paths = { getPath(p) }
end

function e_methods.play(e, a)
	if a then
		e:setAnim(a)
	end
	e.playing = true
end

function e_methods.pause(e)
	e.playing = false
end

function e_methods.setAnim(e, name, frame)
	local _, key = lume.match(e.symbol.animations, function(x)
		return x.name == name
	end)
	if name ~= e.animation then
		e.models = {}
		e.anim = key or e.anim
		e.adata = load_getSymbol(e.key).animations[e.anim]
	end
	e.animation = name or e.animation
	e.frame = frame or 1
	e.tick = frame and 0 or e.tick
	e.fdata = scene_getEntityFrameData(e)
end

function e_methods.setFrame(e, frame)
	e.frame = frame
	e.tick = 0
end

function e_methods.setRandomFrame(e)
	e:setFrame(math.random() * e.adata.length)
end

function e_methods.playExternal(e, t, a, o)
	local t = type(t) == "string" and scene_findEntity(t) or t
	t:play(a)
	e.visible = false
	t.visible = true
	engine_waitFor(function()
		if t.frame == t.adata.length then
			t:pause()
			e.visible = true
			t.visible = false

			if o then
				local offset = -(-t.models[t.adata.offsetLayer or 1].transform * -t.transform * vec3(0))
				e:setPosition(offset.x, offset.y, offset.z)
			end
			t.models = {}
			return true
		end
	end)
end

--self.onAnimMark()
--self.onAnimEnd()

--e_methods.setPlaying
--e_methods.add/enable Scripts
--e_methods.remove/disable Scripts

--e_methods.addChild
--e_methods.removeChild
--e_methods.destroy
--e_methods.reset

--e_methods.addWobble
--e_methods.removeWobble
--e_methods.setWobble

---------------------------------------------------
---------------------------------------------------
---------------------------------------------------

function scene_getTransform(model, t)
	return
		(model.parent and model.parent.transform and model.transform) and tempMat:mul(
		tempMat2:invert(model.transform),
		tempMat3:invert(scene_getTransform(model.parent))
	) or model.transform
end

function scene_newEntity(room, raw)
	local obj = deepcopy(raw)
	local symbol = load_getSymbol(obj.key) or load_getSymbol("_controller")

	if obj.spawn and not ctx.editing and not game.spawned[obj.spawn] then
		game.spawned[obj.spawn] = true
		scene_newEntity(
			room,
			lume.merge(obj, {
				spawn = false,
				key = game.characters[obj.spawn],
			})
		)
		return
	end

	local entity = {
		isEntity = true,
		symbol = symbol,
		key = obj.key or "_controller",
		name = obj.name or "Untitled",
		uid = obj.uid or uuid(),
		alpha = obj.alpha or 1,
		scale = obj.scale and vec3(obj.scale) or vec3(1),
		orientation = obj.orientation and quat(obj.orientation) or quat(0, 0, 1, 1),
		position = obj.position and vec3(obj.position) or vec3(0),
		x = obj.position and obj.position.x or obj.x or 0,
		y = obj.position and obj.position.y or obj.y or 0,
		z = obj.position and obj.position.z or obj.z or 0,
		spawn = obj.spawn,
		static = obj.static,
		visible = (obj.visible == nil or tostring(obj.visible) == "true"),
		gravity = obj.gravity,
		light = type(obj.light) == "table" and obj.light
			or { enabled = false, color = { r = 1, g = 1, b = 1 }, range = 1, strength = 1 },
		surface = obj.surface,
		spin = 1,
		v = obj.v or { x = 0, y = 0, z = 0 },
		vmax = { x = 999, y = 999, z = 999 },
		vmin = { x = -999, y = -999, z = -999 },
		vdamp = { x = 1, y = 1, z = 1 },
		default = obj.default or nil,
		panim = "",
		anim = obj.default or symbol.default,
		frame = 1,
		frame2 = 1,
		frame3 = 1,
		fps = 24,
		tick = 0,
		animTime = 0,
		playbackSpeed = 1,
		playing = true,
		color = { r = 1, g = 1, b = 1, a = 1 },
		brightness = 1,
		planePos1 = {},
		planePos2 = {},
		curve = obj.curve or nil,
		opacity = 1,
		img = symbol.image,
		thumb = load_imageThumb(obj.key),
		paths = obj.paths or {},
		ramps = obj.ramps or {},
		floors = obj.floors or {},
		parent = obj.parent or { id = "", d = 0, r = 0, rl = 0, r2 = 0 },
		parentE = nil,
		delete = false,
		onstage = true,
		simple = obj.simple,
		index = obj.i,
		i = #room.entities + 1,
		time = 0,
		scripts = obj.scripts or {},
		variables = obj.variables or {},
		emitter = obj.emitter or {},
		wobbles = obj.wobbles or {},
		plane = {},
		children = {},
		shapes = {},
		fdata = {},
		sdata = {},
		adata = {},
		shpdata = {},
		bounds = { { 0, 0, 0, 0, 0, 0 } },
		transferring = false,
	}

	entity.layer = entity.surface and -0.25 or 0
	entity.nextScale = vec3.clone(entity.scale)
	entity.nextOrientation = quat.clone(entity.orientation)
	entity.nextPosition = vec3.clone(entity.position)

	entity.pos = entity.position

	entity.transferring = false
	entity.prev = { x = entity.x, y = entity.y, z = entity.z }

	entity.prevZ = entity.z

	entity.oscale = entity.scale
	entity.oangle = entity.angle
	entity.ox = entity.x
	entity.oy = entity.y
	entity.oz = entity.z

	scene_setPlayer(entity)
	scene_computePath(entity)
	floors_addPlanes(entity)
	scene_setScripts(entity)

	setmetatable(entity, e_meta)

	entity.models = {}

	if entity.simple then
		-- entity.models      = {{nocopy=true,mesh=editor.simpleMesh,transform=mat4(),parent=entity}}
	end

	scene_updateEntityAnimData(entity)

	entity:updateTransform()

	if obj.dropped then
		scene_orientEntity(entity, editor.dropMode or "camera")
	end

	if not raw.dontAdd then
		table.insert(room.entities, entity)
		room.entitiesByID[entity.uid] = entity
	end

	return entity
end

function scene_orientEntity(entity, mode)
	if mode == "camera" then
		entity:rotate(ctx.viewport.camera.angle.y, "x")
		entity:rotate(ctx.viewport.camera.angle.x, "y")
	elseif mode == "camera x" then
		entity:rotate(ctx.viewport.camera.angle.x, "y")
	elseif mode == "surface" then
		log("normalll", editor.intersectNormal)
		entity:rotate(
			math.angle(
				0,
				0,
				math.dist(0, 0, editor.intersectNormal.x, editor.intersectNormal.z),
				editor.intersectNormal.y
			),
			"x"
		)
		entity:rotate(math.angle(0, 0, editor.intersectNormal.x, editor.intersectNormal.z) + pi / 2, "y")
		entity:move(-editor.intersectNormal * 0.2)
		entity.surface = true
	end
end

function editor_newEntity(key, props)
	local obj = {
		key = key,
		x = 0,
		y = 0,
		z = 0,
	}

	for k, o in pairs(props or {}) do
		if type(o) == "table" then
			for j, t in pairs(o) do
				if obj[k] then
					obj[k][j] = t
				end
			end
		else
			obj[k] = o
		end
	end

	return scene_newEntity(ctx.viewport.room, obj)
end

function scene_returnObject(entity)
	local obj = {
		name = entity.name,
		key = entity.key,
		default = entity.default,
		x = entity.position.x or entity.x,
		y = entity.position.y or entity.y,
		z = entity.position.z or entity.z,
		position = entity.position or { x = entity.x, y = entity.y, z = entity.z }, --entity.position:clone()
		orientation = {
			x = entity.orientation.x,
			y = entity.orientation.y,
			z = entity.orientation.z,
			w = entity.orientation.w,
		},
		scale = { x = entity.scale.x or 1, y = entity.scale.y or 1, z = entity.scale.z or 1 },
		static = entity.static,
		visible = entity.visible,
		gravity = entity.gravity,
		light = entity.light,
		surface = entity.surface,
		castShadow = entity.castShadow,
		recieveShadow = entity.recieveShadow,
		parent = entity.parentE and { id = entity.parent.id, ptype = entity.parent.ptype } or nil,
		uid = entity.uid,
		paths = lume.count(entity.paths) ~= 0 and entity.paths or nil,
		scripts = lume.count(entity.scripts) ~= 0 and deepcopy(entity.scripts) or nil,
		variables = lume.count(entity.variables) ~= 0 and deepcopy(entity.variables) or nil,
		emitter = lume.count(entity.emitter) ~= 0 and deepcopy(entity.emitter) or nil,
	}
	return obj
end

function scene_returnSimpleObject(entity)
	local obj = deepcopy(scene_returnObject(entity))
	obj.paths = nil
	obj.simple = true
	obj.parent = nil
	return obj
end

function getPath(p)
	for i, path in pairs(room.paths) do
		if path.name == p then
			return path
		end
	end
end

---------------------------------------------------

function scene_pushEntity(t, entity)
	if entity.simple then
		return nil
	end

	table.insert(t, scene_returnObject(entity))

	return entity
end

function scene_setDelete(entity)
	table.insert(ctx.viewport.room.toDelete, entity)
end

function scene_deleteEntity(entity)
	lume.clear(entity)
	lume.remove(ctx.viewport.room.entities, entity)
	scene_destroyEntity(entity)
	entity = nil
end

function scene_destroyEntity(entity) end

function scene_deleteEntities(room)
	if room then
		for i, entity in ipairs(room.toDelete) do
			scene_deleteEntity(entity)
		end
		room.toDelete = {}
	end
end

function scene_entitySelected(ent)
	if #editor.selectedEntities == 0 then
		return false
	end
	local there = false
	for i, e in ipairs(editor.selectedEntities) do
		if e == ent then
			there = true
		end
	end
	return there
end

function scene_getEntityByID(id)
	return ctx.viewport.room.entitiesByID[id]
end

function scene_getEntityIndexByID(id)
	local targ = -1
	for i, e in ipairs(room.entities) do
		if e.uid == id then
			targ = i
		end
	end
	return targ
end

function scene_findEntity(name)
	return lume.match(room.entities, function(x)
		return x.name == name
	end)
end

function scene_updateEntityAnimData(entity)
	if lume.find(game.toUnload, entity.key) then
		for m, model in ipairs(entity.models) do
			model.mesh:release()
		end
		-- entity.models = {}
		entity.img = nil
		entity.adata = nil
		entity.fdata = nil
		log("UNLOBe")
	end

	if not entity.symbol or entity.pointIndex or load_isLoading(entity.key) or not entity.symbol.animations then
		return
	end

	entity.img = entity.symbol.image

	if entity.img then
		if ctx.editing then
			entity.previewing = (editor.animHovered.object == entity.uid and editor.animHovered.key ~= "")
			if entity.previewing then
				entity.anim = editor.animHovered.key
				entity.adata = entity.symbol.animations[entity.anim]

				scene_updateEntityAnimation(entity, 1 / 60)
			else
				entity.frame = 1
				entity.anim = entity.default or entity.symbol.default
			end
		end

		entity.adata = entity.symbol.animations[entity.anim]
		entity.models = scene_getEntityFrameData(entity)
		if not entity.adata then
			print("adata", entity.anim, inspect(entity.symbol.animations))
		end
		entity.animation = entity.adata.name
	end
end

function scene_updateEntityAnimation(entity, dt)
	if not ctx.editing and not entity.playing then
		return
	end
	local dt = dt or (animatePanel.open and 0 or 1 / 60) or engine.dt
	entity.tick = entity.tick + dt * (entity.playbackSpeed or 1)
	entity.animTime = entity.animTime + (1 / 60)
	-- print(dt,entity.playbackSpeed,entity.tick , (1/entity.fps))
	entity.ticked = false
	-- trace("frmam",entity.frame)
	-- trace("TICK",entity.tick)
	-- trace(dt*(entity.playbackSpeed or 1))
	-- trace(1/entity.fps)
	-- trace(entity.adata and entity.adata.length or 99999)
	if entity.tick > (1 / entity.fps) then
		-- trace("TICKING")
		entity.tick = entity.tick - 1 / entity.fps
		entity.frame = entity.frame + 1

		entity.ticked = true
		if entity.frame > (entity.adata and entity.adata.length or 99999) then
			entity.frame = 1
			entity.animTime = 0
		end
	end
end

function layerLength(layer)
	if not layer then
		print_info(5)
		error("nil layer")
	end

	local frameCount = 0
	for f = 1, #layer.frames, 1 do
		frameCount = frameCount + layer.frames[f].l
	end
	layer.length = frameCount
	return frameCount
end

function scene_updatePose(layer, pose)
	layer.current_pose, layer.current_matrices = anim9.update_matrices(layer.skeleton, layer.base, pose, layer.map)
end

local lscale, lorientation, lposition = vec3(), quat(), vec3()
function scene_getEntityFrameData(entity, anim, frame_index)
	bench.push("animData")
	local isEntity = entity.isEntity
	local panel = animatePanel
	local symbol = isEntity and entity.symbol or entity
	local anim = anim or entity.anim
	if not symbol.animations then
		load_loadGameImages({ entity.symbol.key })
		table.add(game.symbolsLoaded, entity.symbol.key)
	end
	local adata = symbol.animations[anim]
	local models = isEntity and entity.models or {}
	local editing = panel.instance == entity
	local frame_index = editing and panel.currentFrame or frame_index or entity.frame
	local onion = editing and panel.onion.enabled and not panel.playing
	local update = panel.updateMesh and editing
	local layers = editing and panel.layers or adata.layers
	local separate = editing or type(entity.img) == "table"

	lume.clear(entity.children)

	if
		panel.refreshEntity --editing or
		or input_keyPressed("r")
		or lume.find(game.toLoad, entity.key)
	then
		print("clear models")
		lume.clear(models)
	end

	entity.fps = adata.fps

	local modelIndex = 0
	for l, layer in ipairs(layers) do
		-- print(l,layer.format)
		local peelsSwapped = false

		local onionFirst = l == panel.layer and (onion and panel.onion.before.enabled) and -panel.onion.before.amount
			or 0
		local onionFinal = l == panel.layer and (onion and panel.onion.after.enabled) and panel.onion.after.amount or 0

		for peel = onionFirst, onionFinal, 1 do
			-- print("peel",peel)
			local isOnion = peel ~= 0
			local looped = frame_index

			if layer.clips then
				local fw, fdiff, rep
				for c, clip in ipairs(layer.clips) do
					if frame_index > clip.f2 and frame_index < clip.f2 + clip.f3 then
						fw = fw or (clip.f2 - clip.f1) + 1
						fdiff = (frame_index - clip.f1)
						rep = mf(fdiff / fw)
						if clip.loopMode == "pingpong" and rep % 2 ~= 0 then
							looped = fw - fdiff % fw - 1
						else
							looped = fdiff % fw
						end
						looped = clip.f1 + looped
					end
				end
			end

			local keyF = scene_getKeyFrame(layer, looped)
			local wrapF = wrap2((keyF or 1) + peel, 1, #layer.frames)

			-- if editing and l==panel.layer then trace("lf "..layer.frame,"f "..frame,"kf "..keyF,"wf "..wrapF) end

			local currentFrame = wrapF --editing and wrapF  or keyF
			if peel == 0 then
				layer.currentFrame = currentFrame
			end
			local layerFrame = layer.frames[currentFrame]

			layerFrame.abs = layerFrame.abs or editor_getAbsoluteFrame(editing and panel or adata, l, currentFrame)

			bench.push("framedata")

			if
				editing
				or (#models == 0 or not entity.hasModels)
				or (layer.type == "model" and not layer.mesh or (not layerFrame.verts or #layerFrame.verts == 0))
				or load_isLoading(entity.key)
			then
				layer.type = layer.type or "drawing"

				if layer.type == "model" then
					local name, ext = layer.obj or layer.iqm, layer.obj and ".obj" or ".iqm"

					if name then
						layer.file = layer.file or LINK_DIR .. "models/" .. name .. ext
						layer.texture = layer.texture
							or (
								lfs.exists(LINK_DIR .. "models/" .. name .. ".png")
									and gfx.newImage(LINK_DIR .. "models/" .. name .. ".png")
								or load_imageTex()
							)
						layer.mesh = layer.mesh
							or (
								layer.iqm and iqm.load(layer.file).mesh
								or gfx.newMesh(zformat, obj_loader(layer.file), "triangles", "static")
							)
					else
						-- MODEL
						if layer.data then
							if type(layer.updots) == "nil" then
								layer.updots = true
							end
							local remesh = layer.updots
								or (panel.meshTransform and panel.meshTransform.mode and mouse.moved)
							bench.push("modeldata")
							if type(layer.data) == "string" then
								layer.data = symbol.models[layer.data]
							end
							local edit = layer.data.verts
								and panel
								and panel.l
								and panel.l.remodel
								and (panel.instance == entity)

							if (edit and remesh) or not layer.data.verts then
								bench.push("meshrefs")
								setMeshReferences(layer.data)
								bench.pop("meshrefs")
								bench.push("meshsets")
								updateMeshData(layer.data, edit, panel)
								bench.pop("meshsets")
							end

							if type(layer.data.verts[1]) == "table" then
								bench.push("meshmakes")
								layer.isMesh = true
								if not layer.mesh or (edit and #layer.data.verts ~= layer.mesh:getVertexCount()) then
									layer.mesh = gfx.newMesh(
										layer.skeleton and animatedVertexFormat or zformat,
										layer.data.verts,
										"triangles"
									)
								elseif edit then
									if remesh then
										layer.mesh:setVertices(layer.data.verts, 1)
									end
								end
								layer.texture = type(layer.texture) == "string"
										and gfx.newImage(
											LINK_DIR
												.. "symbols/"
												.. panel.symbol.name
												.. "/.textures/"
												.. layer.texture
												.. ".png"
										)
									or layer.texture
								if not gfx.getTexture(layer.mesh) then
									gfx.setTexture(layer.mesh, layer.texture)
								end
								bench.pop("meshmakes")
							end
							-- layerFrame.mesh = layer.mesh
							-- layerFrame.tris = layer.data.tris
							layer.dither = edit and panel.seeThru

							if edit then
								bench.push("dotsNlines")
								layer.unlit = panel.unlit
								if panel.dotVerts then
									if panel.selectMode[1] then
										layer.dots = layer.dots or gfx.newMesh(zformat, panel.dotLineVerts, "points")
										if layer.updots and panel.dotVertCount > 0 then
											layer.dots:setVertices(panel.dotVerts, 1, panel.dotVertCount)
										end
									else
										layer.dots = nil
									end
								end
								if panel.lineVerts then
									layer.lines = layer.lines or gfx.newMesh(zformat, panel.dotLineVerts, "triangles")
									if layer.updots and panel.lineVertCount > 0 then
										layer.lines:setVertices(panel.lineVerts, 1, panel.lineVertCount)
									end
								end
								layer.updots = false
								bench.pop("dotsNlines")
							else
								layer.dots = nil
								layer.lines = nil
							end
							bench.pop("modeldata")
						end
					end
				end

				-- for f,frame in ipairs(editing and {layerFrame} or layer.frames) do

				local frame = layerFrame
				if layer.type == "model" then
				elseif layer.type == "drawing" or not layer.type then
					if entity.name == "thing" then
						log("preshMade")
					end

					if frame.blank and layer.external then
						trace("blank?")
					else
						-- TODO just have one universal frame mesh, resize/texture in shader, this is stupid
						if layer.format == "raster" then
							local layerRes = layer.resolution or 1
							frame.verts = frame.verts or {}
							for i = 1, 6, 1 do
								frame.verts[i] = frame.verts[i] or {}
							end
							setValues(
								frame.verts[1],
								-frame.w * 0.5 * layerRes + frame.ox,
								-frame.h * 0.5 * layerRes + frame.oy,
								1,
								0,
								0,
								0,
								1,
								1,
								1,
								1
							)
							setValues(
								frame.verts[2],
								frame.w * 0.5 * layerRes + frame.ox,
								-frame.h * 0.5 * layerRes + frame.oy,
								1,
								0,
								1,
								0,
								1,
								1,
								1,
								1
							)
							setValues(
								frame.verts[3],
								frame.w * 0.5 * layerRes + frame.ox,
								frame.h * 0.5 * layerRes + frame.oy,
								1,
								0,
								1,
								1,
								1,
								1,
								1,
								1
							)
							setValues(
								frame.verts[4],
								-frame.w * 0.5 * layerRes + frame.ox,
								frame.h * 0.5 * layerRes + frame.oy,
								1,
								0,
								0,
								1,
								1,
								1,
								1,
								1
							)
							setValues(
								frame.verts[5],
								-frame.w * 0.5 * layerRes + frame.ox,
								-frame.h * 0.5 * layerRes + frame.oy,
								1,
								0,
								0,
								0,
								1,
								1,
								1,
								1
							)
							setValues(
								frame.verts[6],
								frame.w * 0.5 * layerRes + frame.ox,
								frame.h * 0.5 * layerRes + frame.oy,
								1,
								0,
								1,
								1,
								1,
								1,
								1,
								1
							)

							local draw = panel.drawing

							if panel.changed then
								draw = false
								--editor_releaseSelection(panel)
							end

							if frame.mesh then
								frame.mesh:setVertices(frame.verts, 1)
							else
								frame.mesh = gfx.newMesh(zformat, frame.verts, "triangles")
							end

							local tex = editing and frame.canvas or symbol.image[frame.id]

							if tex and gfx.isTexture(tex) then
								if ctx.isAnim then
									tex:setFilter(panel.antiAlias and "linear" or "nearest", "nearest")
								end
								gfx.setTexture(frame.mesh, tex)
							end

							frame.tris = {
								{
									vec3(frame.verts[1][1], frame.verts[1][2], frame.verts[1][3]),
									vec3(frame.verts[4][1], frame.verts[4][2], frame.verts[4][3]),
									vec3(frame.verts[2][1], frame.verts[2][2], frame.verts[2][3]),
								},
								{
									vec3(frame.verts[4][1], frame.verts[4][2], frame.verts[4][3]),
									vec3(frame.verts[3][1], frame.verts[3][2], frame.verts[3][3]),
									vec3(frame.verts[2][1], frame.verts[2][2], frame.verts[2][3]),
								},
							}
						end
					end
				end
			end

			bench.pop("framedata")

			local palette = (layer.format == "r8" and layer.palette) and vecPalette(layer.palette) or {}

			layerFrame = layer.frames[currentFrame]

			layerFrame.index = currentFrame
			modelIndex = modelIndex + 1
			if not models[modelIndex] then
				-- print("new model",#models,time)
			end
			models[modelIndex] = models[modelIndex] or { nocopy = true, frame = currentFrame }
			local model = models[modelIndex]
			local prevFrame = model.frame
			local keyChanged = model.frame ~= currentFrame
			layer.modelIndex = modelIndex
			model.editLayer = (panel.l == layer and panel.clickedCanvas)
			model.index = modelIndex
			model.sort = modelIndex
			model.frame = currentFrame
			model.color = { 1, 1, 1, 1 }
			model.onion = isOnion
			model.isFrame = layer.type == "drawing"
			--model.front     = layer.type=="model"

			if peel < 0 then
				model.tint = lume.clone(panel.onion.before.tint)
				model.tint.a = model.tint.a * (panel.onion.before.enabled and panel.onion.amount or 0)
			end
			if peel > 0 then
				model.tint = lume.clone(panel.onion.after.tint)
				model.tint.a = model.tint.a * (panel.onion.after.enabled and panel.onion.amount or 0)
			end
			model.tris = layer.data and layer.data.tris or layerFrame.tris
			model.mesh = layer.mesh or layerFrame.mesh

			model.wireframe = layer.wireframe
			model.unlit = layer.unlit
			model.dots = layer.dots
			model.lines = layer.lines
			model.dotlines = (panel.instance == entity) and not (panel.meshTransform and panel.meshTransform.mode)
			model.isCustom = layer.data
			model.dither = layer.dither
			model.vector = layer.format == "vector" and not layer.data
			model.indexed = layer.format == "r8"
			model.alpha = layer.alpha
			model.palette = palette

			bench.push("IK")
			if not editing then
				if layer.skeleton and layerFrame.transform and entity.transform and editor.debug.live_IK then
					layer.mat = -layerFrame.transform * -entity.transform
					scene_inverseKinematics(layer, layerFrame.pose)
					-- layer.poseMesh = nil
				end
			end
			bench.pop("IK")

			if layer.current_pose then
				model.skelly = not layer.remodel
				model.current_pose = layer.current_pose
				model.poseMesh = layer.poseMesh
			end

			if isOnion then
				if peel < 0 then
					model.visible = panel.onion.before.enabled
				end
				if peel > 0 then
					model.visible = panel.onion.after.enabled
				end
			end

			bench.push("Vecterz")

			if model.vector then
				if #layerFrame.strokes == 0 and #layerFrame.symbols == 0 then
					layerFrame.blank = true
				end
				bench.push("rendervecframe")

				model.textureStroke = layer.textureEnabled
				local drawing = (peel == 0 and panel.drawTime > 0 and panel.f == layerFrame)
				-- print(peel==0 , panel.drawTime>0 ,panel.f == layerFrame)

				if
					not model.groupCounts
					or (editing and (keyChanged or drawing or panel.sudoForceRestroke or panel.forceRestroke))
				then
					-- print("updatevec",entity.key,peel)
					if not mouse.down then
						_start_timer("render vector frame")
					end
					if not panel.debugPrintVector then
						_disable_timers()
					end
					model.groupCounts = vector_genVectorFrameMesh(panel, layer, layerFrame, peel, drawing)
						or model.groupCounts
					_enable_timers()
					if not mouse.down then
						_end_timer("render vector frame")
					end
					peelsSwapped = true
				end

				-- if frameChange==0 and not panel.sudoForceRestroke then -- if current frame is the same, dont update peels

				--   if peel~=0 then return  end

				-- else
				--   restroke=true
				-- end

				-- if math.abs(frameChange)==1 then -- swap onion/current if moved one to update only one of the 3
				--     if not panel.onion.enabled then
				--       restroke=true
				--     else
				--       -- print("swap",onion,frameChange)
				--       restroke=true
				--       if frameChange==1 then
				--         layer.strokeMeshes[1],layer.strokeMeshes[3],layer.strokeMeshes[2] = layer.strokeMeshes[3],layer.strokeMeshes[2],layer.strokeMeshes[1]
				--         if onion~=2 then
				--           return
				--         end
				--       else
				--         layer.strokeMeshes[3],layer.strokeMeshes[2],layer.strokeMeshes[1] = layer.strokeMeshes[1],layer.strokeMeshes[3],layer.strokeMeshes[2]
				--         if onion~=1 then
				--           return
				--         end
				--       end
				--       print("only refresh frame",frame.index,time)
				--     end
				--   end

				bench.pop("rendervecframe")
				model.strokeMesh = layer.strokeMeshes and layer.strokeMeshes[peel]
				model.strokeWire = layer.onePixel
				if panel.strokeSelected.dragging then
					model.strokeSelected = layer.strokeSelected
					model.strokeSelectedLayer = layer.strokeSelected
						and lume.find(game.vectorLayers, panel.strokeSelected.stroke.group)
				else
					model.strokeSelected = nil
				end
				model.strokePreview = (drawing and isOnion == false) and panel.strokePreview or nil
				-- print("hi",model.strokePreview)
				model.strokePreviewLayer = panel.previewStroke
					and lume.find(game.vectorLayers, panel.previewStroke.group)
				-- print((isOnion==false),panel.strokePreview,time)
			end
			bench.pop("Vecterz")

			if
				editing
				and layer.format == "raster"
				and panel.brushCanvas
				and panel.drawing
				and panel.layer == l
				and panel.tool == "brush"
			then
				panel.brushVerts = panel.brushVerts or deepcopy(layerFrame.verts)
				panel.brushCanvasMesh = panel.brushCanvasMesh or gfx.newMesh(zformat, layerFrame.verts, "triangles")
				model.brushColor = { editor_brushGamma(panel.color) }
				model.brushColor[4] = panel.color.a
				for i = 1, 6, 1 do
					local v = panel.brushVerts[i]
					v[1], v[2] = layerFrame.verts[i][1], layerFrame.verts[i][2]
					v[3], v[4], v[5], v[6] =
						layerFrame.verts[i][3], layerFrame.verts[i][4], layerFrame.verts[i][5], layerFrame.verts[i][6]
					v[7], v[8], v[9], v[10] =
						model.brushColor[1], model.brushColor[2], model.brushColor[3], model.brushColor[4]
				end
				panel.brushCanvasMesh:setVertices(panel.brushVerts, 1)
				model.brushMesh = panel.brushCanvasMesh
				model.brushCanvas = panel.brushCanvas
				if panel.brushCanvas then
					local frameTex = model.mesh:getTexture()
					if frameTex then
						model.brushCanvas:setFilter(frameTex:getFilter())
					end
					model.brushMesh:setTexture(model.brushCanvas)
				end
			else
				model.brushCanvas = nil
			end

			model.parent = entity
			model.layer = l

			if editing and panel.solo > 0 then
				model.visible = panel.solo == l
			else
				model.visible = layer.visible
			end

			layer.length = layer.length or layerLength(layer)
			if frame_index > layer.length and not layer.loop then
				model.visible = false
			end

			if #layerFrame.symbols > 0 then
				layer.epool = layer.epool or {}
				for s, symbol in ipairs(layerFrame.symbols) do
					layer.epool[symbol.uid] = layer.epool[symbol.uid]
						or scene_newEntity({
							key = symbol.key,
							uid = symbol.uid,
							dontAdd = true,
						})
					symbol.entity = layer.epool[symbol.uid]
					if editing then
					end

					if isEntity then
						symbol.entity.parent = entity
					end

					local scale, orientation, position
					local cf = editing and panel.currentFrame or frame_index
					local play = not ctx.editing
						or (editing and (panel.playing or panel.layer ~= l))
						or entity.previewing
					local nextFrame = layer.frames[currentFrame + 1] or layer.frames[1]
					local sym1, sym2 =
						symbol, lume.match(nextFrame.symbols, function(v)
							return v.uid == symbol.uid
						end)
					local progress, progress2

					if
						not isOnion
						and #layer.frames > 1
						and layerFrame.tween
						and layerFrame.tween.enabled
						and play
						and sym2
					then
						local time = panel.open and panel.time or entity.animTime
						local fps = panel.open and panel.animation.fps or adata.fps

						local t1 = (layerFrame.abs - 1) / fps
						local t2 = (layerFrame.abs + layerFrame.l - 1) / fps
						progress = ((cf - mf((cf - 1) / layer.length) * layer.length) - layerFrame.abs) / layerFrame.l
						progress2 = mc(0, (time - t1) / (t2 - t1), 1)
						progress = mc(
							0,
							easeInOut(
								(not entity.previewing and layer.smooth) and progress2 or progress,
								layerFrame.tween.easeIn,
								layerFrame.tween.easeOut
							),
							1
						)

						if entity.ticked and cf == layerFrame.abs then
							progress = 0
						end

						-- checkTransform(layerFrame)
						-- checkTransform(nextFrame)
						position = vec3.lerp(sym1.position, sym2.position, progress)
						if sym1.orientation == sym2.orientation then
							orientation = sym1.orientation
						else
							orientation = quat.slerp(sym1.orientation, sym2.orientation, progress)
						end
						print(sym1.orientation, sym2.orientation, orientation)
						scale = vec3.lerp(sym1.scale, sym2.scale, progress)
					else
						scale, orientation, position = symbol.scale, symbol.orientation, symbol.position
					end

					symbol.entity.transform:identity()
					symbol.entity.transform:scale(symbol.entity.transform, scale)
					symbol.entity.transform:mul(mat4.from_quaternion(orientation), symbol.entity.transform)
					symbol.entity.transform:translate(symbol.entity.transform, position)
					-- symbol.entity:updateTransform()

					scene_updateEntityAnimData(symbol.entity)
					scene_getEntityFrameData(symbol.entity)
					if isEntity then
						table.insert(entity.children, symbol.entity)
					end
				end
			end

			-- IQM ANIMS
			if layer.iqm then
				local iqmString = LINK_DIR .. "models/" .. layer.iqm
				layer.iqmAnims = layer.iqmAnims
					or iqm.loadAnims(lfs.exists(iqmString .. ".exm") and iqmString .. ".exm" or iqmString .. ".iqm")
				layer.iqmAnims.nocopy = true
				layer.iqmAnims = layer.iqmAnims or layer.iqmAnims
				bench.push("iqm")
				--scene_updateEntityIQM(entity,  model,   layer,   layerFrame,  modelIndex,  isOnion,entity.previewing or editing or not ctx.editing,not ctx.editing or panel.playing or entity.previewing,onion and layerFrame.abs or frame,currentFrame,nextFrame,layer.smooth and progress or progress2,model.transform)
				bench.pop("iqm")
			end

			model.drawBounds = editing
				and layer.type == "drawing"
				and panel.layer == l
				and (panel.tool == "move" and not panel.selection.mesh)
				and not (input_keyDown("left") or input_keyDown("right"))
		end
	end

	clearTail(models, modelIndex + 1)
	entity.hasModels = modelIndex > 0

	if update then
		panel.updateMesh = false
	end

	if editing or not panel.open or not ctx.editing then
		panel.forceRestroke = false
		panel.sudoForceRestroke = false
	end
	panel.refreshEntity = false
	bench.pop("animData")
	return models
end

function scene_inverseKinematics(layer, pose, panel)
	layer.repose = layer.skeleton

	if pose and (not panel or (layer.repose and not layer.remodel)) then
		if not layer.base then
			local bound = anim9.rebind({}, { skeleton = layer.skeleton })
			layer.base = bound.inverse_base
			layer.map = bound.index_map
		end

		if ctx.editing and not panel then
			scene_updatePose(layer, pose)
			return
		end

		if panel then
			editor.currentTool = "Select"
			layer.mat = -panel.f.transform * -panel.instance.transform
			if panel.changed or panel.playing then
				lume.clear(editor.selectedEntities)
				editor_clearBones(panel)
			end
		end

		if not layer.bones then
			layer.boneLinks = {}
			scene_updatePose(layer, pose)

			layer.bones = {}
			for b, bone in ipairs(layer.skeleton) do
				local mat = layer.current_matrices[bone.name]
				local pos = -(mat * layer.mat * vec3(0))
				layer.bones[b] = panel
						and scene_newEntity(ctx.viewport.room, { x = pos.x, y = pos.y, z = pos.z, simple = true })
					or {
						index = b,
						position = pos,
						getPosition = function(bone)
							return bone.position
						end,
						setPosition = function(bone, position)
							bone.position:set(position)
						end,
					}
				layer.bones[b].bone = b
				layer.bones[b].name = bone.name
				layer.bones[b].visible = panel
				if bone.parent > 0 then
					layer.bones[b].parentID = bone.parent
					table.insert(layer.boneLinks, { b, bone.parent })
				else
					layer.bones[b].visible = false
				end
			end
		else
			if not ctx.editing or not panel then
				for b, bone in ipairs(layer.bones) do
					local mat = layer.current_matrices[bone.name]
					local pos = -(mat * layer.mat * vec3(0))
					-- layer.bones[b]:setPosition(pos)
					pos = camera_project(-pos)
          editor_pushDraw(function()
            gfx.push("all")
            gfx.setBlendMode ('alpha', 'premultiplied')
            gfx.setColor(1,1,1,1)
            gfx.print(tostring(b)..", "..bone.name,pos.x+8,pos.y+4)
            gfx.pop()
          end)
				end
			end
		end

		layer.pose = layer.pose or {}
		layer.nextPose = {}

		-----------------------------------------
		local function getRot(bone, diff)
			local rotate = (diff and layer.nextPose[bone.bone])
					and (layer.nextPose[bone.bone] * pose[bone.bone].rotate):normalize()
				or pose[bone.bone].rotate
			if bone.parentID then
				return getRot(layer.bones[bone.parentID], diff) * rotate
			else
				return rotate
			end
		end

		if panel and editor.selectedEntities[1] then
			local selected = editor.selectedEntities[1]

			for b, bone in ipairs(layer.bones) do
				if bone.parentID == selected.parentID then
					table.add(editor.selectedEntities, bone)
				end
			end
		end
		------------------------------------------
		layer.ik = true
		if layer.ik then
			local first, last
			local seqs = {
				{
					1,
					8,
					11,
					{
						{ 11, 16, 19, 21, 23 },
						{ 11, 15, 18, 20, 22 },
						{ 11, 14, 17 },
					},
				},
				{
					2,
					9,
					12,
				},
				{
					4,
					10,
					13,
				},
			}

			local function buildChains(seq, parent)
				local joints, bones, indexes = {}, {}, {}
				for j, joint in ipairs(seq) do
					if type(joint) == "number" then
						table.insert(joints, layer.bones[joint]:getPosition())
						table.insert(bones, layer.bones[joint])
						table.insert(indexes, joint)
					else
						for b, branch in ipairs(joint) do
							buildChains(branch, joint)
						end
					end
				end
				local new = ik.new(joints, bones, indexes)
				new.parent = parent
				table.insert(layer.chains, new)
			end

			if not layer.chains then
				layer.chains = {}
				for s, seq in ipairs(seqs) do
					buildChains(seq)
				end
			end

			if layer.chains then
				if not ctx.editing then
					for c, chain in ipairs(layer.chains) do
						for j = 1, chain.n, 1 do
							chain.joints[j]:set(chain.bones[j]:getPosition())
						end
					end

					for c, chain in ipairs(layer.chains) do
						for b, bone in ipairs(chain.bones) do
							if layer.bones[bone.parentID] and b > 1 then
								local target = (chain.joints[b] - chain.joints[b - 1]):normalize()
								bone.last = target:clone()
							end
						end
					end

					for c, chain in ipairs(layer.chains) do
						if chain.bones[chain.n].index == 12 then
							chain.bones[chain.n].position.y = chain.bones[chain.n].position.y + 0.01
							chain.bones[chain.n].position.z = chain.bones[chain.n].position.z + 0.01
							chain.bones[chain.n]:setPosition(vec3())
						end

						for j = 1, chain.n, 1 do
							chain.joints[j]:set(chain.bones[j]:getPosition())
						end
					end
				end

				for c, chain in ipairs(layer.chains) do
					local selectedBone, sb = lume.match(chain.bones, function(item)
						return item.selected
					end)

					if not ctx.editing then
						sb = chain.n
					end

					if sb then
						local start = chain.parent and 2 or 1
						sb = sb or chain.n
						first, last = start, sb
						chain.origin = chain.bones[first]:getPosition():clone()
						chain.target = chain.bones[last]:getPosition():clone()
						chain:solve(first, last)

						if sb ~= chain.n then
							first, last = sb, chain.n
							chain.origin = chain.bones[first]:getPosition():clone()
							chain.target = chain.bones[last]:getPosition():clone()
							chain:forward(first, last)
						end

						for j = start, #chain.bones, 1 do
							if not (editor.gizmo.dragging and j == sb) then
								chain.bones[j]:setPosition(chain.joints[j])
							end
						end

						for c2, chain2 in ipairs(layer.chains) do
							-- if chain2.parent==chain.indexes[chain.n] then
							--  first,last = 1,chain2.n
							--  chain2.origin = chain.joints[2]:clone()
							--  chain2:forward(first,last)
							--  for j2=1,chain2.n,1 do
							--    chain2.bones[j2].nextPosition.x,chain2.bones[j2].nextPosition.y,chain2.bones[j2].nextPosition.z = chain2.joints[j2]:unpack()
							--  end
							-- end
							-- if chain2.indexes[1]==chain.indexes[1] then
							--  first,last = 2,chain2.n
							--  chain2.origin = chain.joints[2]:clone()
							--  chain2:forward(first,last)
							--  for j2=1,chain2.n,1 do
							--    chain2.bones[j2].nextPosition.x,chain2.bones[j2].nextPosition.y,chain2.bones[j2].nextPosition.z = chain2.joints[j2]:unpack()
							--  end
							-- end
						end
					end
				end

				for c, chain in ipairs(layer.chains) do
					for b, bone in ipairs(chain.bones) do
						layer.pose[chain.indexes[b]] = -chain.joints[b]
					end
				end
			end
		end

		----------------------------------------
		if layer.chains then
			for c, chain in ipairs(layer.chains) do
				for b, bone in ipairs(chain.bones) do
					local vec = chain.joints[b]
					local pos = camera_project(-vec)
					if panel then
						gfx.push("all")
						gfx.setBlendMode("alpha", "premultiplied")
						gfx.setColor(1, 1, 1, 0.3 - math.dist(mouse.x, mouse.y, pos.x, pos.y) / 200)
						gfx.print(tostring(b) .. ", " .. bone.name, pos.x + 8, pos.y + 4)
						gfx.pop()
						if bone.render then
							bone.render.ox, bone.render.oy = pos.x, pos.y
						end
					end

					local parent = layer.bones[bone.parentID]
					if parent and b > 1 then
						local parent2 = parent.parentID and layer.bones[parent.parentID]
						local target = (vec - chain.joints[b - 1]):normalize()

						if bone.last then
							local prot1 = (parent2 and getRot(parent2, false) or quat()):inverse()
							local prot2 = (parent2 and getRot(parent2, true) or quat()):inverse()
							local diff = quatBetweenVecs(prot1 * bone.last, prot2 * target)
							local rotation = diff

							local change = math.abs(diff.x) + math.abs(diff.y) + math.abs(diff.z) + math.abs(diff.w)

							-- trace(bone.parentID,change)

							layer.nextPose[bone.parentID] = rotation:normalize()
						end
						if ctx.editing and panel then
							bone.last = target:clone()
						end
					end
				end
			end

			for b, bone in pairs(layer.nextPose) do
				if bone then
					pose[b].rotate = bone * pose[b].rotate
				end
			end
		end

		scene_updatePose(layer, pose)

		------------------------------------------
		if ctx.editing and not panel then
			layer.poseMesh = nil
		elseif layer.boneLinks[1] then
			local verts = {}
			for i, link in ipairs(layer.boneLinks) do
				local c1 = layer.bones[link[1]].selected and { 1, 0.5, 0.3 } or { 1, 1, 1 }
				local c2 = layer.bones[link[2]].selected and { 1, 0.5, 0.3 } or { 1, 1, 1 }
				-- local x1,y1,z1 = layer.pose[link[1]]:getPosition():unpack()
				-- local x2,y2,z2 = layer.pose[link[2]]:getPosition():unpack()
				table.insert(
					verts,
					{ layer.pose[link[1]].x, layer.pose[link[1]].y, layer.pose[link[1]].z, 1, unpack(c1) }
				)
				table.insert(
					verts,
					{ layer.pose[link[2]].x, layer.pose[link[2]].y, layer.pose[link[2]].z, 1, unpack(c2) }
				)
				table.insert(
					verts,
					{ layer.pose[link[1]].x, layer.pose[link[1]].y, layer.pose[link[1]].z, 1, unpack(c1) }
				)
			end
			if not layer.poseMesh then
				layer.poseMesh = gfx.newMesh(zformat3, #verts * 2, "triangles")
			end

			layer.poseMesh:setVertices(verts, 1)

			local v = #verts + 1
			if layer.chain and layer.chain.joints then
				local joints = layer.chain.joints
				for j = 0, #joints - 2, 1 do
					layer.poseMesh:setVertices({
						{ -joints[j + 1].x, -joints[j + 1].y, -joints[j + 1].z, 1, 0.5, 1, 1 },
						{ -joints[j + 2].x, -joints[j + 2].y, -joints[j + 2].z, 1, 0.5, 1, 1 },
						{ -joints[j + 1].x + 0.1, -joints[j + 1].y + 0.1, -joints[j + 1].z + 0.1, 1, 0.5, 1, 1 },
					}, v)
					v = v + 3
				end
			end
		end
	else
		layer.poseMesh = nil
		layer.bones = nil
	end
end

function checkTransform(t)
	t.position = vec3.is_vec3(t.position) and t.position or vec3(t.position)
	t.orientation = quat.is_quat(t.orientation) and t.orientation or quat(t.orientation)
	t.scale = vec3.is_vec3(t.scale) and t.scale or vec3(t.scale)
end

function scene_updateEntityIQM(
	entity,
	target,
	layer,
	layerFrame,
	l,
	onion,
	update,
	play,
	f,
	cf,
	nextFrame,
	progress,
	transform
)
	local update = update or not target.anim
	local trackChanged = false
	target.anim = target.anim or anim9(layer.iqmAnims)
	--trace("#anims",lume.count(target.anim.timeline))

	if not play then
		if target.track then
			target.track.blend = 1
		end
		if target.track2 then
			target.track2.blend = 0
			if nextFrame.anim == layerFrame.anim then
				target.track2 = nil
			end
		end
	end

	if target.anim and layerFrame.anim and layerFrame.anim ~= "" then
		if not target.track or layerFrame.anim ~= target.nextAnim then
			trackChanged = true
			--trace("NEWTRACK")
			target.nextAnim = layerFrame.anim
			target.nextAnim2 = layerFrame.anim
			target.anim:stop()
			target.track2 = nil
			target.track = target.anim:new_track(target.nextAnim, 1, 1, function(anim)
				anim.time = 0
			end)
			target.anim:play(target.track)
		end
	end

	if
		not onion
		and layerFrame.tween
		and layerFrame.tween.blend == true
		and (nextFrame.anim ~= layerFrame.anim) == true
		and (target.nextAnim2 ~= nextFrame.anim) == true
	then
		trackChanged = true
		target.nextAnim2 = nextFrame.anim
		--log("new blend track")

		target.track2 = target.anim:new_track(target.nextAnim2, 1, 1, function(anim)
			anim.time = 0
		end)
		target.anim:play(target.track2)
	end

	local length = target.anim:length(target.nextAnim)

	bench.push("5")
	if target.anim and update then
		local start = scene_getStartFrame(layerFrame, layer, cf) or layerFrame.abs
		target.track.time = utils.wrap(((f - start) / (length * 18)) * length, length * 0.99)
		--trace("1time",target.track.time)

		if target.track2 and progress and play then
			local length2 = target.anim:length(target.nextAnim2)
			target.track2.time = utils.wrap((target.track.time / length) * length2, length2 * 0.99) --utils.wrap(((f-nextFrame.abs)/(length2*18))*length,length2*0.99)
			target.track2.blend = mc(0, progress * 2, 1)
			target.track.blend = 1 - progress
			target.nextStartTime = target.track2.time
			--trace("2time",target.track2.name,target.track2.time)
		end

		if entity.ticked or trackChanged then
			bench.push("A")
			target.anim:update(0)
			bench.pop("A")
		end

		bench.push("B")
		entity.models[l].skelly = true
		--entity.models[l].skelly = target.anim.current_pose
		bench.pop("B")
		--entity.models[l].skelly.nocopy = true
		target.anim.nocopy = true
		target.track.nocopy = true
		target.nocopy = true

		if target.track.time >= target.anim:length(target.nextAnim) then
			--target.anim:play(target.track)
			--target.track.time = 0
			--target.anim:update(0)
		end

		if not ctx.editing then
			if target.anim.current_matrices and entity.transform and transform then
				-- gfx.push("all")
				-- gfx.reset()
				local alpha = editor.debug.feet and 1 or 0
				local screenSpace, screenSpace2, screenSpace3
				local worldSpace
				local name
				local range = 2.3
				local side
				for b, bone in pairs(target.anim.current_matrices) do
					-- gfx.setColor(1, 1, 1, 1 * alpha)
					name = target.anim.skeleton[b].name
					side = name:sub(6, 6)
					if name:sub(1, 4) == "Foot" then
						worldSpace = bone * -transform * -entity.transform * blankVec
						screenSpace = camera_project(worldSpace) / game.resolution
						-- gfx.print(name, screenSpace.x + 4, screenSpace.y)
						entity.footTurn = entity.footTurn or "R"

						if worldSpace.y > entity.floor - range and worldSpace.y < entity.floor + range * 2 then
							if not entity[name] then
								entity.stepPoint = worldSpace:clone()
								entity.footTurn = side
								entity.footEase = 0
								--target.anim.overrides = {[b]={worldSpace=worldSpace,translate=bone*vec3(0)}}
							end
							entity[name] = true
							-- gfx.setColor(1, 0.2, 0.3, 1 * alpha)
							-- gfx.circle("fill", screenSpace.x, screenSpace.y, 7, 20)
							-- gfx.setColor(1, 1, 1, 1 * alpha)
						else
							entity[name] = false
						end

						if side == entity.footTurn then
							entity.footPoint = worldSpace:clone()
							if target.anim.overrides and target.anim.overrides[b] then
								screenSpace2 = camera_project(target.anim.overrides[b].worldSpace) / game.resolution
								-- gfx.setColor(0.2, 1, 0.2, 1 * alpha)
								-- gfx.circle("fill", screenSpace2.x, screenSpace2.y, 5, 20)
								--target.anim.overrides[b].translate = -layerFrame.orientation * -entity.orientation * (entity.position+target.anim.overrides[b].worldSpace) * entity.scale
							end
							-- gfx.setColor(0.3, 0.2, 1, 1 * alpha)
							-- gfx.circle("fill", screenSpace.x, screenSpace.y, 5, 20)
						end
						-- gfx.circle("fill", screenSpace.x, screenSpace.y, 5, 20)
					end
				end
				if entity.stepPoint then
					-- gfx.setColor(1, 0, 1, 1 * alpha)
					screenSpace3 = camera_project(entity.stepPoint) / game.resolution
					-- gfx.circle("fill", screenSpace3.x, screenSpace3.y, 8, 20)
					screenSpace3 = camera_project(entity.footPoint) / game.resolution
					-- gfx.circle("line", screenSpace3.x, screenSpace3.y, 10, 20)

					entity.footDiff = entity.stepPoint - entity.footPoint
					trace("entity.stepPoint-entity.footPoint", entity.stepPoint - entity.footPoint)

					--entity.footEase = 0.2

					--entity:move(-entity.footDiff.x*entity.footEase,0,-entity.footDiff.z*entity.footEase)
				end
				-- gfx.pop()
			end
		end
	end
	bench.pop("5")
end

function scene_getStartFrame(frame, layer, cf)
	local start = frame.abs
	for i = cf, 1, -1 do
		if layer.frames[i] and layer.frames[i].anim and layer.frames[i].anim == frame.anim then
			start = layer.frames[i].abs
		else
			break
		end
	end
	return start
end

function scene_getKeyFrame(layer, currentFrame)
	local frameCount = 1
	local frameMark = 1
	for f, frame in ipairs(layer.frames) do
		if (frameCount + frame.l >= currentFrame and frameCount <= currentFrame) or frameCount == currentFrame then
			frameMark = f
		end
		frameCount = frameCount + frame.l
	end
	return frameMark
end

function scene_updateEntityLasso(entity)
	if
		editor.state == "Normal"
		and editor.viewport
		and ctx.viewport.key == editor.viewport.key
		and entity.visible
		and (
			editor.selectAll
			or (
				game.levelClicked
				and editor.currentTool == "Select"
				and entity.render.x
				and simplePointInRect(
					{ x = entity.render.x / game.resolution, y = entity.render.y / game.resolution },
					editor.selectBounds
				)
			)
		)
	then
		editor_deleteFromSelectedEntities(entity.uid)
		if
			not scene_entitySelected(entity)
			and (not pathsPanel.open or entity.simple)
			and (not animatePanel.open or entity.bone)
		then
			table.insert(editor.selectedEntities, entity)
		end
	end
end

function scene_updateEntityVars(entity)
	entity.i = entity.index
	entity.time = entity.time + 1
	entity.layer = entity.surface and -0.15 or 0

	if #editor.selectedEntities ~= editor.numSelected then
		entity.selected = scene_entitySelected(entity)
	elseif #editor.selectedEntities == 0 then
		entity.selected = false
	end

	if entity.selected then
		entity:move(0, 0, 0)
	end

	entity.hovered = (editor.hoveredEntity == entity)

	scene_updateEntityCollision(entity)

	-- entity.pos =  entity.position
end

function scene_updateEntityCollision(entity)
	if ctx.editing then
		return
	else
		entity:move()
		entity:damp()
	end

	entity.floor = entity.floor or 0
	entity.plane = nil
	entity.head = nil

	if entity.gravity then
		scene_updateEntityPlane(entity)
	end

	scene_updateEntityPlaneVars(entity)

	local slideVec = vec2(0, 0)
	local slidVec = vec2(0, 0)
	local edgeVec = vec2(0, 0)
	local dirVec = vec2(-entity.v.x, -entity.v.z)
	local slide = {}
	local edgeDot
	local edgeIn
	local radius = 20
	local dist, dist2, dist3
	local collided = 999
	local newPos = vec2(0)
	local corner

	entity.cantOffset = false
	entity.edgeResults = entity.edgeResults and lume.clear(entity.edgeResults) or {}

	if not entity.ghost and entity.gravity then
		local nearfloor = math.abs(-entity.floor - entity.y) < 10

		if entity.plane and entity.plane.edges then
			scene_debugPlanes(
				entity,
				6,
				{ entity.planePos1.x, entity.planePos1.y, entity.planePos2.x, entity.planePos2.y }
			)

			for e, edge in ipairs(entity.plane.edges) do
				scene_debugPlanes(entity, 5, edge)
				dist = distPointToLineSegment(
					{ entity.planePos1.x, entity.planePos1.y },
					{ edge.p1.x, edge.p1.y },
					{ edge.p2.x, edge.p2.y }
				)
				dist3 = entity.planePos3
						and distPointToLineSegment(
							{ entity.planePos3.x, entity.planePos3.y },
							{ edge.p1.x, edge.p1.y },
							{ edge.p2.x, edge.p2.y }
						)
					or 999
				if dist3 < radius then
					entity.cantOffset = true
				end
				if dist < radius then
					local projected =
						{ entity.planePos1.x + edge.norm.x * -dist, entity.planePos1.y + edge.norm.y * -dist }
					dist2 = distPointToLineSegment(projected, { edge.p1.x, edge.p1.y }, { edge.p2.x, edge.p2.y })

					if dist2 > 1 then
						local side = math.dist(edge.p1.x, edge.p1.y, projected[1], projected[2])
									< math.dist(edge.p2.x, edge.p2.y, projected[1], projected[2])
								and edge.p1
							or edge.p2
						local dv2 = (side - entity.planePos1):normalize() * radius
						local newCoords = side - dv2
						scene_debugPlanes(entity, 6, { side.x, side.y, entity.planePos1.x, entity.planePos1.y })
						if dist < collided then
							entity:setPosition(-newCoords.x, nil, -newCoords.y)
							collided = dist
						end
					else
						scene_debugPlanes(
							entity,
							6,
							{ entity.planePos1.x, entity.planePos1.y, projected[1], projected[2] }
						)

						coords = { entity.planePos1.x + edge.norm.x * -dist, entity.planePos1.y + edge.norm.y * -dist }
						edgeVec = vec2(edge[3] - edge[1], edge[4] - edge[2]):normalize()
						coords = vec2(coords)
						local newCoords = coords + vec2(edge.norm.x * radius, edge.norm.y * radius)
						coords = coords + edgeVec:perpendicular()
						slideMul = (radius - dist) / radius
						dot = vec2.dot(dirVec, edgeVec)
						slideVec = (edgeVec * dot):normalize()
						slideVec = slideVec * slideMul
						slidVec = coords + slideVec * 100
						slide = { coords.x, coords.y, slidVec.x, slidVec.y }

						table.insert(entity.edgeResults, vec2(-newCoords.x, -newCoords.y))

						if dist < collided or #entity.edgeResults > 1 then
							entity:setPosition(-newCoords.x, nil, -newCoords.y)
							scene_updateEntityPlaneVars(entity)
							scene_debugPlanes(entity, 7, slide)
							collided = dist
						end
					end
				end
			end
		end

		if nearfloor and entity.v.y < 1 then
			entity.v.y = 0
			entity:setPosition(nil, -entity.floor)
		else
			entity:changeVelocity(0, -1, 0) --keyDown("q") and -1 or keyDown("e") and 1 or 0,0)
		end

		entity:setPosition(nil, math.max(-entity.floor, entity.y))
		entity.position.y = math.max(-entity.floor, entity.y)
	end
end

function scene_updateEntityPlaneVars(entity)
	entity.planeAng = math.angle(0, 0, entity.v.x, entity.v.z)
	entity.planeDist = math.max(1, math.dist(0, 0, entity.v.x, entity.v.z))
	entity.planeDir = vec2.from_cartesian(entity.planeDist, entity.planeAng)
	entity.planePos1.x, entity.planePos1.y = -entity.x, -entity.z
	entity.planePos2.x, entity.planePos2.y = -entity.x - entity.planeDir.x, -entity.z - entity.planeDir.y
	if entity.frameOffsetStored then
		entity.planePos3 = { x = -entity.x + entity.frameOffsetStored.x, y = -entity.z + entity.frameOffsetStored.z }
	end
end

function scene_debugPlanes(entity, step, plane, color)
	if not editor.debug.draw_ground_planes then
		return
	end
	editor_pushDraw(function()
	gfx.push()
	gfx.translate(screenW / 2, screenH / 2)
	gfx.rotate(-editor.viewport.camera.angle.x)
	gfx.translate(editor.viewport.camera.x, editor.viewport.camera.z)
	gfx.scale(0.5, 0.5)
	--gfx.translate(-screenWidth/2,-screenHeight/2)

	if step == 1 then
		gfx.setColor(1, 1, 1, 0.5)
		gfx.circle("line", -entity.x, -entity.z, 12)
		gfx.line(plane.shape)
	elseif step == 2 then
		gfx.setColor(1, 1, 1, 1)
		gfx.polygon("line", plane.shape)
		gfx.circle("line", -entity.x, -entity.z, 9)
	elseif step == 3 then
		gfx.circle("fill", -entity.x, -entity.z, 6)
		gfx.setColor(1, 1, 1, 1)
		gfx.setLineWidth(3)
		gfx.polygon("line", plane.shape)
	elseif step == 4 then
		gfx.setColor(1, 0, 0, 1)
		gfx.line(entity.plane.shape)
	elseif step == 5 then
		gfx.setLineWidth(2)
		gfx.setColor(0, 1, 0, 1)
		gfx.line(plane)
	elseif step == 6 then
		gfx.setLineWidth(2)
		gfx.setColor(0, 0, 1, 1)
		gfx.line(plane)
	elseif step == 7 then
		gfx.setLineWidth(4)
		gfx.setColor(1, 0, 1, 1)
		gfx.line(plane)
	end
	gfx.pop()
	end)
end

function scene_updateEntityPlane(entity)
	local floored = false
	local bottom = 0 --entity.pointBottom and 0 or entity.bbox[5]
	if not entity.ghost and not (entity.simple and pathsPanel.open) and entity.gravity then
		local ceiled = false
		local highestFloor = 0
		for p, plane in ipairs(room.planes) do
			local incline = 40 --(plane.incline or 10)*2
			local ytest = -(entity.y + (entity.bbox and entity.bbox[5] or 0) + incline)
			local ytest2 = -(entity.bbbox and entity.bbbox[2] or entity.y - 20) --+(entity.prev.y-entity.y)+(entity.bbox and entity.bbox[5] or 0)-incline

			scene_debugPlanes(entity, 1, plane)
			if ytest < plane.y and plane.group ~= 8 then
				scene_debugPlanes(entity, 2, plane)

				if point_in_poly(plane.shape, { x = -entity.x, y = -entity.z }) then
					scene_debugPlanes(entity, 3, plane)
					if plane.isFloor then
						if plane.y < highestFloor then
							highestFloor = plane.y
							entity.plane = plane
							entity.floor = plane.y
							floored = true
						end
					else
						local floor = floors_evaluateRamp(plane, -entity.x, -entity.z, true)
						trace("ramp", floor, ytest)
						if ytest < floor and floor < highestFloor then
							highestFloor = floor
							entity.plane = plane
							entity.floor = floor
							floored = true
						end
					end
				end
			end
		end
		if entity.plane then
			scene_debugPlanes(entity, 4)
		end

		entity.floor = floored and (entity.bbox and entity.floor - math.max(entity.bbox[5], 0) or entity.floor)
			or (editor.gridEnabled and 0 or math.huge)
		entity.floorb = entity.floor + (entity.bbox and entity.bbox[5] or 0) --+entity.bbox[5]*2+40
	end
	-- LINK
	--trace("entity.transferring", entity.transferring)
	if
		not ctx.editing
		and entity.transferring == false
		and entity.plane
		and (entity.plane.dest or entity.plane.source)
		and floored
	then
		local side, l2, ang, r1, r2, d, l
		local projected =
			{ x = entity.x - (entity.prev.x - entity.x) * 2, z = entity.z - (entity.prev.z - entity.z) * 2 }
		local s = entity.plane.shape
		local s = entity.plane.source and { s[1], s[2], s[7], s[8] } or { s[3], s[4], s[5], s[6] }
		local dist = distPointToLineSegment({ entity.x, entity.z }, { s[1], s[2] }, { s[3], s[4] })
		d = entity.plane.source or entity.plane.dest
		if dist < 10 and point_in_poly(entity.plane.shape, { x = entity.x, y = entity.z }) then
			ang = math.angle(s[1], s[2], s[3], s[4]) + pi / 2
			entity.testline = {
				{ x = s[1], y = s[2] },
				{ x = s[3], y = s[4] },
			}

			r1, r2 = math.intersect(
				{ x = projected.x, y = projected.z },
				{ x = entity.x, y = entity.z },
				entity.testline[1],
				entity.testline[2]
			)

			if r1 then
				local dest = ctx.rooms[d.room][d.mode][d.path]
				if dest then
					local progress = mc(
						0,
						(math.dist(entity.x, entity.z, s[1], s[2]) - math.dist(entity.x, entity.z, s[3], s[4]))
								/ math.dist(s[1], s[2], s[3], s[4])
								* 0.5
							+ 0.5,
						1
					)
					local coords = dest.points[d.side == "source" and 1 or #dest.points]
					l = dest[d.side].line
					coords = {
						x = lerp(l[1], l[3], progress),
						z = lerp(l[2], l[4], progress),
						y = l[5] - 10,
					}
					entity.transferring = true
					entity:transfer(coords, d.room ~= room.key and d.room or nil)
				end
			end
		end
	end
end

function scene_updateScripts(entity)
	if ctx.editing then
		return
	end
	local success
	for s, script in ipairs(entity.scripts) do
		if script.enabled and script.file and not script.broken then
			if script.file.load and game.time == 1 then
				success = pcall(script.file.load)
			elseif script.file.loop and game.time > 1 then
				success = pcall(script.file.loop)
			end
		end
	end
end

function scene_updateEntityBounds(entity)
	-- HOVER
	if --view==ctx.viewport
		entity.visible
		and (editor.lastviewHovered == ctx.viewport or ctx.roomZoom == 0)
		and not editor.linkHovered
		and editor.stageHovered
		--and entity.layer==layer
		and editor.currentTool == "Select"
		and (editor.state == "Normal" or editor.state == "Transforming")
		and not (animatePanel.open and animatePanel.instance == entity)
		and (not pathsPanel.open or entity.simple)
	then
		if (entity.simple or entity.light.enabled) and entity.render and entity.render.x then
			if math.dist(mouse.x, mouse.y, entity.render.x, entity.render.y) < pathsPanel.pointSize * 1.5 then
				editor.pointing = true
				editor.modelHovered = nil
				editor.entityHovered = entity
				scene_setHoveredEntity(entity)
				entity.hovered = true
			end
		elseif editor.entityHovered == entity then
			if ctx.editing and ctx.viewport.key == ctx.viewport.room.key then
				if editor.hoveredEntity ~= nil then
					editor.hoveredEntity.hovered = false
				end
				scene_setHoveredEntity(entity)
			end
		end
	end
end

function scene_setHoveredEntity(entity)
	editor.hoveredEntity = entity
	entity.hovered = true
end

function scene_generateEvents(entity) end

function scene_updateEntityRenderInfo(entity)
	if not editor.forceProject then
		entity.projected = false
	end
	if
		(editor.forceProject and entity.projected == false and editor.selectBounds.h > 4 and editor.selectBounds.w > 4)
		or not (entity.render and entity.render.x)
	then
		entity.render = entity.render or {}
		entity.projected = true
		local proj = camera_project(entity.position or entity)
		if proj then
			entity.render.x, entity.render.y = proj.x, proj.y
		end
		return entity.render
	end
end

function scene_getMatrix(model)
	model.matrix = model.matrix or mat4()
	model.transform = model.transform or mat4()
	-- return model.matrix:transpose(tMat3:mul(tMat1:invert(model.transform),tMat2:invert(model.parent and model.parent.transform or tMat4 )))
	return model.parent and model.matrix:mul(model.transform, scene_getMatrix(model.parent)) or model.transform
end

function scene_renderRegPoint(entity)
	gfx.setColor(1, 1, 1, 0.8)
	gfx.draw(load_imageTex("BodyCenter"), mf(entity.render.x), mf(entity.render.y), 0, 0.5, 0.5, 10, 10)
end

function scene_renderBBox(entity)
	if entity.bbox == nil or entity.simple then
		return
	end
	local box = entity.bbox
	gfx.setLineWidth(1)
	gfx.setColor(1, 1, 1, 0.2)
	gfx.polygon("line", { box[1], box[2], box[3], box[2], box[3], box[4], box[1], box[4] })
	gfx.setColor(0, 0, 0, 0.2)
	gfx.polygon(
		"line",
		{ box[1] + 1, box[2] + 1, box[3] + 1, box[2] + 1, box[3] + 1, box[4] + 1, box[1] + 1, box[4] + 1 }
	)
end

function scene_drawEntityLinks(entity)
	-- PARENT LINK -----------------------
	if entity.parentE ~= nil and entity.parent.r2 ~= nil and game.state == "Edit" and not entity.simple then
		gfx.setCanvas(engine.canvas)
		if entity.parent.ptype == "Weld" then
			gfx.setColor(1, 0.5, 0.75, 1)
		else
			gfx.setColor(0.75, 0.5, 1, 1)
		end
		gfx.polygon(
			"fill",
			entity.parentE.render.x + math.cos(entity.parent.r2 + pi / 2) * 8 * cam.zoom,
			entity.parentE.render.y + math.sin(entity.parent.r2 + pi / 2) * 8 * cam.zoom,
			entity.render.x,
			entity.render.y,
			entity.parentE.render.x,
			entity.parentE.render.y
		)
	end
end
