{"id":23401181,"url":"https://github.com/upf-gti/retargeting-threejs","last_synced_at":"2025-04-11T19:12:18.308Z","repository":{"id":247998237,"uuid":"827465054","full_name":"upf-gti/retargeting-threejs","owner":"upf-gti","description":"Animation and pose retargeting solver for 3D humanoid characters using Threejs.","archived":false,"fork":false,"pushed_at":"2025-04-09T10:18:19.000Z","size":924,"stargazers_count":20,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-09T11:26:16.506Z","etag":null,"topics":["3d","animation","retargeting","retargeting-animations","retargeting-motion","threejs","threejs-example"],"latest_commit_sha":null,"homepage":"https://resources.gti.upf.edu/demos/retargeting-threejs/demo/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/upf-gti.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-07-11T17:39:58.000Z","updated_at":"2025-04-09T10:18:22.000Z","dependencies_parsed_at":"2025-04-09T11:33:55.368Z","dependency_job_id":null,"html_url":"https://github.com/upf-gti/retargeting-threejs","commit_stats":null,"previous_names":["upf-gti/retargeting-threejs"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upf-gti%2Fretargeting-threejs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upf-gti%2Fretargeting-threejs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upf-gti%2Fretargeting-threejs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upf-gti%2Fretargeting-threejs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/upf-gti","download_url":"https://codeload.github.com/upf-gti/retargeting-threejs/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248465346,"owners_count":21108244,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["3d","animation","retargeting","retargeting-animations","retargeting-motion","threejs","threejs-example"],"created_at":"2024-12-22T11:14:42.284Z","updated_at":"2025-04-11T19:12:18.301Z","avatar_url":"https://github.com/upf-gti.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# retargeting-threejs\n\nAnimation and pose retargeting solver for 3D humanoid characters using Threejs.\n\nSee [this](docs/Algorithm.md) documentation for the algorithm explanation and some animation related topics.\n\n[[Try demo](https://resources.gti.upf.edu/demos/retargeting-threejs/demo/)]\n\n## Set up\nTo use the code, include the \u003cb\u003eretargeting.js\u003c/b\u003e file in your project and include the following lines in your \u003cb\u003eindex.html\u003c/b\u003e. The \u003cb\u003eretargeting.js\u003c/b\u003e expects an importmap for \"three\":\n``` html\n\u003cscript async src=\"https://ga.jspm.io/npm:es-module-shims@1.10.0/dist/es-module-shims.js\"\u003e\u003c/script\u003e\n\u003cscript type=\"importmap\"\u003e\n    {\n            \"imports\": {\n                \"three\": \"https://cdn.jsdelivr.net/npm/three@0.166.1/build/three.module.js\"\n            }\n    }\n\u003c/script\u003e\n```\n## API\n\n``` javascript\n// example offline retarget\n\nlet source = loadSourceSkeleton(); // user function: returns THREE.Skeleton\nlet sourceAnim = loadSourceAnimation(); // user function: returns THREE.AnimationClip\n\nlet target = loadSkinnedModel(); // user function: returns some skinned model from a glb (for example)\n\nlet options = {\n    srcPoseMode: AnimationRetargeting.BindPoseModes.DEFAULT, // will use the actual skeleton's bind pose\n    trgPoseMode: AnimationRetargeting.BindPoseModes.CURRENT, // will use the current local transforms of the bones as bind pose\n    trgEmbedWorldTransforms: true // the rotations of the parent(s) of the skeleton will be included in the retargeting. They are needed to make the source and target skeletons match, for this example.\n}\nlet retargeting = new AnimationRetargeting( source, target, options );\nretargeting.retargetAnimation( sourceAnim );\n\n```\n\n\n###  Constructor\n\nRetargets animations and/or current poses from one skeleton to another. \nBoth skeletons must have the same bind pose (same orientation for each mapped bone) in order to properly work.\nUse optional parameters to adjust the bind pose.\n\n``` javascript\nAnimationRetargeting( srcSkeleton, trgSkeleton, options )\n```\n- `srcSkeleton` THREE.Skeleton \u003cbr\u003e Skeleton of the source avatar. If not an skeleton, an object traverse will be perfomed to find one (from a skinnedMesh for example).\n- `trgSkeleton` THREE.Skeleton \u003cbr\u003e Same as srcSkeleton but for the target avatar\n- `options` Object \u003cbr\u003e optional attribute to modify the retargeting behaviour\n    \n    - `srcPoseMode` and `trgPoseMode`: AnimationRetargeting.BindPoseModes \u003cbr\u003e Pose of the srcSkeleton that will be used as the bind pose for the retargeting. Default: skeleton's actual bind pose.\n\n    - `srcEmbedWorldTransforms` and `trgEmbedWorldTransforms`: Bool \u003cbr\u003e Retargeting only takes into account the  transforms from the actual bone objects (local transforms). If set to true, external (parent) transforms are computed and embedded into the root joint (only once, on construction). \nAfterwards, parent transforms/matrices can be safely modified and will not affect in retargeting.\nUseful when it is easier to modify the container of the skeleton rather than the actual skeleton in order to align source and target poses.\n    - `boneNameMap`: Object. \u003cbr\u003e String-to-string mapping between src (keys) and trg (values) through bone names. Only supports one-to-one mapping. If no mapping is specified, an automatic one is performed based on the name of the bones.\n\n  \n\n### Static Properties\n\n#### BindPoseModes\nEnumeration that determines which pose will be used as the retargeting bind pose.\n\n- `DEFAULT` or `0`: Uses skeleton's actual bind pose\n- `CURRENT` or `1`: Uses skeleton's current pose\n\n### Methods \n\n#### retargetPose\nRetargets the current pose from the source skeleton to the target skeleton. Only the mapped bones are computed.\n\n``` javascript\n.retargetPose() : undefined\n```\n\n#### retargetAnimation\nRetargets a THREEJS AnimationClip from source to target skeleton. Returns another AnimationClip.\nOnly mapped bones are computed\n\n``` javascript\n.retargetAnimation( anim ) : THREE.AnimationClip\n```\n- `anim`: THREE.AnimationClip \u003cbr\u003e animation to retarget\n\n\u003e [!CAUTION]\n\u003e *Work in progress*\n\u003e #### applyTPose\n\u003e Force the skeleton to have a T-pose shape, facing the +Z axis. Only works for humanoid skeletons.\n\u003e``` javascript\n\u003e.applyTpose( skeleton, map ) : THREE.AnimationClip\n\u003e```\n\n## Usual issues \n\nA failed retargeting might be due to many reasons.\n\nThe lack of movement might be caused by an improper bone mapping or bad track IDs.\n\nWeird target rotations might also be due to improper bone mapping. However, most commonly, it will be caused by incorrect set up of the auxiliary pose. The API exposes some attributes to alleviate this\n\n\n#### :heavy_check_mark: Case A: Successful Retargeting\n\u003cdiv style=\"display:flex; flex-wrap: wrap; width='100%' \"\u003e\n    \u003cimage src=\"docs/imgs/GoodPose.png\" alt=\"Good skeleton bind pose\" width=\"50%\"\u003e\u003c/image\u003e\n    \u003cimage src=\"docs/imgs/GoodRetarget.png\" alt=\"Good skeleton bind pose\" width=\"50%\"\u003e\u003c/image\u003e \n\u003c/div\u003e\n\nCase A shows a successful retargeting from the avatar on the left (red shirt) to the avatar on the right(white shirt). Note the white avatar only moves one finger as the source avatar only has one finger. It could have been manually mapped instead of relying on the automap.\n\n#### :warning: Case B: Current pose modification\n\n\u003cdiv style=\"display:flex; flex-wrap: wrap; width='100%' \"\u003e\n    \u003cimage src=\"docs/imgs/BadCurrentPose.png\" alt=\"Bad skeleton bind Pose that requires modifying a joint\" width=\"50%\"\u003e\u003c/image\u003e\n    \u003cimage src=\"docs/imgs/BadCurrentPoseRetarget.png\" alt=\"Bad skeleton bind Pose that requires modifying a joint\" width=\"50%\"\u003e\u003c/image\u003e \n\u003c/div\u003e\n\nCase B shows an example where both skeleton's auxiliary pose are different. In this particular case, only the root joint (hips) differs. Since the world rotations of the source avatar do not mean the same for the target avatar, the resulting animation look weird. For this case, it would suffice to rotate 90º the root joint and instantiating the AnimationRetargeting class with the `trgPoseMode` set to `CURRENT`, so it checks for the current modified target bone setup.\n\n#### :warning: Case C: World transform embeding\n\n\u003cdiv style=\"display:flex; flex-wrap: wrap; width='100%' \"\u003e\n    \u003cimage src=\"docs/imgs/BadEmbedPose.png\" width=\"50%\" alt=\"Bad skeleton's bind pose that requires modifying the container's transform\"\u003e\u003c/image\u003e\n    \u003cimage src=\"docs/imgs/BadEmbedRetarget.png\" width=\"50%\" alt=\"Bad skeleton's bind pose that requires modifying the container's transform\"\u003e\u003c/image\u003e\n\u003c/div\u003e\n\n\nCase C shows a similar example as in case B. However, for this case it might be easier to just modify the container of the skeleton (or some upper container). Rotating the skeleton's parent object 90º results in the same pose as Case A. Then it would only suffice to instantiate AnimationRetargeting with `trgEmbedWorldTransforms` set to ```true```, so the algorithm takes the container's rotation into account.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fupf-gti%2Fretargeting-threejs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fupf-gti%2Fretargeting-threejs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fupf-gti%2Fretargeting-threejs/lists"}