Skip to content

Expose SkinnedMeshRenderer bone API to Lua (read/write local transforms)#707

Merged
adriengivry merged 5 commits intoOverload-Technologies:mainfrom
Gopmyc:677
Apr 16, 2026
Merged

Expose SkinnedMeshRenderer bone API to Lua (read/write local transforms)#707
adriengivry merged 5 commits intoOverload-Technologies:mainfrom
Gopmyc:677

Conversation

@Gopmyc
Copy link
Copy Markdown
Contributor

@Gopmyc Gopmyc commented Apr 15, 2026

Description

This PR exposes skeleton/bone runtime controls for SkinnedMeshRenderer in Lua.

What was added:

  • Bone discovery API:
    • GetBoneCount()
    • GetBoneName(index)
    • GetBoneIndex(name)
  • Bone local transform accessors:
    • GetBoneLocalPosition(index)
    • GetBoneLocalRotation(index)
    • GetBoneLocalScale(index)
  • Bone local transform mutators:
    • SetBoneLocalPosition(index, position)
    • SetBoneLocalRotation(index, rotation)
    • SetBoneLocalScale(index, scale)

Implementation details:

  • Added minimal, localized API in CSkinnedMeshRenderer.
  • Added safe validation for invalid indices/model state.
  • Added pose recomputation path after manual bone transform changes.
  • Exposed methods through Lua bindings.
  • Updated Lua component API docs/stubs accordingly.

Related Issue(s)

Fixes #677

Review Guidance

  • Use a skinned actor with a SkinnedMeshRenderer.
  • In a behaviour script:
    • Resolve a bone index via GetBoneIndex("Head") (or another bone).
    • Read local TRS with GetBoneLocal*.
    • Apply runtime updates with SetBoneLocal*.
  • Confirm:
    • Valid index updates the pose at runtime.
    • Invalid index/name safely returns nil/false.
    • Existing animation controls (Play, SetAnimation, etc.) keep working.

Screenshots/GIFs

B.mp4

Checklist

  • My code follows the project's code style guidelines
  • I have commented my code, particularly in hard-to-understand areas
  • I have updated the documentation accordingly
  • My changes don't generate new warnings or errors

@Gopmyc
Copy link
Copy Markdown
Contributor Author

Gopmyc commented Apr 15, 2026

Here is the Lua script I used locally for testing :

---@class BoneApiTest : Behaviour
local BoneApiTest =
{
	targetBoneName = "mixamorig:Head",
	speed = 2.0,
	positionAmplitude = 0.03,
	rotationAmplitude = 20.0,
	scaleAmplitude = 0.05,
	logBonesOnStart = false,

	_renderer = nil,
	_boneIndex = nil,
	_basePos = nil,
	_baseRot = nil,
	_baseScale = nil,
	_t = 0.0
}

function BoneApiTest:OnStart()
	self._renderer = self.owner:GetSkinnedMeshRenderer()
	if not self._renderer then
		Debug.LogError("[BoneApiTest] SkinnedMeshRenderer introuvable")
		return
	end

	local boneCount = self._renderer:GetBoneCount()
	Debug.LogInfo("[BoneApiTest] Bone count = " .. tostring(boneCount))

	if self.logBonesOnStart then
		for i = 0, boneCount - 1 do
			Debug.LogInfo("[BoneApiTest] Bone[" .. i .. "] = " .. tostring(self._renderer:GetBoneName(i)))
		end
	end

	self._boneIndex = self._renderer:GetBoneIndex(self.targetBoneName)

	if not self._boneIndex then
		if boneCount > 0 then
			self._boneIndex = 0
			Debug.LogWarning("[BoneApiTest] Bone '" .. self.targetBoneName .. "' introuvable, fallback sur l'index 0")
		else
			Debug.LogError("[BoneApiTest] Aucun bone disponible")
			return
		end
	end

	self._basePos = self._renderer:GetBoneLocalPosition(self._boneIndex)
	self._baseRot = self._renderer:GetBoneLocalRotation(self._boneIndex)
	self._baseScale = self._renderer:GetBoneLocalScale(self._boneIndex)

	if not self._basePos or not self._baseRot or not self._baseScale then
		Debug.LogError("[BoneApiTest] Impossible de lire le transform local du bone")
		self._boneIndex = nil
	end
end

function BoneApiTest:OnUpdate(deltaTime)
	if not self._renderer or self._boneIndex == nil then
		return
	end

	self._t = self._t + deltaTime * self.speed
	local wave = math.sin(self._t)

	local pos = Vector3.new(
		self._basePos.x,
		self._basePos.y + wave * self.positionAmplitude,
		self._basePos.z
	)
	self._renderer:SetBoneLocalPosition(self._boneIndex, pos)

	local scaleFactor = 1.0 + wave * self.scaleAmplitude
	local scl = Vector3.new(
		self._baseScale.x * scaleFactor,
		self._baseScale.y * scaleFactor,
		self._baseScale.z * scaleFactor
	)
	self._renderer:SetBoneLocalScale(self._boneIndex, scl)

	local rotOffset = Quaternion.new(Vector3.new(0.0, wave * self.rotationAmplitude, 0.0))
	self._renderer:SetBoneLocalRotation(self._boneIndex, self._baseRot * rotOffset)
end

function BoneApiTest:OnDestroy()
	if not self._renderer or self._boneIndex == nil then
		return
	end

	self._renderer:SetBoneLocalPosition(self._boneIndex, self._basePos)
	self._renderer:SetBoneLocalRotation(self._boneIndex, self._baseRot)
	self._renderer:SetBoneLocalScale(self._boneIndex, self._baseScale)
end

return BoneApiTest

Copy link
Copy Markdown
Member

@adriengivry adriengivry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for making this PR!

Looks good to me!

The only feedback I would have is that when the character is in T-pose (no animation clip selected), it's not possible to modify bones (the new pose won't be shown).

@Gopmyc
Copy link
Copy Markdown
Contributor Author

Gopmyc commented Apr 16, 2026

Thanks for making this PR!

Looks good to me!

The only feedback I would have is that when the character is in T-pose (no animation clip selected), it's not possible to modify bones (the new pose won't be shown).

Done !

Comment thread Sources/OvCore/src/OvCore/ECS/Components/CSkinnedMeshRenderer.cpp Outdated
@Gopmyc Gopmyc requested a review from adriengivry April 16, 2026 16:35
Copy link
Copy Markdown
Member

@adriengivry adriengivry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@adriengivry adriengivry added the Graphics Graphical feature label Apr 16, 2026
@adriengivry adriengivry merged commit a80b453 into Overload-Technologies:main Apr 16, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Graphics Graphical feature

Development

Successfully merging this pull request may close these issues.

Expose Skeleton/Bone API to Lua for SkinnedMeshRenderer

2 participants