#if UNITY_EDITOR using System; using System.IO; using System.Linq; using System.Collections.Generic; using System.Security.Cryptography; using Newtonsoft.Json; using UnityEditor; using UnityEditor.Animations; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.SceneManagement; public static class ImportLmGirlTokenRigFbx { const string FolderPath="Assets/Bundles/Characters/Human/test/girl_tokenrig"; const string ModelPath=FolderPath+"/LmGirlTokenRig.fbx"; const string MaterialPath=FolderPath+"/LmGirlTokenRig.mat"; static string Repo=>Directory.GetParent(Application.dataPath).Parent.FullName; static string Output=>Path.Combine(Repo,"Tools/output/lmgirl_tpose_studio/rig_mixamo41/fbx"); static float[] V(Vector3 v)=>new[]{v.x,v.y,v.z}; static void Folder(string p){if(AssetDatabase.IsValidFolder(p))return;Folder(Path.GetDirectoryName(p).Replace('\\','/'));AssetDatabase.CreateFolder(Path.GetDirectoryName(p).Replace('\\','/'),Path.GetFileName(p));} static string Hash(string p){using(var sha=SHA256.Create())return BitConverter.ToString(sha.ComputeHash(File.ReadAllBytes(p))).Replace("-","").ToLowerInvariant();} static void CopyNew(string source,string target) { var full=Path.Combine(Directory.GetParent(Application.dataPath).FullName,target); if(File.Exists(full)){if(Hash(full)!=Hash(source))throw new Exception("Existing destination differs: "+target);} else File.Copy(source,full); AssetDatabase.ImportAsset(target,ImportAssetOptions.ForceSynchronousImport); } public static string Import() { if(EditorApplication.isPlaying)throw new Exception("Edit Mode required"); Folder(FolderPath); foreach(var suffix in new[]{"base","mix"}) { string path=FolderPath+"/LmGirlTokenRig_"+suffix+".png"; CopyNew(Path.Combine(Repo,"Tools/output/lmgirl_tpose_studio/xpbr/LmGirlTpose_"+suffix+".png"),path); var ti=(TextureImporter)AssetImporter.GetAtPath(path);ti.sRGBTexture=suffix=="base";ti.alphaIsTransparency=false;ti.maxTextureSize=1024;ti.SaveAndReimport(); } var material=AssetDatabase.LoadAssetAtPath(MaterialPath); if(!material) { var reference=AssetDatabase.LoadAssetAtPath("Assets/Review/CharacterAnimationLab/LmGirl/Generated/LmGirlTextured.mat"); if(!reference)throw new Exception("Missing approved XPbr reference material"); material=new Material(reference);material.name="LmGirlTokenRig";AssetDatabase.CreateAsset(material,MaterialPath); } material.SetTexture("_MainTex",AssetDatabase.LoadAssetAtPath(FolderPath+"/LmGirlTokenRig_base.png")); material.SetTexture("_MixTex",AssetDatabase.LoadAssetAtPath(FolderPath+"/LmGirlTokenRig_mix.png")); EditorUtility.SetDirty(material);AssetDatabase.SaveAssetIfDirty(material); CopyNew(Path.Combine(Output,"LmGirlTokenRig.fbx"),ModelPath); var importer=(ModelImporter)AssetImporter.GetAtPath(ModelPath); importer.animationType=ModelImporterAnimationType.Human;importer.avatarSetup=ModelImporterAvatarSetup.CreateFromThisModel; importer.importAnimation=false;importer.globalScale=1;importer.useFileScale=true; importer.importCameras=false;importer.importLights=false; importer.meshCompression=ModelImporterMeshCompression.Off; importer.skinWeights=ModelImporterSkinWeights.Custom;importer.maxBonesPerVertex=4;importer.minBoneWeight=0; importer.materialImportMode=ModelImporterMaterialImportMode.ImportStandard; importer.AddRemap(new AssetImporter.SourceAssetIdentifier(typeof(Material),"LmGirlTokenRig_Body"),material); importer.SaveAndReimport(); var model=AssetDatabase.LoadAssetAtPath(ModelPath); var avatar=AssetDatabase.LoadAllAssetsAtPath(ModelPath).OfType().FirstOrDefault(); if(!avatar || !avatar.isValid || !avatar.isHuman)throw new Exception("Invalid Humanoid Avatar"); if(!model.GetComponentsInChildren().All(s=>s.sharedMaterial==material))throw new Exception("Material remap failed"); return "Imported "+ModelPath+" with valid Humanoid Avatar and project XPbr material"; } public static string Verify() { var scene=EditorSceneManager.NewPreviewScene();var units=new List(); var controller=AssetDatabase.LoadAssetAtPath("Assets/Bundles/Characters/Human/Common/Man.controller"); var walk=controller.layers[0].stateMachine.states.Single(s=>s.state.name=="walk").state; var records=new List(); try { int column=0; foreach(var path in new[]{"Assets/Bundles/Characters/Human/test/girl/LmGirlTpose.fbx",ModelPath}) { var go=(GameObject)PrefabUtility.InstantiatePrefab(AssetDatabase.LoadAssetAtPath(path),scene);units.Add(go); go.transform.position=new Vector3((.5f-column++)*.95f,0,0); var a=go.GetComponent();var skin=go.GetComponentInChildren(); if(!a||!a.isHuman||!a.avatar.isValid)throw new Exception("Invalid Animator: "+path); var baked=new Mesh();skin.BakeMesh(baked); var vertices=baked.vertices.Select(p=>go.transform.InverseTransformPoint(skin.transform.TransformPoint(p))).ToArray(); var lo=vertices.Aggregate(Vector3.positiveInfinity,Vector3.Min);var hi=vertices.Aggregate(Vector3.negativeInfinity,Vector3.Max); var toes=new[]{HumanBodyBones.LeftToes,HumanBodyBones.RightToes}.Select(b=>go.transform.InverseTransformPoint(a.GetBoneTransform(b).position)).ToArray(); var ankles=new[]{HumanBodyBones.LeftFoot,HumanBodyBones.RightFoot}.Select(b=>go.transform.InverseTransformPoint(a.GetBoneTransform(b).position)).ToArray(); if(hi.y-lo.y<1 || hi.y-lo.y>1.4f || Mathf.Abs(lo.y)>.001f)throw new Exception("Wrong height/floor: "+path); if(toes[0].z<=ankles[0].z||toes[1].z<=ankles[1].z)throw new Exception("Not facing +Z: "+path); var counts=skin.sharedMesh.GetBonesPerVertex();int maxInfluences=0;foreach(var n in counts)maxInfluences=Math.Max(maxInfluences,n);counts.Dispose(); var rest=new{boundsMin=V(lo),boundsMax=V(hi),rootRotation=V(go.transform.localEulerAngles),rootScale=V(go.transform.localScale),toes=toes.Select(V).ToArray(),ankles=ankles.Select(V).ToArray()}; UnityEngine.Object.DestroyImmediate(baked); a.runtimeAnimatorController=controller;a.applyRootMotion=false;a.cullingMode=AnimatorCullingMode.AlwaysAnimate;a.Rebind();a.Update(0); float minGap=float.MaxValue,maxGap=float.MinValue;int crossed=0; for(int i=0;i<125;i++) { a.Play("Base Layer.walk",0,(i/120f)/walk.motion.averageDuration);a.Update(0); var left=go.transform.InverseTransformPoint(a.GetBoneTransform(HumanBodyBones.LeftFoot).position);var right=go.transform.InverseTransformPoint(a.GetBoneTransform(HumanBodyBones.RightFoot).position); float gap=right.x-left.x;minGap=Mathf.Min(minGap,gap);maxGap=Mathf.Max(maxGap,gap);if(gap<0)crossed++; } if(crossed>0)throw new Exception("Crossed ankles: "+path); records.Add(new{path,avatar=a.avatar.name,a.avatar.isHuman,a.avatar.isValid,bones=skin.bones.Length,vertices=skin.sharedMesh.vertexCount,triangles=skin.sharedMesh.triangles.Length/3, uv=skin.sharedMesh.uv.Length,maxInfluences,material=AssetDatabase.GetAssetPath(skin.sharedMaterial),shader=skin.sharedMaterial.shader.name,rest, walk=new{clip=AssetDatabase.GetAssetPath(walk.motion),footIK=walk.iKOnFeet,samples=125,minGap,maxGap,crossed}}); a.Play("Base Layer.walk",0,.2666667f/walk.motion.averageDuration);a.Update(0); var hips=a.GetBoneTransform(HumanBodyBones.Hips);var hp=hips.position;hp.x=go.transform.position.x;hp.z=0;hips.position=hp; } Render(scene); var json=JsonConvert.SerializeObject(new{records,scenes=Enumerable.Range(0,SceneManager.sceneCount).Select(i=>{var s=SceneManager.GetSceneAt(i);return new{s.path,s.isDirty};}).ToArray()},Formatting.Indented); File.WriteAllText(Path.Combine(Output,"unity_validation.json"),json);return json; } finally{EditorSceneManager.ClosePreviewScene(scene);} } static void Render(Scene scene) { var light=new GameObject("ExportAuditLight").AddComponent();SceneManager.MoveGameObjectToScene(light.gameObject,scene);light.type=LightType.Directional;light.intensity=1.3f;light.transform.rotation=Quaternion.Euler(35,140,0); var camera=new GameObject("ExportAuditCamera").AddComponent();SceneManager.MoveGameObjectToScene(camera.gameObject,scene);camera.scene=scene;camera.orthographic=true;camera.orthographicSize=.73f; camera.transform.position=new Vector3(0,.61f,5);camera.transform.LookAt(new Vector3(0,.61f,0));camera.clearFlags=CameraClearFlags.SolidColor;camera.backgroundColor=new Color(.22f,.25f,.29f);camera.nearClipPlane=.01f;camera.farClipPlane=20; var rt=new RenderTexture(1080,800,24);var tex=new Texture2D(1080,800,TextureFormat.RGB24,false);var previous=RenderTexture.active; try{camera.targetTexture=rt;camera.Render();RenderTexture.active=rt;tex.ReadPixels(new Rect(0,0,1080,800),0,0);tex.Apply();File.WriteAllBytes(Path.Combine(Output,"unity_walking.png"),tex.EncodeToPNG());} finally{RenderTexture.active=previous;camera.targetTexture=null;rt.Release();UnityEngine.Object.DestroyImmediate(rt);UnityEngine.Object.DestroyImmediate(tex);} } } #endif