Add visual indicator of advanced selected bones (#15)

* Add visual indicator of advanced selected bones
+ Add bone scaling indicator when the user hovers over a bone using advanced bone select. Bones also scale when selecting from a set of bones, or when looking through the bone options
* Rename SelectedBones -> BoneScaleGroup; revert scale amplitude
* Synchronize bone scale with UI
- Change `CurrentBoneScales` into a `ClientBoneState` singleton which stores scales and the selected entity. We call methods on this singleton to ensure that the visual bone scaling indicator works as intended, while also ensuring that the UI stays in sync with that state
This commit is contained in:
vlazed 2025-02-19 11:24:14 -05:00 committed by GitHub
parent 4722de62f9
commit 6134e33504
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 111 additions and 6 deletions

View File

@ -1236,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)
@ -1256,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)
@ -1391,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
@ -1477,7 +1483,7 @@ function AdvBoneSelectRadialRender(ent, bones, bonenodes, isresetmode)
k = k + 1
end
return {bone}, 1 + bone
end
end
@ -1486,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

View File

@ -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,14 +5003,15 @@ 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
@ -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