File size: 4,435 Bytes
b6a38d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
-- ExtrasGen based editor tools place fences, decals, etc. alongside these line guide helper objects
--
-- The line guide is aligned to the object's X axis, and the line normal direction - to the Z axis.
-- The line guide length is determined by the object's Scale; it is StandardLength long at normal scale.

DefineClass.EditorLineGuide = {
	__parents = { "Mesh", "CollideLuaObject", "EditorVisibleObject", "EditorCallbackObject" },
	
	StandardLength = 10 * guim, -- length at 100% scale
	NormalColor = RGB(240, 240, 240),
	HighlightColor = RGB(240, 230, 150),
	SelectedColor = RGB(240, 230, 40),
	
	collide_mesh = false,
	color = RGB(240, 240, 240),
}

-- rotates the object so that axis1 before the rotation matches axis2 after the rotation
local function rotate_to_match(obj, axis1, axis2)
	axis1, axis2 = SetLen(axis1, 4096), SetLen(axis2, 4096)
	local axis = Cross(axis1, axis2)
	if axis ~= point30 then
		obj:Rotate(axis, GetAngle(axis1, axis2))
	end
end

function EditorLineGuide:Set(pos1, pos2, normal)
	local pos = (pos1 + pos2) / 2
	self:SetPos(pos)
	self:SetOrientation(normal, 0)
	self:SetScale(MulDivRound((pos1 - pos2):Len(), 100, self.StandardLength))
	
	local axis1 = self:GetRelativePoint(axis_y) - self:GetPos()
	local axis2 = pos1 - self:GetPos()
	rotate_to_match(self, axis1, axis2)
	
	self:SetGameFlags(const.gofPermanent)
	self:UpdateVisuals()
end

function EditorLineGuide:GetLength()
	return MulDivRound(self.StandardLength, self:GetScale(), 100)
end

function EditorLineGuide:SetLength(length)
	self:SetScale(MulDivRound(length, 100, self.StandardLength))
	self:UpdateVisuals()
end

function EditorLineGuide:GetPos1()
	return self:GetRelativePoint(SetLen(axis_y, self.StandardLength / 2))
end

function EditorLineGuide:GetPos2()
	return self:GetRelativePoint(-SetLen(axis_y, self.StandardLength / 2))
end

function EditorLineGuide:GetNormal()
	return self:GetRelativePoint(axis_z) - self:GetVisualPos()
end

function EditorLineGuide:IsHorizontal()
	local tangent = self:GetRelativePoint(axis_y) - self:GetPos()
	local angle = GetAngle(tangent, axis_z) / 60
	return abs(angle) > 85
end

function EditorLineGuide:IsVertical()
	local tangent = self:GetRelativePoint(axis_y) - self:GetPos()
	local angle = GetAngle(tangent, axis_z) / 60
	return angle < 5 or angle > 175
end

function EditorLineGuide:UpdateVisuals()
	if self:GetScale() == 0 then
		self:SetMesh(pstr(""))
		return
	end

	local offset = SetLen(axis_y, self.StandardLength / 2)
	local arrowlen = MulDivRound(guim / 2, 100, self:GetScale())
	local normal = SetLen(axis_z, arrowlen)
	local along = SetLen(offset, arrowlen / 2)

	local str = pstr("")
	str:AppendVertex(offset, self.color)
	str:AppendVertex(-offset)
	str:AppendVertex(-along)
	str:AppendVertex(normal)
	str:AppendVertex(normal)
	str:AppendVertex(along)
	self:SetShader(ProceduralMeshShaders.mesh_linelist)
	self:SetMesh(str)
	
	if IsEditorActive() then
		self:SetEnumFlags(const.efVisible)
	end
end

EditorLineGuide.EditorCallbackPlaceCursor = EditorLineGuide.UpdateVisuals
EditorLineGuide.EditorCallbackPlace       = EditorLineGuide.UpdateVisuals
EditorLineGuide.EditorCallbackScale       = EditorLineGuide.UpdateVisuals
EditorLineGuide.EditorEnter               = EditorLineGuide.UpdateVisuals

function EditorLineGuide:GetBBox()
	local grow = guim / 4
	local length = self:GetLength()
	return GrowBox(box(0, -length / 2, 0, 0, length / 2, 0), grow, grow, grow)
end

function EditorLineGuide:TestRay(pos, dir)
	-- TODO: Refactor C++ code to expect intersection point to be returned
	return true
end


----- Selection and highlighting on hover

if FirstLoad then
	SelectedLineGuides = {}
end

function EditorLineGuide:SetHighlighted(highlight)
	local selected = table.find(SelectedLineGuides, self)
	self.color = selected  and self.SelectedColor  or
	             highlight and self.HighlightColor or self.NormalColor
	self:UpdateVisuals()
end

function OnMsg.EditorSelectionChanged(objects)
	local lines = table.ifilter(objects, function(idx, obj) return IsKindOf(obj, "EditorLineGuide") end )
	if #lines > 0 then
		for _, line in ipairs(table.subtraction(lines, SelectedLineGuides)) do
			line.color = line.SelectedColor
			line:UpdateVisuals()
		end
	end
	if #SelectedLineGuides > 0 then
		for _, line in ipairs(table.subtraction(SelectedLineGuides, lines)) do
			if IsValid(line) then
				line.color = nil
				line:UpdateVisuals()
			end
		end
	end
	SelectedLineGuides = lines
end