Bladeren bron

tools: fix ScaleTool

Daniele Bartolini 5 jaren geleden
bovenliggende
commit
0955550387
2 gewijzigde bestanden met toevoegingen van 91 en 67 verwijderingen
  1. 6 2
      docs/changelog.rst
  2. 85 65
      samples/core/editors/level_editor/level_editor.lua

+ 6 - 2
docs/changelog.rst

@@ -7,8 +7,12 @@ Changelog
 
 
 **Runtime**
 **Runtime**
 
 
-* Upgraded to LuaJIT 2.1
-* Added support to 64-bits Android (ARMv8-a)
+* Upgraded to LuaJIT 2.1.
+* Added support to 64-bits Android (ARMv8-a).
+
+**Tools**
+
+* The Scale Tool will now work as expected.
 
 
 0.41.0
 0.41.0
 ------
 ------

+ 85 - 65
samples/core/editors/level_editor/level_editor.lua

@@ -79,6 +79,28 @@ function draw_grid(lines, tm, center, size, axis, color)
 	Device.set_temp_count(nv, nq, nm)
 	Device.set_temp_count(nv, nq, nm)
 end
 end
 
 
+-- From Bitsquid's math.lua
+function line_line(line_a_pt1, line_a_pt2, line_b_pt1, line_b_pt2)
+	local line_a_vector = line_a_pt2 - line_a_pt1
+	local line_b_vector = line_b_pt2 - line_b_pt1
+	local a = Vector3.dot(line_a_vector, line_a_vector)
+	local e = Vector3.dot(line_b_vector, line_b_vector)
+	local b = Vector3.dot(line_a_vector, line_b_vector)
+	local d = a * e - b * b
+
+	if d < 0.001 then
+		-- The lines are parallel. There is no intersection.
+		return nil, nil
+	end
+
+	local r = line_a_pt1 - line_b_pt1
+	local c = Vector3.dot(line_a_vector, r)
+	local f = Vector3.dot(line_b_vector, r)
+	local normalized_distance_along_line_a = (b * f - c * e) / d
+	local normalized_distance_along_line_b = (a * f - b * c) / d
+	return normalized_distance_along_line_a, normalized_distance_along_line_b
+end
+
 function draw_world_origin_grid(lines, size, step)
 function draw_world_origin_grid(lines, size, step)
 	local nv, nq, nm = Device.temp_count()
 	local nv, nq, nm = Device.temp_count()
 
 
@@ -867,28 +889,6 @@ function MoveTool:update(dt, x, y)
 end
 end
 
 
 function MoveTool:drag_offset(x, y)
 function MoveTool:drag_offset(x, y)
-	-- From Bitsquid's math.lua
-	function line_line(line_a_pt1, line_a_pt2, line_b_pt1, line_b_pt2)
-		local line_a_vector = line_a_pt2 - line_a_pt1
-		local line_b_vector = line_b_pt2 - line_b_pt1
-		local a = Vector3.dot(line_a_vector, line_a_vector)
-		local e = Vector3.dot(line_b_vector, line_b_vector)
-		local b = Vector3.dot(line_a_vector, line_b_vector)
-		local d = a * e - b * b
-
-		if d < 0.001 then
-			-- The lines are parallel. There is no intersection.
-			return nil, nil
-		end
-
-		local r = line_a_pt1 - line_b_pt1
-		local c = Vector3.dot(line_a_vector, r)
-		local f = Vector3.dot(line_b_vector, r)
-		local normalized_distance_along_line_a = (b * f - c * e) / d
-		local normalized_distance_along_line_b = (a * f - b * c) / d
-		return normalized_distance_along_line_a, normalized_distance_along_line_b
-	end
-
 	local pos, dir = LevelEditor:camera():camera_ray(x, y)
 	local pos, dir = LevelEditor:camera():camera_ray(x, y)
 	local drag_axis = self:drag_axis()
 	local drag_axis = self:drag_axis()
 	local drag_plane = self:drag_plane()
 	local drag_plane = self:drag_plane()
@@ -1181,7 +1181,9 @@ function ScaleTool:init()
 	self._position    = Vector3Box(Vector3.zero())
 	self._position    = Vector3Box(Vector3.zero())
 	self._drag_start  = Vector3Box(Vector3.zero())
 	self._drag_start  = Vector3Box(Vector3.zero())
 	self._drag_offset = Vector3Box(Vector3.zero())
 	self._drag_offset = Vector3Box(Vector3.zero())
+	self._scale_start = Vector3Box(Vector3.zero())
 	self._selected    = nil
 	self._selected    = nil
+	self.SCALE_MIN    = 0.01
 	-- States
 	-- States
 	self._state = "idle"
 	self._state = "idle"
 end
 end
@@ -1237,6 +1239,10 @@ function ScaleTool:is_idle()
 	return self._state == "idle"
 	return self._state == "idle"
 end
 end
 
 
+function ScaleTool:is_scaling()
+	return self._state == "scaling"
+end
+
 function ScaleTool:set_state(state)
 function ScaleTool:set_state(state)
 	assert(state == "idle" or state == "scaling")
 	assert(state == "idle" or state == "scaling")
 	self._state = state
 	self._state = state
@@ -1265,18 +1271,15 @@ function ScaleTool:update(dt, x, y)
 
 
 	local l = LevelEditor:camera():screen_length_to_world_length(self:position(), Gizmo.size)
 	local l = LevelEditor:camera():screen_length_to_world_length(self:position(), Gizmo.size)
 
 
-	-- Select axis
 	if self:is_idle() then
 	if self:is_idle() then
-		local tm = self:world_pose()
-
 		-- Select axis
 		-- Select axis
-		local pos, dir = LevelEditor:camera():camera_ray(x, y)
-
 		self._selected = "none"
 		self._selected = "none"
-		if Math.ray_obb_intersection(pos, dir, transform(tm, l * Vector3(1, 0, 0)), l * Vector3(0.1, 0.1, 0.1)) ~= -1.0 then self._selected = "x" end
-		if Math.ray_obb_intersection(pos, dir, transform(tm, l * Vector3(0, 1, 0)), l * Vector3(0.1, 0.1, 0.1)) ~= -1.0 then self._selected = "y" end
-		if Math.ray_obb_intersection(pos, dir, transform(tm, l * Vector3(0, 0, 1)), l * Vector3(0.1, 0.1, 0.1)) ~= -1.0 then self._selected = "z" end
-		if Math.ray_obb_intersection(pos, dir, transform(tm, l * Vector3.zero()), l * Vector3(0.1, 0.1, 0.1)) ~= -1.0 then self._selected = "xyz" end
+		local tm = self:world_pose()
+		local pos, dir = LevelEditor:camera():camera_ray(x, y)
+		if Math.ray_obb_intersection(pos, dir, transform(tm, l * Vector3(1, 0, 0)), l * Vector3(0.05, 0.05, 0.05)) ~= -1.0 then self._selected = "x" end
+		if Math.ray_obb_intersection(pos, dir, transform(tm, l * Vector3(0, 1, 0)), l * Vector3(0.05, 0.05, 0.05)) ~= -1.0 then self._selected = "y" end
+		if Math.ray_obb_intersection(pos, dir, transform(tm, l * Vector3(0, 0, 1)), l * Vector3(0.05, 0.05, 0.05)) ~= -1.0 then self._selected = "z" end
+		if Math.ray_disc_intersection(pos, dir, Matrix4x4.translation(tm), l * 0.5, Matrix4x4.z(LevelEditor:camera():local_pose())) ~= -1.0 then self._selected = "xyz" end
 	end
 	end
 
 
 	-- Drawing
 	-- Drawing
@@ -1288,38 +1291,37 @@ function ScaleTool:update(dt, x, y)
 	DebugLine.add_line(lines, p, p + l * Matrix4x4.y(tm), self._selected == "y" and Colors.axis_selected() or Colors.axis_y())
 	DebugLine.add_line(lines, p, p + l * Matrix4x4.y(tm), self._selected == "y" and Colors.axis_selected() or Colors.axis_y())
 	DebugLine.add_line(lines, p, p + l * Matrix4x4.z(tm), self._selected == "z" and Colors.axis_selected() or Colors.axis_z())
 	DebugLine.add_line(lines, p, p + l * Matrix4x4.z(tm), self._selected == "z" and Colors.axis_selected() or Colors.axis_z())
 
 
-	DebugLine.add_obb(lines, transform(tm, l * Vector3(1, 0, 0)), l * Vector3(0.1, 0.1, 0.1), self._selected == "x" and Colors.axis_selected() or Colors.axis_x())
-	DebugLine.add_obb(lines, transform(tm, l * Vector3(0, 1, 0)), l * Vector3(0.1, 0.1, 0.1), self._selected == "y" and Colors.axis_selected() or Colors.axis_y())
-	DebugLine.add_obb(lines, transform(tm, l * Vector3(0, 0, 1)), l * Vector3(0.1, 0.1, 0.1), self._selected == "z" and Colors.axis_selected() or Colors.axis_z())
-	DebugLine.add_obb(lines, transform(tm, l * Vector3.zero()), l * Vector3(0.1, 0.1, 0.1), self._selected == "xyz" and Colors.axis_selected() or Colors.axis_z())
+	DebugLine.add_obb(lines, transform(tm, l * Vector3(1, 0, 0)), l * Vector3(0.05, 0.05, 0.05), self._selected == "x" and Colors.axis_selected() or Colors.axis_x())
+	DebugLine.add_obb(lines, transform(tm, l * Vector3(0, 1, 0)), l * Vector3(0.05, 0.05, 0.05), self._selected == "y" and Colors.axis_selected() or Colors.axis_y())
+	DebugLine.add_obb(lines, transform(tm, l * Vector3(0, 0, 1)), l * Vector3(0.05, 0.05, 0.05), self._selected == "z" and Colors.axis_selected() or Colors.axis_z())
+	DebugLine.add_circle(lines, p, l * 0.5, Matrix4x4.z(LevelEditor:camera():local_pose()), self._selected == "xyz" and Colors.axis_selected() or Colors.grid())
 end
 end
 
 
 function ScaleTool:mouse_move(x, y)
 function ScaleTool:mouse_move(x, y)
-	if self:is_idle() then return end
-
-	if self:axis_selected() then
-			local delta = self:drag_offset(x, y) - self._drag_offset:unbox()
-			local drag_vector = Vector3.zero()
+	if self:is_idle() then
+		return
+	elseif self:axis_selected() then
+		local delta = self:drag_offset(x, y) - self._drag_offset:unbox()
+		local delta_vector = Vector3.zero()
 
 
-			for _, a in ipairs{"x", "y", "z"} do
-				local axis = Vector3.zero()
+		for _, a in ipairs{"x", "y", "z"} do
+			if self:is_axis_selected(a) then
+				if a == "x" then delta_vector.x = delta.x end
+				if a == "y" then delta_vector.y = delta.y end
+				if a == "z" then delta_vector.z = delta.z end
+			end
+		end
 
 
-				if self:is_axis_selected(a) then
-					if a == "x" then axis = Vector3.right() end
-					if a == "y" then axis = Vector3.up() end
-					if a == "z" then axis = Vector3.forward() end
-				end
+		local axis_length = LevelEditor:camera():screen_length_to_world_length(self:position(), Gizmo.size)
+		local scale_percent = delta_vector * (1 / axis_length)
 
 
-				local contribution = Vector3.dot(axis, delta)
-				drag_vector = drag_vector + axis*contribution
-			end
+		local local_scale = self._scale_start:unbox()
+		local_scale.x = math.max(self.SCALE_MIN, local_scale.x + (local_scale.x * scale_percent.x))
+		local_scale.y = math.max(self.SCALE_MIN, local_scale.y + (local_scale.y * scale_percent.y))
+		local_scale.z = math.max(self.SCALE_MIN, local_scale.z + (local_scale.z * scale_percent.z))
 
 
-			local selected = LevelEditor._selection:last_selected_object()
-			local pos = Vector3(1, 1, 1) + drag_vector
-			-- print(Vector3.to_string(self:drag_start()))
-			-- print(Vector3.to_string(pos))
-			-- print(Matrix4x4.to_string(selected:world_pose()) .. "\n")
-			-- selected:set_local_scale(LevelEditor:snap(self:world_pose(), pos) or pos)
+		local selected = LevelEditor._selection:last_selected_object()
+		selected:set_local_scale(LevelEditor:snap(self:world_pose(), local_scale) or local_scale)
 	end
 	end
 end
 end
 
 
@@ -1328,45 +1330,63 @@ function ScaleTool:mouse_down(x, y)
 		self:set_state("scaling")
 		self:set_state("scaling")
 		self._drag_start:store(self:position())
 		self._drag_start:store(self:position())
 		self._drag_offset:store(self:drag_offset(x, y))
 		self._drag_offset:store(self:drag_offset(x, y))
+		self._scale_start:store(LevelEditor._selection:last_selected_object():local_scale())
 	else
 	else
 		LevelEditor.select_tool:mouse_down(x, y)
 		LevelEditor.select_tool:mouse_down(x, y)
 	end
 	end
 end
 end
 
 
 function ScaleTool:mouse_up(x, y)
 function ScaleTool:mouse_up(x, y)
-	self:set_state("idle")
+	if self:is_scaling() then
+		LevelEditor._selection:send_move_objects()
+	end
 
 
 	if self:axis_selected() then
 	if self:axis_selected() then
 		self._drag_start:store(Vector3.zero())
 		self._drag_start:store(Vector3.zero())
 		self._drag_offset:store(Vector3(1, 1, 1))
 		self._drag_offset:store(Vector3(1, 1, 1))
+		self._scale_start:store(Vector3.zero())
 	else
 	else
 		LevelEditor.select_tool:mouse_up(x, y)
 		LevelEditor.select_tool:mouse_up(x, y)
 	end
 	end
+
+	self:set_state("idle")
 end
 end
 
 
 function ScaleTool:is_axis_selected(axis)
 function ScaleTool:is_axis_selected(axis)
 	return string.find(self._selected, axis)
 	return string.find(self._selected, axis)
 end
 end
 
 
+function ScaleTool:drag_axis()
+	if self._selected == "x" then return self:x_axis()
+	elseif self._selected == "y" then return self:y_axis()
+	elseif self._selected == "z" then return self:z_axis()
+	else return nil end
+end
+
 function ScaleTool:drag_plane()
 function ScaleTool:drag_plane()
-	if self._selected == "x"   then return self:position(), self:y_axis() end
-	if self._selected == "y"   then return self:position(), self:x_axis() end
-	if self._selected == "z"   then return self:position(), self:y_axis() end
-	if self._selected == "xyz" then return self:position(), self:x_axis() end
-	return nil
+	local camera_dir = Matrix4x4.z(LevelEditor:camera():local_pose())
+	if self._selected == "xyz" then return self:position(), -camera_dir
+	else return nil end
 end
 end
 
 
 function ScaleTool:drag_offset(x, y)
 function ScaleTool:drag_offset(x, y)
+	local drag_axis = self:drag_axis()
 	local drag_plane = self:drag_plane()
 	local drag_plane = self:drag_plane()
 
 
-	if (drag_plane ~= nil) then
+	if drag_axis ~= nil then
+		local pos, dir = LevelEditor:camera():camera_ray(x, y)
+		local _, delta = line_line(pos, pos + dir, self:drag_start(), self:drag_start() + drag_axis)
+		if delta ~= nil then
+			return Vector3(delta, delta, delta)
+		end
+	elseif drag_plane ~= nil then
 		local pos, dir = LevelEditor:camera():camera_ray(x, y)
 		local pos, dir = LevelEditor:camera():camera_ray(x, y)
 		local t = Math.ray_plane_intersection(pos, dir, self:drag_plane())
 		local t = Math.ray_plane_intersection(pos, dir, self:drag_plane())
 
 
 		if t ~= -1.0 then
 		if t ~= -1.0 then
 			local point_on_plane = pos + dir*t
 			local point_on_plane = pos + dir*t
-			local offset = point_on_plane - self:drag_start()
-			return offset;
+			local distance = Vector3.distance(point_on_plane, self:drag_start())
+			return Vector3(1, 1, 1) * distance
 		end
 		end
 	end
 	end