Skip to content
Draft
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
5e25b5f
Add optional component displayed at the right side of the pane header
Popov72 Mar 9, 2026
3136762
Add support for the disabled attribute to fluent check box
Popov72 Mar 9, 2026
0f9fdbb
Create an extension for animation retargeting
Popov72 Mar 9, 2026
5f6a6cb
Merge branch 'master' of https://github.com/BabylonJS/Babylon.js into…
Popov72 Mar 16, 2026
680c5c9
More changes
Popov72 Mar 18, 2026
0072998
Don't crash when trying to store a string too big for the local storage
Popov72 Mar 18, 2026
53db1b1
More changes
Popov72 Mar 18, 2026
8df07f4
More changes
Popov72 Mar 18, 2026
e49e2d3
Fix lint problems
Popov72 Mar 18, 2026
0919205
Remove unused parameters
Popov72 Mar 19, 2026
4dccce0
More changes (4)
Popov72 Mar 19, 2026
87f8498
Allow to pass a react component for the side panel title
Popov72 Mar 19, 2026
3975e6a
Remove Dude
Popov72 Mar 19, 2026
11e1410
Address comments
Popov72 Mar 19, 2026
b8a2387
More fixes
Popov72 Mar 19, 2026
933ae33
Fix documentation link
Popov72 Mar 19, 2026
c8cd98e
Clean up ShellService changes
ryantrem Mar 19, 2026
9314750
Address comments related to visuals
Popov72 Mar 20, 2026
f64537a
Merge branch 'inspectorv2-art-extension' of https://github.com/Popov7…
Popov72 Mar 20, 2026
f828eb4
Use shared components
Popov72 Mar 20, 2026
49a8a48
Use ISettingStore instead of direct local storage access
Popov72 Mar 20, 2026
d81d541
Remove changes to ShellService
Popov72 Mar 20, 2026
0a8fd56
Use DataGrid
Popov72 Mar 20, 2026
28d969d
Use engine from current scene, don't create a new instance
Popov72 Mar 20, 2026
c414193
feat(inspector): add 'Import from current scene' for animation retarg…
Popov72 Mar 20, 2026
cea17d0
Merge branch 'master' of https://github.com/BabylonJS/Babylon.js into…
Popov72 Mar 21, 2026
b6a2a6e
Add support for avatars and animations created from scene, as well as…
Popov72 Mar 24, 2026
ef7e7c3
Add bridge between inspector and PG
Popov72 Mar 24, 2026
2c2dfb6
Scale the animation skeleton to fit the screen
Popov72 Mar 24, 2026
fb87590
Fix ts
Popov72 Mar 24, 2026
7720b27
Merge branch 'master' of https://github.com/BabylonJS/Babylon.js into…
Popov72 Mar 31, 2026
69ee458
Update code doc
Popov72 Mar 31, 2026
ef25276
Animation retargeting: scene import, export to scene, lint fixes, aut…
Popov72 Mar 31, 2026
9585646
Fix import
Popov72 Mar 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,12 @@ export const DefaultInspectorExtensionFeed = new BuiltInsExtensionFeed("Inspecto
author: { name: "Babylon.js", forumUserName: "" },
getExtensionModuleAsync: async () => await import("../services/panes/tools/reflectorService"),
},
{
name: "Animation Retargeting",
description: "Retarget animations from one skeleton to another using AnimatorAvatar. Includes a dedicated 3D viewport with dual-camera preview.",
keywords: ["animation", "retargeting", "skeleton", "avatar", "bones"],
...BabylonWebResources,
author: { name: "Babylon.js", forumUserName: "" },
getExtensionModuleAsync: async () => await import("../extensions/animationRetargeting/animationRetargetingExtension"),
},
]);

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import type { IDisposable } from "core/index";
import type { ServiceDefinition } from "../../modularity/serviceDefinition";
import type { IShellService } from "../../services/shellService";
import { ShellServiceIdentity } from "../../services/shellService";

import { Observable } from "core/Misc/observable";
import { PersonRunning20Regular } from "@fluentui/react-icons";
import { Body1 } from "@fluentui/react-components";

import { AnimationRetargetingViewport } from "./animationRetargetingViewport";
import { AnimationRetargetingPanel, DefaultPanelState } from "./animationRetargetingPanel";
import type { PanelStateStore } from "./animationRetargetingPanel";
import type { RetargetingSceneManager } from "./retargetingSceneManager";
import { NamingSchemeManager } from "./namingSchemeManager";
import { AvatarManager } from "./avatarManager";
import { AnimationManager } from "./animationManager";
import { Link } from "shared-ui-components/fluent/primitives/link";

/**
* Service definition for the Animation Retargeting extension.
* - Registers a dedicated 3D viewport (central content, left+right cameras).
* - Registers a controls side pane with Avatar / Animation / Retarget sections.
* - Exposes an Enable / Disable toggle to restore the original inspector scene.
*/
export const AnimationRetargetingServiceDefinition: ServiceDefinition<[], [IShellService]> = {
friendlyName: "Animation Retargeting",
consumes: [ShellServiceIdentity],
factory: (shellService) => {
// Observable that fires whenever a new scene manager is ready (on each enable)
const onManagerReadyObs = new Observable<RetargetingSceneManager>();
// Observable that broadcasts enable/disable state changes to the panel
const isEnabledObs = new Observable<boolean>();
// Observable that fires when the config dialog closes β€” panel refreshes dropdowns
const onConfigChangedObs = new Observable<void>();

// Naming scheme manager β€” persists across extension lifetime via localStorage
const namingSchemeManager = new NamingSchemeManager();

// Avatar manager β€” persists across extension lifetime via localStorage + IndexedDB
const avatarManager = new AvatarManager();

// Animation manager β€” persists across extension lifetime via localStorage + IndexedDB
const animationManager = new AnimationManager();

// Create default entries if both lists are empty (first-time use)
if (avatarManager.getAllAvatars().length === 0 && animationManager.getAllDisplayNames().length === 0) {
avatarManager.createDefaults();
animationManager.createDefaults();
}

let isEnabled = false;
let viewportReg: IDisposable | null = null;
let currentManager: RetargetingSceneManager | null = null;
// Persists all panel UI state across remounts (e.g. when the panel is docked elsewhere)
const allAvatars = avatarManager.getAllAvatars();
const allDisplayNames = animationManager.getAllDisplayNames();
const persistedPanelState: PanelStateStore = {
...DefaultPanelState,
avatarName: allAvatars.length > 0 ? allAvatars[0].name : "",
animationName: allDisplayNames.length > 0 ? allDisplayNames[0] : "",
};

function setEnabled(enabled: boolean): void {
if (enabled === isEnabled) {
return;
}
isEnabled = enabled;

if (enabled) {
// Register the central-content viewport; the component creates the Engine+Scene
viewportReg = shellService.addCentralContent({
key: "AnimationRetargetingViewport",
order: 10,
component: () => (
<AnimationRetargetingViewport
onManagerReady={(manager) => {
currentManager = manager;
onManagerReadyObs.notifyObservers(manager);
}}
/>
),
});
} else {
// Dispose viewport β€” the component's useEffect cleanup will dispose Engine+Scene
viewportReg?.dispose();
viewportReg = null;
currentManager = null;
}

isEnabledObs.notifyObservers(enabled);
}

// Start disabled so the user opts-in to the viewport
setEnabled(false);

const panelReg = shellService.addSidePane({
key: "AnimationRetargetingPanel",
title: "Animation Retargeting",
description: (
<Body1>
Learn more in the <Link url="https://doc.babylonjs.com/features/featuresDeepDive/animation/animationRetargeting/" value="docs" />.
</Body1>
),
icon: PersonRunning20Regular,
horizontalLocation: "left",
verticalLocation: "top",
content: () => (
<AnimationRetargetingPanel
initialIsEnabled={isEnabled}
isEnabledObs={isEnabledObs}
onConfigChangedObs={onConfigChangedObs}
onManagerReadyObs={onManagerReadyObs}
getCurrentManager={() => currentManager}
namingSchemeManager={namingSchemeManager}
avatarManager={avatarManager}
animationManager={animationManager}
stateStore={persistedPanelState}
onSetEnabled={setEnabled}
onToggleConsole={() => currentManager?.htmlConsole.toggle()}
/>
),
});

return {
dispose: () => {
setEnabled(false);
panelReg.dispose();
onManagerReadyObs.clear();
isEnabledObs.clear();
onConfigChangedObs.clear();
},
};
},
};

export default {
serviceDefinitions: [AnimationRetargetingServiceDefinition],
} as const;
Loading