diff --git a/lua/autorun/ragdollmover.lua b/lua/autorun/ragdollmover.lua index 3ae7d6a..8698198 100644 --- a/lua/autorun/ragdollmover.lua +++ b/lua/autorun/ragdollmover.lua @@ -1050,16 +1050,40 @@ function DrawEntName(ent) draw.SimpleTextOutlined(name, "RagdollMoverFont", textpos.x, textpos.y, COLOR_RGMGREEN, TEXT_ALIGN_LEFT, TEXT_ALIGN_BOTTOM, OUTLINE_WIDTH, COLOR_RGMBLACK) end -local RGM_CIRCLE = { - { x = -3, y = -3 }, +local RGM_SHAPES = { + { -- Square, phys + { x = -5, y = -5 }, - { x = 0, y = -4 }, - { x = 3, y = -3 }, - { x = 4, y = 0 }, - { x = 3, y = 3 }, - { x = 0, y = 4 }, - { x = -3, y = 3 }, - { x = -4, y = 0 } + { x = 5, y = -5 }, + { x = 5, y = 5 }, + { x = -5, y = 5 } + }, + + { -- Circle, nonphys + { x = -3, y = -3 }, + + { x = 0, y = -4 }, + { x = 3, y = -3 }, + { x = 4, y = 0 }, + { x = 3, y = 3 }, + { x = 0, y = 4 }, + { x = -3, y = 3 }, + { x = -4, y = 0 } + }, + + { -- Triangle, procedural + { x = 0, y = -5 }, + + { x = 5, y = 5 }, + { x = -5, y = 5 } + }, + + { -- 180 rotated triangle, parented + { x = -5, y = -5 }, + + { x = 5, y = -5 }, + { x = 0, y = 5 } + } } local LockGo = Material("icon16/lock_go.png", "alphatest") @@ -1165,25 +1189,27 @@ function AdvBoneSelectRender(ent, bonenodes, prevbones, calc, eyePos, eyeVector, prevbones[i] = math.Clamp(math.ceil(fraction * NUM_GRADIENT_POINTS), 1, NUM_GRADIENT_POINTS ) end + local bonetype = bonenodes[ent][i] and bonenodes[ent][i].Type or 1 + if dist < 576 then -- 24 pixels surface.SetDrawColor(COLOR_BRIGHT_YELLOW:Unpack()) table.insert(selectedBones, {name, i}) else - if nodesExist and bonenodes[ent][i] and bonenodes[ent][i].Type and prevbones[i] then - surface.SetDrawColor(BONETYPE_COLORS[bonenodes[ent][i].Type][prevbones[i]]:Unpack()) + if nodesExist and bonetype and prevbones[i] then + surface.SetDrawColor(BONETYPE_COLORS[bonetype][prevbones[i]]:Unpack()) else surface.SetDrawColor(COLOR_RGMGREEN:Unpack()) end end - local circ = table.Copy(RGM_CIRCLE) - for k, v in ipairs(circ) do + local shape = table.Copy(RGM_SHAPES[bonetype]) + for k, v in ipairs(shape) do v.x = v.x + x v.y = v.y + y end draw.NoTexture() - surface.DrawPoly(circ) + surface.DrawPoly(shape) if bonenodes[ent][i].bonelock then surface.SetMaterial(LockGo) @@ -1210,6 +1236,7 @@ function AdvBoneSelectRender(ent, bonenodes, prevbones, calc, eyePos, eyeVector, local scrH = ScrH() - 100 -- Some padding to keep the bones centered local columns = 0 + local id = #selectedBones -- List the selected bones. If they attempt to overflow through the screen, add the items to another column. for i = 0, #selectedBones - 1 do local yPos = my + (i % maxItemsPerColumn) * (RGMFontSize + 3) @@ -1230,9 +1257,13 @@ function AdvBoneSelectRender(ent, bonenodes, prevbones, calc, eyePos, eyeVector, end draw.SimpleTextOutlined(selectedBones[i + 1][1], "RagdollMoverFont", xPos, yPos, color, TEXT_ALIGN_LEFT, TEXT_ALIGN_BOTTOM, OUTLINE_WIDTH, COLOR_RGMBLACK) + + -- Modify the data structure of the individual selectedBone so we don't have to worry about indexing later on + selectedBones[i + 1] = selectedBones[i + 1][2] + id = id + selectedBones[i + 1] end - return prevbones + return prevbones, selectedBones, id end function AdvBoneSelectPick(ent, bonenodes) @@ -1315,8 +1346,12 @@ function AdvBoneSelectRadialRender(ent, bones, bonenodes, isresetmode) local thisrad = thisang / 180 * math.pi local uix, uiy = (math.sin(thisrad) * 250 * modifier), (math.cos(thisrad) * -250 * modifier) local color = COLOR_WHITE - if bonenodes and bonenodes[ent] and bonenodes[ent][bone] and bonenodes[ent][bone].Type then - color = BONETYPE_COLORS[bonenodes[ent][bone].Type][1] + + local nodecheck = (bonenodes and bonenodes[ent] and bonenodes[ent][bone] and bonenodes[ent][bone].Type) and true or false + local bonetype = bonenodes[ent][bone] and bonenodes[ent][bone].Type or 1 + + if nodecheck then + color = BONETYPE_COLORS[bonetype][1] end uix, uiy = uix + midw, uiy + midh @@ -1328,8 +1363,8 @@ function AdvBoneSelectRadialRender(ent, bones, bonenodes, isresetmode) local pos = ent:GetBonePosition(bone) pos = pos:ToScreen() - local circ = table.Copy(RGM_CIRCLE) - for k, v in ipairs(circ) do + local shape = table.Copy(RGM_SHAPES[bonetype]) + for k, v in ipairs(shape) do v.x = v.x + pos.x v.y = v.y + pos.y end @@ -1339,15 +1374,11 @@ function AdvBoneSelectRadialRender(ent, bones, bonenodes, isresetmode) color = COLOR_BRIGHT_YELLOW SelectedBone = bone else - if bonenodes and bonenodes[ent] and bonenodes[ent][bone] and bonenodes[ent][bone].Type then - surface.SetDrawColor(BONETYPE_COLORS[bonenodes[ent][bone].Type][1]:Unpack()) - else - surface.SetDrawColor(COLOR_RGMGREEN:Unpack()) - end + surface.SetDrawColor(BONETYPE_COLORS[bonetype][1]:Unpack()) end draw.NoTexture() - surface.DrawPoly(circ) + surface.DrawPoly(shape) local ytextoffset = -14 if uiy > (midh + 30) then ytextoffset = RGMFontSize + 14 end @@ -1365,6 +1396,7 @@ function AdvBoneSelectRadialRender(ent, bones, bonenodes, isresetmode) draw.SimpleTextOutlined(name, "RagdollMoverFont", uix + xtextoffset, uiy + ytextoffset, color, TEXT_ALIGN_CENTER, TEXT_ALIGN_BOTTOM, OUTLINE_WIDTH, COLOR_RGMBLACK) end + return {SelectedBone}, SelectedBone and 1 + SelectedBone or 0 else local bone = bones[1] local btype = 2 @@ -1375,8 +1407,8 @@ function AdvBoneSelectRadialRender(ent, bones, bonenodes, isresetmode) local pos = ent:GetBonePosition(bone) pos = pos:ToScreen() - local circ = table.Copy(RGM_CIRCLE) - for k, v in ipairs(circ) do + local shape = table.Copy(RGM_SHAPES[btype]) + for k, v in ipairs(shape) do v.x = v.x + pos.x v.y = v.y + pos.y end @@ -1384,7 +1416,7 @@ function AdvBoneSelectRadialRender(ent, bones, bonenodes, isresetmode) surface.SetDrawColor(COLOR_WHITE:Unpack()) draw.NoTexture() - surface.DrawPoly(circ) + surface.DrawPoly(shape) local boneoptions = btype == 1 and FeaturesPhys or FeaturesNPhys local count = #boneoptions @@ -1451,7 +1483,7 @@ function AdvBoneSelectRadialRender(ent, bones, bonenodes, isresetmode) k = k + 1 end - + return {bone}, 1 + bone end end @@ -1460,6 +1492,19 @@ function AdvBoneSelectRadialPick() return SelectedBone end +do + local pi, sin = math.pi, math.sin + function AdvBoneSelectPulse(ent, bones, boneScales) + for _, bone in ipairs(bones) do + if not bone then continue end + + if boneScales[bone] then + ent:ManipulateBoneScale(bone, boneScales[bone] + VECTOR_ONE * 0.05 * sin(2.666 * pi * RealTime())) + end + end + end +end + function DrawBoneConnections(ent, bone) local mainpos = ent:GetBonePosition(bone) if not mainpos then diff --git a/lua/weapons/gmod_tool/stools/ragdollmover.lua b/lua/weapons/gmod_tool/stools/ragdollmover.lua index 21b9b98..59d0416 100644 --- a/lua/weapons/gmod_tool/stools/ragdollmover.lua +++ b/lua/weapons/gmod_tool/stools/ragdollmover.lua @@ -2527,6 +2527,42 @@ end) local GizmoWidth, SkeletonDraw +-- A singleton to track bone manipulate state, particularly scale. Useful if we want to use +-- the bone manipulate state to indicate something (such as hovering over a bone in advanced bone select) +local ClientBoneState = { + entity = NULL, + Scales = {}, + UpdateBoneScales = function(self) + for i = 0, self.entity:GetBoneCount() - 1 do + self.Scales[i] = self.entity:GetManipulateBoneScale(i) + end + end, + ResetBoneScales = function(self) + for i = 0, self.entity:GetBoneCount() - 1 do + self.Scales[i] = VECTOR_SCALEDEF + end + end, + SetBoneScales = function(self, scale) + for i = 0, self.entity:GetBoneCount() - 1 do + self.Scales[i] = scale + end + end, + SetBoneScale = function(self, bone, scale, recursive) + self.Scales[bone] = scale or self.entity:GetManipulateBoneScale(bone) + + local childBones = self.entity:GetChildBones(bone) + if recursive and #childBones > 0 then + for _, cbone in ipairs(childBones) do + self:SetBoneScale(cbone, scale, true) + end + end + end, + SetEntity = function(self, newEntity) + self.entity = newEntity + self:UpdateBoneScales() + end +} + do local ConVars = { @@ -3062,6 +3098,7 @@ end local function RGMResetAllBones() if not RAGDOLLMOVER[pl] or not RAGDOLLMOVER[pl].Entity then return end + ClientBoneState:ResetBoneScales() NetStarter.rgmResetAllBones() net.WriteEntity(RAGDOLLMOVER[pl].Entity) net.SendToServer() @@ -3409,6 +3446,7 @@ NetStarter = { local NodeFunctions = { function(ent, id) -- 1 nodeReset + ClientBoneState:SetBoneScale(id, VECTOR_SCALEDEF) NetStarter.rgmResetAll() net.WriteEntity(ent) net.WriteUInt(id, 10) @@ -3433,6 +3471,7 @@ local NodeFunctions = { end, function(ent, id) -- 4 nodeResetScale + ClientBoneState:SetBoneScale(id, VECTOR_SCALEDEF) NetStarter.rgmResetScale() net.WriteEntity(ent) net.WriteBool(false) @@ -3441,6 +3480,7 @@ local NodeFunctions = { end, function(ent, id) -- 5 nodeResetCh + ClientBoneState:SetBoneScale(id, VECTOR_SCALEDEF, true) NetStarter.rgmResetAll() net.WriteEntity(ent) net.WriteUInt(id, 10) @@ -3465,6 +3505,7 @@ local NodeFunctions = { end, function(ent, id) -- 8 nodeResetScaleCh + ClientBoneState:SetBoneScale(id, VECTOR_SCALEDEF, true) NetStarter.rgmResetScale() net.WriteEntity(ent) net.WriteBool(true) @@ -3473,6 +3514,7 @@ local NodeFunctions = { end, function(ent, id) -- 9 nodeScaleZero + ClientBoneState:SetBoneScale(id, vector_origin) NetStarter.rgmScaleZero() net.WriteEntity(ent) net.WriteBool(false) @@ -3481,6 +3523,7 @@ local NodeFunctions = { end, function(ent, id) -- 10 nodeScaleZeroCh + ClientBoneState:SetBoneScale(id, vector_origin, true) NetStarter.rgmScaleZero() net.WriteEntity(ent) net.WriteBool(true) @@ -4321,7 +4364,7 @@ end local function UpdateManipulationSliders(boneid, ent) if not IsValid(Pos1) then return end - local pos, rot, scale = ent:GetManipulateBonePosition(boneid), ent:GetManipulateBoneAngles(boneid), ent:GetManipulateBoneScale(boneid) + local pos, rot, scale = ent:GetManipulateBonePosition(boneid), ent:GetManipulateBoneAngles(boneid), ClientBoneState.Scales[boneid] or ent:GetManipulateBoneScale(boneid) rot:Normalize() ManipSliderUpdating = true @@ -4419,6 +4462,7 @@ local NETFUNC = { physchildren[i] = net.ReadEntity() end + ClientBoneState:SetEntity(selectedent) if IsValid(BonePanel) then RGMBuildBoneMenu(ents, selectedent, BonePanel) end @@ -4468,6 +4512,7 @@ local NETFUNC = { physchildren[i] = net.ReadEntity() end + ClientBoneState:SetEntity(ent) if IsValid(BonePanel) then RGMBuildBoneMenu(ents, ent, BonePanel) end @@ -4707,6 +4752,11 @@ function TOOL:Think() local nowpressed = input.IsMouseDown(MOUSE_LEFT) or input.IsMouseDown(MOUSE_RIGHT) local isright = input.IsMouseDown(MOUSE_RIGHT) + -- Track that we are moving a bone, and store its bone scale. + if plTable.Moving then + ClientBoneState:UpdateBoneScales() + end + if nowpressed and not LastPressed and op == 2 then -- left click is a predicted function, so leftclick wouldn't work in singleplayer since i need data from client local ent = plTable.Entity @@ -4893,6 +4943,7 @@ hook.Add("KeyPress", "rgmSwitchSelectionMode", function(pl, key) end) local BoneColors = {} +local BoneScaleGroup, LastId, LastOp = {}, 0, 0 local LastSelectThink, LastEnt = 0, nil function TOOL:DrawHUD() @@ -4952,16 +5003,17 @@ function TOOL:DrawHUD() rgm.DrawSkeleton(ent, nodes) end + local id = 0 if self:GetOperation() == 2 and IsValid(ent) then local timecheck = (thinktime - LastSelectThink) > 0.1 local calc = ( not LastEnt or LastEnt ~= ent ) or timecheck if self:GetStage() == 0 then - BoneColors = rgm.AdvBoneSelectRender(ent, nodes, BoneColors, calc, eyepos, viewvec, fov) + BoneColors, BoneScaleGroup, id = rgm.AdvBoneSelectRender(ent, nodes, BoneColors, calc, eyepos, viewvec, fov) else - rgm.AdvBoneSelectRadialRender(ent, plTable.SelectedBones, nodes, ResetMode) + BoneScaleGroup, id = rgm.AdvBoneSelectRadialRender(ent, plTable.SelectedBones, nodes, ResetMode) end - + LastEnt = ent if timecheck then LastSelectThink = thinktime @@ -4978,6 +5030,40 @@ function TOOL:DrawHUD() rgm.DrawEntName(HoveredEnt) end + -- Advanced Bone Select visual indicators + local opsDifferent = LastOp ~= self:GetOperation() + if IsValid(ent) then + -- We need to track the original manipulatebonescale (scales for short) while we also use the ManipulateBoneScale function + + if opsDifferent then + if self:GetOperation() == 2 then + -- If we began advanced bone select, store the original scales + ClientBoneState:UpdateBoneScales() + else + -- If we just left it, restore the original scales + for i = 0, ent:GetBoneCount() - 1 do + ent:ManipulateBoneScale(i, ClientBoneState.Scales[i]) + end + end + end + + if self:GetOperation() == 2 then + -- If we're hovering over a different bone + if LastId ~= id then + -- Reset the bone to the original scale. This prevents us from adding or subtracting scales from + -- previous iterations of using advanced bone select + for i = 0, ent:GetBoneCount() - 1 do + ent:ManipulateBoneScale(i, ClientBoneState.Scales[i]) + end + end + + -- Show selected bone, regardless if any are selected + rgm.AdvBoneSelectPulse(ent, BoneScaleGroup, ClientBoneState.Scales) + end + + LastId = id + end + LastOp = self:GetOperation() end end