1"use strict";(self.webpackChunkuthana_api_docs=self.webpackChunkuthana_api_docs||[]).push([["2942"],{329(e,t,n){n.r(t),n.d(t,{metadata:()=>a,default:()=>p,frontMatter:()=>s,contentTitle:()=>c,toc:()=>h,assets:()=>d});var a=JSON.parse('{"id":"capabilities/retargeting","title":"Retargeting","description":"Apply motions generated with Uthana to your custom 3D characters. Retargeting automatically adapts animations to work with different character skeletons and proportions.","source":"@site/docs/capabilities/retargeting.mdx","sourceDirName":"capabilities","slug":"/capabilities/retargeting","permalink":"/docs/api/capabilities/retargeting","draft":false,"unlisted":false,"tags":[],"version":"current","sidebarPosition":8,"frontMatter":{"id":"retargeting","title":"Retargeting","sidebar_position":8},"sidebar":"docs","previous":{"title":"Unitree G1","permalink":"/docs/api/capabilities/unitree-g1"},"next":{"title":"Download a character","permalink":"/docs/api/capabilities/downloading-character"}}'),o=n(4848),i=n(8453),r=n(7331),l=n(7250);let s={id:"retargeting",title:"Retargeting",sidebar_position:8},c="Retarget to any character",d={},h=[{value:"Overview",id:"overview",level:2},{value:"How retargeting works",id:"how-retargeting-works",level:2},{value:"Step-by-step tutorial",id:"step-by-step-tutorial",level:2},{value:"Step 1: Upload your character",id:"step-1-upload-your-character",level:3},{value:"Step 2: Generate a motion",id:"step-2-generate-a-motion",level:3},{value:"Step 3: Download retargeted motion",id:"step-3-download-retargeted-motion",level:3},{value:"Downloading motion-only files",id:"downloading-motion-only-files",level:2},{value:"Next steps",id:"next-steps",level:2}];function m(e){let t={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,i.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(t.header,{children:(0,o.jsx)(t.h1,{id:"retarget-to-any-character",children:"Retarget to any character"})}
1),"\n",(0,o.jsx)(t.p,{children:"Apply motions generated with Uthana to your custom 3D characters. Retargeting automatically adapts animations to work with different character skeletons and proportions."}),"\n",(0,o.jsx)(t.h2,{id:"overview",children:"Overview"}),"\n",(0,o.jsx)(t.p,{children:"When you generate a motion with Uthana, it's created for a specific character skeleton. Retargeting allows you to apply that same motion to any character you've uploaded, automatically adapting the animation to match different bone structures and proportions."}),"\n",(0,o.jsxs)(t.p,{children:["Learn more about ",(0,o.jsx)(t.a,{href:"https://uthana.com/embodiment/retargeting",children:"Retargeting"}),"."]}),"\n",(0,o.jsx)(t.h2,{id:"how-retargeting-works",children:"How retargeting works"}),"\n",(0,o.jsxs)(t.ol,{children:["\n",(0,o.jsxs)(t.li,{children:[(0,o.jsx)(t.strong,{children:"Generate or select a motion"}),": Create a motion using text-to-motion, locomotion, or video-to-motion, or use an existing motion"]}),"\n",(0,o.jsxs)(t.li,{children:[(0,o.jsx)(t.strong,{children:"Choose your character"}),": Select the character you want to apply the motion to"]}),"\n",(0,o.jsxs)(t.li,{children:[(0,o.jsx)(t.strong,{children:"Download the retargeted motion"}),": The motion is automatically retargeted when you download it with a specific character ID"]}),"\n"]}),"\n",(0,o.jsx)(t.h2,{id:"step-by-step-tutorial",children:"Step-by-step tutorial"}),"\n",(0,o.jsx)(t.h3,{id:"step-1-upload-your-character",children:"Step 1: Upload your character"}),"\n",(0,o.jsx)(t.p,{children:"First, upload your custom character model. Uthana will automatically rig it if needed."}),"\n",(0,o.jsxs)(t.p,{children:["See the ",(0,o.jsx)(t.a,{href:"/capabilities/auto-rig-and-add-character",children:"Auto-rig / add a character"})," guide for detailed instructions on uploading characters, including supported formats, auto-rigging options, and code examples for all languages."]}),"\n",(0,o.jsx)(t.p,{children:"After uploading, save the character ID from the response. You'll need it for retargeting motions."}),"\n",(0,o.jsx)(t.h3,{id:"step-2-generate-a-motion",children:"Step 2: Generate a motion"}),"\n",(0,o.jsxs)(t.p,{children:["Generate a motion using ",(0,o.jsx)(t.a,{href:"/capabilities/text-to-motion",children:"Text to motion"}),", ",(0,o.jsx)(t.a,{href:"/capabilities/locomotion",children:"Locomotion"}),", or ",(0,o.jsx)(t.a,{href:"/capabilities/video-to-motion",children:"Video to motion"}),". Save the motion ID from the response\u2014you'll need it for downloading the retargeted motion."]}),"\n",(0,o.jsx)(t.h3,{id:"step-3-download-retargeted-motion",children:"Step 3: Download retargeted motion"}),"\n",(0,o.jsx)(t.p,{children:"When you download a motion with a specific character ID, Uthana automatically retargets it to that character's skeleton."}),"\n",(0,o.jsxs)(r.A,{groupId:"language",children:[(0,o.jsx)(l.A,{value:"curl",label:"Shell",children:(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-bash",children:'# Download retargeted motion as FBX (filename is customizable)\ncurl -L "https://uthana.com/motion/file/motion_viewer/$CHARACTER_ID/$MOTION_ID/fbx/motion.fbx" \\\n -u $API_KEY: \\\n -o retargeted-motion.fbx\n\n# Download retargeted motion as GLB (filename is customizable)\ncurl -L "https://uthana.com/motion/file/motion_viewer/$CHARACTER_ID/$MOTION_ID/glb/motion.glb" \\\n -u $API_KEY: \\\n -o retargeted-motion.glb\n'})})}),(0,o.jsx)(l.A,{value:"python",label:"Python",children:(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-python",children:'import asyncio\nfrom uthana import Uthana\n\nclient = Uthana("{{apiKey}}")\n\nasync def main():\n # Download retargeted motion as FBX for a specific character\n fbx_data = await client.motions.download(CHARACTER_ID, MOTION_ID, output_format="fbx")\n with open("retargeted-motion.fbx", "wb") as f:\n f.write(fbx_data)\n\n # Download retargeted motion as GLB\n glb_data = await client.motions.download(CHARACTER_ID, MOTION_ID, output_format="glb")\n with open("retargeted-motion.glb", "wb") as f:\n f.write(glb_data)\n\nasyncio.run(main())\n'})})}),(0,o.jsx)(l.A,{value:"typescript",label:"TypeScript",children:(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-typescript",children:'import { UthanaClient } from "@uthana/client";\n\nconst client = new UthanaClient(process.env.UTHANA_API_KEY!);\n\n// Download retargeted motion as FBX for a specific character\nconst fbxBuffer = await client.motions.download(CHARACTER_ID, MOTION_ID, { output_format: "fbx" });\n\n// Download retargeted motion as GLB\nconst glbBuffer = await client.motions.download(CHARACTER_ID, MOTION_ID, { output_format: "glb" });\n'})})}),(0,o.jsx)(l.A,{value:"react",label:"React",children:(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-tsx",children:'import { useUthanaClient } from "@uthana/react";\n\nfunction DownloadRetargetedMotion({\n characterId,\n motionId,\n}: {\n characterId: string;\n motionId: string;\n}) {\n const client = useUthanaClient();\n\n async function download(format: "glb" | "fbx") {\n const buffer = await client.motions.download(characterId, motionId, { output_format: format });\n const mime = format === "glb" ? "model/gltf-binary" : "application/octet-stream";\n const url = URL.createObjectURL(new Blob([buffer], { type: mime }));\n const a = document.createElement("a");\n a.href = url;\n a.download = `retargeted-motion.${format}`;\n a.click();\n }\n\n return (\n <div>\n <button onClick={() => download("fbx")}>Download FBX</button>\n <button onClick={() => download("glb")}>Download GLB</button>\n </div>\n );\n}\n'})})}),(0,o.jsx)(l.A,{value:"csharp",label:"C#",children:(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-csharp",children:'public async Task<byte[]> DownloadRetargetedMotionAsync(string characterId, string motionId, string format = "fbx", string filename = "motion")\n{\n var downloadUrl = $"https://uthana.com/motion/file/motion_viewer/{characterId}/{motionId}/{format}/{filename}.{format}";\n\n var response = await _httpClient.GetAsync(downloadUrl);\n response.EnsureSuccessStatusCode();\n\n return await response.Content.ReadAsByteArrayAsync();\n}\n\n// Usage\nvar fbxData = await DownloadRetargetedMotionAsync(characterId, motionId, "fbx");\nvar glbData = await DownloadRetargetedMotionAsync(characterId, motionId, "glb");\n'})})})]}),"\n",(0,o.jsx)(t.h2,{id:"downloading-motion-only-files",children:"Downloading motion-only files"}),"\n",(0,o.jsx)(t.p,{children:"For animation data without the character mesh (useful for applying to characters in your game engine), download the motion-only GLB:"}),"\n",(0,o.jsxs)(r.A,{groupId:"language",children:[(0,o.jsx)(l.A,{value:"curl",label:"Shell",children:(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-bash",children:'# Download motion-only GLB (animation data without character mesh, filename is customizable)\ncurl -L "https://uthana.com/motion/animation/motion_viewer/$CHARACTER_ID/$MOTION_ID/glb/motion.glb" \\\n -u $API_KEY: \\\n -o motion-only.glb\n'})})}),(0,o.jsx)(l.A,{value:"python",label:"Python",children:(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-python",children:'async def main():\n data = await client.motions.download(CHARACTER_ID, MOTION_ID, output_format="glb", no_mesh=True)\n with open("motion-only.glb", "wb") as f:\n f.write(data)\n\nasyncio.run(main())\n'})})}),(0,o.jsx)(l.A,{value:"typescript",label:"TypeScript",children:(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-typescript",children:'const buffer = await client.motions.download(CHARACTER_ID, MOTION_ID, {\n output_format: "glb",\n no_mesh: true,\n});\n'})})}),(0,o.jsx)(l.A,{value:"react",label:"React",children:(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-tsx",children:'const buffer = await client.motions.download(characterId, motionId, {\n output_format: "glb",\n no_mesh: true,\n});\n'})})}),(0,o.jsx)(l.A,{value:"csharp",label:"C#",children:(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-csharp",children:'public async Task<byte[]> DownloadMotionOnlyGlbAsync(string characterId, string motionId, string filename = "motion")\n{\n var downloadUrl = $"https://uthana.com/motion/animation/motion_viewer/{characterId}/{motionId}/glb/{filename}.glb";\n\n var response = await _httpClient.GetAsync(downloadUrl);\n response.EnsureSuccessStatusCode();\n\n return await response.Content.ReadAsByteArrayAsync();\n}\n\n// Usage\nvar motionOnlyData = await DownloadMotionOnlyGlbAsync(characterId, motionId);\n'})})})]}),"\n",(0,o.jsx)(t.h2,{id:"next-steps",children:"Next steps"}),"\n",(0,o.jsxs)(t.ul,{children:["\n",(0,o.jsxs)(t.li,{children:["Learn about ",(0,o.jsx)(t.a,{href:"/capabilities/text-to-motion",children:"Text to motion"})," for generating animations"]}),"\n",(0,o.jsxs)(t.li,{children:["Learn about ",(0,o.jsx)(t.a,{href:"/capabilities/locomotion",children:"Locomotion"})," for directional, looptable locomotion clips"]}),"\n",(0,o.jsxs)(t.li,{children:["Explore ",(0,o.jsx)(t.a,{href:"/capabilities/asset-management",children:"Asset management"})," for managing your characters and motions"]}),"\n",(0,o.jsxs)(t.li,{children:["Check the ",(0,o.jsx)(t.a,{href:"/graphql",children:"API reference"})," for complete schema documentation"]}),"\n"]})]})}function p(e={}){let{wrapper:t}={...(0,i.R)(),...e.components};return t?(0,o.jsx)(t,{...e,children:(0,o.jsx)(m,{...e})}):m(e)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.