/** * AeThex Avatar Platform Configuration * Cross-platform avatar specifications for Roblox, VRChat, RecRoom, Spatial, Sandbox, and more */ export type AvatarPlatformId = | 'roblox' | 'vrchat' | 'recroom' | 'spatial' | 'sandbox' | 'neos' | 'resonite' | 'chilloutvr' | 'decentraland' | 'meta-horizon' | 'universal'; export interface BoneMapping { name: string; universalName: string; required: boolean; parent?: string; alternateNames?: string[]; } export interface AvatarConstraints { maxPolygons: number; maxBones: number; maxMaterials: number; maxTextureSize: number; maxFileSize: number; // in MB supportedFormats: string[]; minHeight?: number; maxHeight?: number; requiresPhysBones?: boolean; requiresDynamicBones?: boolean; } export interface SkeletonSpec { type: 'humanoid' | 'generic' | 'r6' | 'r15' | 'rthro' | 'custom'; rootBone: string; bones: BoneMapping[]; blendShapeSupport: boolean; maxBlendShapes?: number; ikSupport: boolean; fingerTracking: boolean; eyeTracking: boolean; fullBodyTracking: boolean; } export interface AvatarPlatform { id: AvatarPlatformId; name: string; displayName: string; description: string; color: string; icon: string; constraints: AvatarConstraints; skeleton: SkeletonSpec; exportFormat: string; importFormats: string[]; documentation: string; status: 'supported' | 'beta' | 'experimental' | 'coming-soon'; features: string[]; } // Universal humanoid bone names (industry standard) export const UNIVERSAL_BONES = { // Root ROOT: 'Root', HIPS: 'Hips', // Spine SPINE: 'Spine', SPINE1: 'Spine1', SPINE2: 'Spine2', CHEST: 'Chest', UPPER_CHEST: 'UpperChest', NECK: 'Neck', HEAD: 'Head', // Left Arm LEFT_SHOULDER: 'LeftShoulder', LEFT_UPPER_ARM: 'LeftUpperArm', LEFT_LOWER_ARM: 'LeftLowerArm', LEFT_HAND: 'LeftHand', // Left Fingers LEFT_THUMB_PROXIMAL: 'LeftThumbProximal', LEFT_THUMB_INTERMEDIATE: 'LeftThumbIntermediate', LEFT_THUMB_DISTAL: 'LeftThumbDistal', LEFT_INDEX_PROXIMAL: 'LeftIndexProximal', LEFT_INDEX_INTERMEDIATE: 'LeftIndexIntermediate', LEFT_INDEX_DISTAL: 'LeftIndexDistal', LEFT_MIDDLE_PROXIMAL: 'LeftMiddleProximal', LEFT_MIDDLE_INTERMEDIATE: 'LeftMiddleIntermediate', LEFT_MIDDLE_DISTAL: 'LeftMiddleDistal', LEFT_RING_PROXIMAL: 'LeftRingProximal', LEFT_RING_INTERMEDIATE: 'LeftRingIntermediate', LEFT_RING_DISTAL: 'LeftRingDistal', LEFT_LITTLE_PROXIMAL: 'LeftLittleProximal', LEFT_LITTLE_INTERMEDIATE: 'LeftLittleIntermediate', LEFT_LITTLE_DISTAL: 'LeftLittleDistal', // Right Arm RIGHT_SHOULDER: 'RightShoulder', RIGHT_UPPER_ARM: 'RightUpperArm', RIGHT_LOWER_ARM: 'RightLowerArm', RIGHT_HAND: 'RightHand', // Right Fingers RIGHT_THUMB_PROXIMAL: 'RightThumbProximal', RIGHT_THUMB_INTERMEDIATE: 'RightThumbIntermediate', RIGHT_THUMB_DISTAL: 'RightThumbDistal', RIGHT_INDEX_PROXIMAL: 'RightIndexProximal', RIGHT_INDEX_INTERMEDIATE: 'RightIndexIntermediate', RIGHT_INDEX_DISTAL: 'RightIndexDistal', RIGHT_MIDDLE_PROXIMAL: 'RightMiddleProximal', RIGHT_MIDDLE_INTERMEDIATE: 'RightMiddleIntermediate', RIGHT_MIDDLE_DISTAL: 'RightMiddleDistal', RIGHT_RING_PROXIMAL: 'RightRingProximal', RIGHT_RING_INTERMEDIATE: 'RightRingIntermediate', RIGHT_RING_DISTAL: 'RightRingDistal', RIGHT_LITTLE_PROXIMAL: 'RightLittleProximal', RIGHT_LITTLE_INTERMEDIATE: 'RightLittleIntermediate', RIGHT_LITTLE_DISTAL: 'RightLittleDistal', // Left Leg LEFT_UPPER_LEG: 'LeftUpperLeg', LEFT_LOWER_LEG: 'LeftLowerLeg', LEFT_FOOT: 'LeftFoot', LEFT_TOES: 'LeftToes', // Right Leg RIGHT_UPPER_LEG: 'RightUpperLeg', RIGHT_LOWER_LEG: 'RightLowerLeg', RIGHT_FOOT: 'RightFoot', RIGHT_TOES: 'RightToes', // Eyes LEFT_EYE: 'LeftEye', RIGHT_EYE: 'RightEye', JAW: 'Jaw', } as const; // VRChat skeleton specification const vrchatSkeleton: SkeletonSpec = { type: 'humanoid', rootBone: 'Armature', blendShapeSupport: true, maxBlendShapes: 256, ikSupport: true, fingerTracking: true, eyeTracking: true, fullBodyTracking: true, bones: [ { name: 'Hips', universalName: UNIVERSAL_BONES.HIPS, required: true }, { name: 'Spine', universalName: UNIVERSAL_BONES.SPINE, required: true, parent: 'Hips' }, { name: 'Chest', universalName: UNIVERSAL_BONES.CHEST, required: true, parent: 'Spine' }, { name: 'Upper Chest', universalName: UNIVERSAL_BONES.UPPER_CHEST, required: false, parent: 'Chest' }, { name: 'Neck', universalName: UNIVERSAL_BONES.NECK, required: true, parent: 'Chest' }, { name: 'Head', universalName: UNIVERSAL_BONES.HEAD, required: true, parent: 'Neck' }, { name: 'Left Shoulder', universalName: UNIVERSAL_BONES.LEFT_SHOULDER, required: false, parent: 'Chest' }, { name: 'Left Upper Arm', universalName: UNIVERSAL_BONES.LEFT_UPPER_ARM, required: true, parent: 'Left Shoulder' }, { name: 'Left Lower Arm', universalName: UNIVERSAL_BONES.LEFT_LOWER_ARM, required: true, parent: 'Left Upper Arm' }, { name: 'Left Hand', universalName: UNIVERSAL_BONES.LEFT_HAND, required: true, parent: 'Left Lower Arm' }, { name: 'Right Shoulder', universalName: UNIVERSAL_BONES.RIGHT_SHOULDER, required: false, parent: 'Chest' }, { name: 'Right Upper Arm', universalName: UNIVERSAL_BONES.RIGHT_UPPER_ARM, required: true, parent: 'Right Shoulder' }, { name: 'Right Lower Arm', universalName: UNIVERSAL_BONES.RIGHT_LOWER_ARM, required: true, parent: 'Right Upper Arm' }, { name: 'Right Hand', universalName: UNIVERSAL_BONES.RIGHT_HAND, required: true, parent: 'Right Lower Arm' }, { name: 'Left Upper Leg', universalName: UNIVERSAL_BONES.LEFT_UPPER_LEG, required: true, parent: 'Hips' }, { name: 'Left Lower Leg', universalName: UNIVERSAL_BONES.LEFT_LOWER_LEG, required: true, parent: 'Left Upper Leg' }, { name: 'Left Foot', universalName: UNIVERSAL_BONES.LEFT_FOOT, required: true, parent: 'Left Lower Leg' }, { name: 'Left Toes', universalName: UNIVERSAL_BONES.LEFT_TOES, required: false, parent: 'Left Foot' }, { name: 'Right Upper Leg', universalName: UNIVERSAL_BONES.RIGHT_UPPER_LEG, required: true, parent: 'Hips' }, { name: 'Right Lower Leg', universalName: UNIVERSAL_BONES.RIGHT_LOWER_LEG, required: true, parent: 'Right Upper Leg' }, { name: 'Right Foot', universalName: UNIVERSAL_BONES.RIGHT_FOOT, required: true, parent: 'Right Lower Leg' }, { name: 'Right Toes', universalName: UNIVERSAL_BONES.RIGHT_TOES, required: false, parent: 'Right Foot' }, { name: 'Left Eye', universalName: UNIVERSAL_BONES.LEFT_EYE, required: false, parent: 'Head' }, { name: 'Right Eye', universalName: UNIVERSAL_BONES.RIGHT_EYE, required: false, parent: 'Head' }, { name: 'Jaw', universalName: UNIVERSAL_BONES.JAW, required: false, parent: 'Head' }, ], }; // Roblox R15 skeleton specification const robloxR15Skeleton: SkeletonSpec = { type: 'r15', rootBone: 'HumanoidRootPart', blendShapeSupport: true, maxBlendShapes: 50, ikSupport: true, fingerTracking: false, eyeTracking: true, fullBodyTracking: false, bones: [ { name: 'HumanoidRootPart', universalName: UNIVERSAL_BONES.ROOT, required: true }, { name: 'LowerTorso', universalName: UNIVERSAL_BONES.HIPS, required: true, parent: 'HumanoidRootPart' }, { name: 'UpperTorso', universalName: UNIVERSAL_BONES.SPINE, required: true, parent: 'LowerTorso' }, { name: 'Head', universalName: UNIVERSAL_BONES.HEAD, required: true, parent: 'UpperTorso' }, { name: 'LeftUpperArm', universalName: UNIVERSAL_BONES.LEFT_UPPER_ARM, required: true, parent: 'UpperTorso' }, { name: 'LeftLowerArm', universalName: UNIVERSAL_BONES.LEFT_LOWER_ARM, required: true, parent: 'LeftUpperArm' }, { name: 'LeftHand', universalName: UNIVERSAL_BONES.LEFT_HAND, required: true, parent: 'LeftLowerArm' }, { name: 'RightUpperArm', universalName: UNIVERSAL_BONES.RIGHT_UPPER_ARM, required: true, parent: 'UpperTorso' }, { name: 'RightLowerArm', universalName: UNIVERSAL_BONES.RIGHT_LOWER_ARM, required: true, parent: 'RightUpperArm' }, { name: 'RightHand', universalName: UNIVERSAL_BONES.RIGHT_HAND, required: true, parent: 'RightLowerArm' }, { name: 'LeftUpperLeg', universalName: UNIVERSAL_BONES.LEFT_UPPER_LEG, required: true, parent: 'LowerTorso' }, { name: 'LeftLowerLeg', universalName: UNIVERSAL_BONES.LEFT_LOWER_LEG, required: true, parent: 'LeftUpperLeg' }, { name: 'LeftFoot', universalName: UNIVERSAL_BONES.LEFT_FOOT, required: true, parent: 'LeftLowerLeg' }, { name: 'RightUpperLeg', universalName: UNIVERSAL_BONES.RIGHT_UPPER_LEG, required: true, parent: 'LowerTorso' }, { name: 'RightLowerLeg', universalName: UNIVERSAL_BONES.RIGHT_LOWER_LEG, required: true, parent: 'RightUpperLeg' }, { name: 'RightFoot', universalName: UNIVERSAL_BONES.RIGHT_FOOT, required: true, parent: 'RightLowerLeg' }, ], }; // RecRoom skeleton specification const recRoomSkeleton: SkeletonSpec = { type: 'humanoid', rootBone: 'Root', blendShapeSupport: true, maxBlendShapes: 30, ikSupport: true, fingerTracking: false, eyeTracking: false, fullBodyTracking: false, bones: [ { name: 'Root', universalName: UNIVERSAL_BONES.ROOT, required: true }, { name: 'Hips', universalName: UNIVERSAL_BONES.HIPS, required: true, parent: 'Root' }, { name: 'Spine', universalName: UNIVERSAL_BONES.SPINE, required: true, parent: 'Hips' }, { name: 'Chest', universalName: UNIVERSAL_BONES.CHEST, required: true, parent: 'Spine' }, { name: 'Neck', universalName: UNIVERSAL_BONES.NECK, required: true, parent: 'Chest' }, { name: 'Head', universalName: UNIVERSAL_BONES.HEAD, required: true, parent: 'Neck' }, { name: 'LeftArm', universalName: UNIVERSAL_BONES.LEFT_UPPER_ARM, required: true, parent: 'Chest' }, { name: 'LeftForeArm', universalName: UNIVERSAL_BONES.LEFT_LOWER_ARM, required: true, parent: 'LeftArm' }, { name: 'LeftHand', universalName: UNIVERSAL_BONES.LEFT_HAND, required: true, parent: 'LeftForeArm' }, { name: 'RightArm', universalName: UNIVERSAL_BONES.RIGHT_UPPER_ARM, required: true, parent: 'Chest' }, { name: 'RightForeArm', universalName: UNIVERSAL_BONES.RIGHT_LOWER_ARM, required: true, parent: 'RightArm' }, { name: 'RightHand', universalName: UNIVERSAL_BONES.RIGHT_HAND, required: true, parent: 'RightForeArm' }, { name: 'LeftLeg', universalName: UNIVERSAL_BONES.LEFT_UPPER_LEG, required: true, parent: 'Hips' }, { name: 'LeftKnee', universalName: UNIVERSAL_BONES.LEFT_LOWER_LEG, required: true, parent: 'LeftLeg' }, { name: 'LeftFoot', universalName: UNIVERSAL_BONES.LEFT_FOOT, required: true, parent: 'LeftKnee' }, { name: 'RightLeg', universalName: UNIVERSAL_BONES.RIGHT_UPPER_LEG, required: true, parent: 'Hips' }, { name: 'RightKnee', universalName: UNIVERSAL_BONES.RIGHT_LOWER_LEG, required: true, parent: 'RightLeg' }, { name: 'RightFoot', universalName: UNIVERSAL_BONES.RIGHT_FOOT, required: true, parent: 'RightKnee' }, ], }; // Universal/Standard humanoid skeleton const universalSkeleton: SkeletonSpec = { type: 'humanoid', rootBone: 'Armature', blendShapeSupport: true, maxBlendShapes: 256, ikSupport: true, fingerTracking: true, eyeTracking: true, fullBodyTracking: true, bones: Object.entries(UNIVERSAL_BONES).map(([key, name]) => ({ name, universalName: name, required: ['HIPS', 'SPINE', 'HEAD', 'LEFT_UPPER_ARM', 'LEFT_LOWER_ARM', 'LEFT_HAND', 'RIGHT_UPPER_ARM', 'RIGHT_LOWER_ARM', 'RIGHT_HAND', 'LEFT_UPPER_LEG', 'LEFT_LOWER_LEG', 'LEFT_FOOT', 'RIGHT_UPPER_LEG', 'RIGHT_LOWER_LEG', 'RIGHT_FOOT'].includes(key), })), }; export const avatarPlatforms: Record = { roblox: { id: 'roblox', name: 'Roblox', displayName: 'Roblox Studio', description: 'Import/export avatars for Roblox experiences with R6, R15, or Rthro support', color: '#00A2FF', icon: 'gamepad-2', constraints: { maxPolygons: 10000, maxBones: 76, maxMaterials: 1, maxTextureSize: 1024, maxFileSize: 30, supportedFormats: ['fbx', 'obj'], minHeight: 0.7, maxHeight: 3.0, }, skeleton: robloxR15Skeleton, exportFormat: 'fbx', importFormats: ['fbx', 'obj', 'glb', 'gltf', 'vrm'], documentation: 'https://create.roblox.com/docs/art/characters', status: 'supported', features: ['R6', 'R15', 'Rthro', 'Layered Clothing', 'Dynamic Heads', 'Accessories'], }, vrchat: { id: 'vrchat', name: 'VRChat', displayName: 'VRChat SDK', description: 'Create avatars for VRChat with full body tracking, PhysBones, and expressions', color: '#1FB2A5', icon: 'glasses', constraints: { maxPolygons: 70000, // Poor rating threshold maxBones: 256, maxMaterials: 32, maxTextureSize: 2048, maxFileSize: 200, supportedFormats: ['fbx', 'vrm'], requiresPhysBones: true, }, skeleton: vrchatSkeleton, exportFormat: 'unitypackage', importFormats: ['fbx', 'glb', 'gltf', 'vrm', 'pmx'], documentation: 'https://creators.vrchat.com/avatars/', status: 'supported', features: ['PhysBones', 'Avatar Dynamics', 'Eye Tracking', 'Face Tracking', 'OSC', 'Full Body'], }, recroom: { id: 'recroom', name: 'RecRoom', displayName: 'Rec Room', description: 'Create fun, stylized avatars for Rec Room social experiences', color: '#FF6B6B', icon: 'party-popper', constraints: { maxPolygons: 15000, maxBones: 52, maxMaterials: 4, maxTextureSize: 512, maxFileSize: 20, supportedFormats: ['fbx'], }, skeleton: recRoomSkeleton, exportFormat: 'fbx', importFormats: ['fbx', 'glb', 'gltf', 'vrm'], documentation: 'https://recroom.com/developer', status: 'supported', features: ['Stylized Look', 'Props', 'Costumes', 'Expressions'], }, spatial: { id: 'spatial', name: 'Spatial', displayName: 'Spatial Creator Toolkit', description: 'Create avatars for Spatial VR/AR experiences and virtual spaces', color: '#9B5DE5', icon: 'globe', constraints: { maxPolygons: 50000, maxBones: 128, maxMaterials: 8, maxTextureSize: 2048, maxFileSize: 50, supportedFormats: ['glb', 'gltf', 'vrm'], }, skeleton: vrchatSkeleton, exportFormat: 'glb', importFormats: ['glb', 'gltf', 'vrm', 'fbx'], documentation: 'https://toolkit.spatial.io/docs/avatars', status: 'supported', features: ['Ready Player Me', 'Custom Avatars', 'Emotes', 'Accessories'], }, sandbox: { id: 'sandbox', name: 'Sandbox', displayName: 'The Sandbox', description: 'Create voxel-style or custom avatars for The Sandbox metaverse', color: '#00D4FF', icon: 'box', constraints: { maxPolygons: 20000, maxBones: 64, maxMaterials: 8, maxTextureSize: 1024, maxFileSize: 30, supportedFormats: ['glb', 'gltf', 'vox'], }, skeleton: universalSkeleton, exportFormat: 'glb', importFormats: ['glb', 'gltf', 'fbx', 'vrm', 'vox'], documentation: 'https://sandboxgame.gitbook.io/the-sandbox', status: 'supported', features: ['Voxel Style', 'LAND Integration', 'NFT Support', 'Equipment'], }, neos: { id: 'neos', name: 'NeosVR', displayName: 'NeosVR', description: 'Create highly customizable avatars for NeosVR', color: '#F5A623', icon: 'cpu', constraints: { maxPolygons: 100000, maxBones: 256, maxMaterials: 32, maxTextureSize: 4096, maxFileSize: 300, supportedFormats: ['fbx', 'glb', 'gltf', 'vrm'], }, skeleton: vrchatSkeleton, exportFormat: 'glb', importFormats: ['fbx', 'glb', 'gltf', 'vrm', 'obj'], documentation: 'https://wiki.neos.com/', status: 'beta', features: ['LogiX', 'Dynamic Bones', 'Full Customization', 'In-World Editing'], }, resonite: { id: 'resonite', name: 'Resonite', displayName: 'Resonite', description: 'Successor to NeosVR with enhanced avatar capabilities', color: '#7B68EE', icon: 'sparkles', constraints: { maxPolygons: 100000, maxBones: 256, maxMaterials: 32, maxTextureSize: 4096, maxFileSize: 300, supportedFormats: ['fbx', 'glb', 'gltf', 'vrm'], }, skeleton: vrchatSkeleton, exportFormat: 'glb', importFormats: ['fbx', 'glb', 'gltf', 'vrm', 'obj'], documentation: 'https://wiki.resonite.com/', status: 'beta', features: ['ProtoFlux', 'Dynamic Bones', 'Face Tracking', 'Full Body'], }, chilloutvr: { id: 'chilloutvr', name: 'ChilloutVR', displayName: 'ChilloutVR', description: 'Create avatars for ChilloutVR social platform', color: '#E91E63', icon: 'heart', constraints: { maxPolygons: 80000, maxBones: 256, maxMaterials: 24, maxTextureSize: 2048, maxFileSize: 150, supportedFormats: ['fbx', 'vrm'], }, skeleton: vrchatSkeleton, exportFormat: 'unitypackage', importFormats: ['fbx', 'glb', 'gltf', 'vrm'], documentation: 'https://docs.abinteractive.net/', status: 'beta', features: ['Advanced Rigging', 'Toggles', 'Gestures', 'Eye/Face Tracking'], }, decentraland: { id: 'decentraland', name: 'Decentraland', displayName: 'Decentraland', description: 'Create Web3-enabled avatars for the Decentraland metaverse', color: '#FF2D55', icon: 'landmark', constraints: { maxPolygons: 1500, maxBones: 52, maxMaterials: 2, maxTextureSize: 512, maxFileSize: 2, supportedFormats: ['glb'], }, skeleton: universalSkeleton, exportFormat: 'glb', importFormats: ['glb', 'gltf', 'fbx', 'vrm'], documentation: 'https://docs.decentraland.org/creator/wearables/creating-wearables/', status: 'supported', features: ['Wearables', 'NFT Integration', 'Emotes', 'Blockchain'], }, 'meta-horizon': { id: 'meta-horizon', name: 'Meta Horizon', displayName: 'Meta Horizon Worlds', description: 'Create avatars for Meta Horizon Worlds VR platform', color: '#0668E1', icon: 'headphones', constraints: { maxPolygons: 25000, maxBones: 70, maxMaterials: 8, maxTextureSize: 1024, maxFileSize: 50, supportedFormats: ['glb', 'fbx'], }, skeleton: universalSkeleton, exportFormat: 'glb', importFormats: ['glb', 'gltf', 'fbx', 'vrm'], documentation: 'https://developer.oculus.com/', status: 'experimental', features: ['Hand Tracking', 'Body Estimation', 'Expressions'], }, universal: { id: 'universal', name: 'Universal', displayName: 'AeThex Universal Format', description: 'The AeThex universal avatar format compatible with all platforms', color: '#00FF88', icon: 'sparkles', constraints: { maxPolygons: 100000, maxBones: 256, maxMaterials: 32, maxTextureSize: 4096, maxFileSize: 500, supportedFormats: ['aeth', 'glb', 'gltf', 'vrm', 'fbx'], }, skeleton: universalSkeleton, exportFormat: 'aeth', importFormats: ['fbx', 'glb', 'gltf', 'vrm', 'obj', 'pmx', 'vroid'], documentation: 'https://aethex.dev/docs/avatar-format', status: 'supported', features: ['All Platforms', 'Lossless Conversion', 'Metadata Preservation', 'Auto-Optimization'], }, }; export const supportedPlatforms = Object.values(avatarPlatforms).filter( (p) => p.status === 'supported' || p.status === 'beta' ); export function getAvatarPlatform(id: AvatarPlatformId): AvatarPlatform { return avatarPlatforms[id]; } export function isPlatformSupported(id: AvatarPlatformId): boolean { return avatarPlatforms[id].status === 'supported'; } export function getConstraintsForPlatform(id: AvatarPlatformId): AvatarConstraints { return avatarPlatforms[id].constraints; } export function getSkeletonForPlatform(id: AvatarPlatformId): SkeletonSpec { return avatarPlatforms[id].skeleton; } export function canConvert(from: AvatarPlatformId, to: AvatarPlatformId): boolean { const fromPlatform = avatarPlatforms[from]; const toPlatform = avatarPlatforms[to]; // Can always convert to universal if (to === 'universal') return true; // Can convert from universal to anything if (from === 'universal') return true; // Check if formats are compatible const fromFormat = fromPlatform.exportFormat; return toPlatform.importFormats.includes(fromFormat); }