1#pragma warning disable 168
3using System.Collections.Generic;
6using System.Text.RegularExpressions;
27 public static class AssetLoader
32 private const int ProcessingAnimationsStep = 0;
37 private const int ProcessingTexturesStep = 1;
42 private const int ProcessingRenderersStep = 2;
47 private const int FinalStep = 3;
52 private const int ExtraStepsCount = 4;
64 public static AssetLoaderContext LoadModelFromFile(
string path, Action<AssetLoaderContext> onLoad, Action<AssetLoaderContext> onMaterialsLoad, Action<AssetLoaderContext, float> onProgress, Action<IContextualizedError> onError =
null, GameObject wrapperGameObject =
null,
AssetLoaderOptions assetLoaderOptions =
null,
object customContextData =
null)
66#if UNITY_WEBGL || UNITY_UWP || TRILIB_FORCE_SYNC
67 AssetLoaderContext assetLoaderContext =
null;
70 assetLoaderContext = LoadModelFromFileNoThread(path, onError, wrapperGameObject, assetLoaderOptions ?? CreateDefaultLoaderOptions(), customContextData);
71 onLoad?.Invoke(assetLoaderContext);
72 onMaterialsLoad?.Invoke(assetLoaderContext);
74 catch (Exception exception)
76 if (exception is IContextualizedError contextualizedError)
78 HandleError(contextualizedError);
82 HandleError(
new ContextualizedError<AssetLoaderContext>(exception,
null));
85 return assetLoaderContext;
87 var assetLoaderContext =
new AssetLoaderContext
89 Options = assetLoaderOptions ?? CreateDefaultLoaderOptions(),
91 BasePath = FileUtils.GetFileDirectory(path),
92 WrapperGameObject = wrapperGameObject,
93 OnMaterialsLoad = onMaterialsLoad,
95 OnProgress = onProgress,
96 HandleError = HandleError,
98 CustomData = customContextData,
100 if (assetLoaderContext.Options.ForceGCCollectionWhileLoading)
102 GCHelper.GetInstance()?.RegisterLoading();
104#if TRILIB_USE_THREAD_NAMES
105 var threadName =
"TriLib_LoadModelFromFile";
107 var threadName =
string.Empty;
109 assetLoaderContext.Tasks.Add(ThreadUtils.RunThread(assetLoaderContext, ref assetLoaderContext.CancellationToken, LoadModel, ProcessRootModel, HandleError, assetLoaderContext.Options.Timeout, threadName));
110 return assetLoaderContext;
126 public static AssetLoaderContext LoadModelFromStream(Stream stream,
string filename =
null,
string fileExtension =
null, Action<AssetLoaderContext> onLoad =
null, Action<AssetLoaderContext> onMaterialsLoad =
null, Action<AssetLoaderContext, float> onProgress =
null, Action<IContextualizedError> onError =
null, GameObject wrapperGameObject =
null,
AssetLoaderOptions assetLoaderOptions =
null,
object customContextData =
null)
128#if UNITY_WEBGL || UNITY_UWP || TRILIB_FORCE_SYNC
129 AssetLoaderContext assetLoaderContext =
null;
132 assetLoaderContext = LoadModelFromStreamNoThread(stream, filename, fileExtension, onError, wrapperGameObject, assetLoaderOptions ?? CreateDefaultLoaderOptions(), customContextData);
133 onLoad?.Invoke(assetLoaderContext);
134 onMaterialsLoad?.Invoke(assetLoaderContext);
136 catch (Exception exception)
138 if (exception is IContextualizedError contextualizedError)
140 HandleError(contextualizedError);
144 HandleError(
new ContextualizedError<AssetLoaderContext>(exception,
null));
147 return assetLoaderContext;
149 var assetLoaderContext =
new AssetLoaderContext
151 Options = assetLoaderOptions ?? CreateDefaultLoaderOptions(),
154 FileExtension = fileExtension ?? FileUtils.GetFileExtension(filename,
false),
155 BasePath = FileUtils.GetFileDirectory(filename),
156 WrapperGameObject = wrapperGameObject,
157 OnMaterialsLoad = onMaterialsLoad,
159 OnProgress = onProgress,
160 HandleError = HandleError,
162 CustomData = customContextData
164 if (assetLoaderContext.Options.ForceGCCollectionWhileLoading)
166 GCHelper.GetInstance()?.RegisterLoading();
168#if TRILIB_USE_THREAD_NAMES
169 var threadName =
"TriLib_LoadModelFromStream";
171 var threadName =
string.Empty;
173 assetLoaderContext.Tasks.Add(ThreadUtils.RunThread(assetLoaderContext, ref assetLoaderContext.CancellationToken, LoadModel, ProcessRootModel, HandleError, assetLoaderContext.Options.Timeout, threadName));
174 return assetLoaderContext;
185 public static AssetLoaderContext LoadModelFromFileNoThread(
string path, Action<IContextualizedError> onError =
null, GameObject wrapperGameObject =
null,
AssetLoaderOptions assetLoaderOptions =
null,
object customContextData =
null)
187 var assetLoaderContext =
new AssetLoaderContext
189 Options = assetLoaderOptions ?? CreateDefaultLoaderOptions(),
191 BasePath = FileUtils.GetFileDirectory(path),
192 CustomData = customContextData,
193 HandleError = HandleError,
195 WrapperGameObject = wrapperGameObject,
200 if (assetLoaderContext.Options.ForceGCCollectionWhileLoading)
202 GCHelper.GetInstance()?.RegisterLoading();
204 LoadModel(assetLoaderContext);
205 if (assetLoaderContext.Reader !=
null)
207 ProcessRootModel(assetLoaderContext);
210 catch (Exception exception)
212 HandleError(
new ContextualizedError<AssetLoaderContext>(exception, assetLoaderContext));
214 return assetLoaderContext;
226 public static AssetLoaderContext LoadModelFromStreamNoThread(Stream stream,
string filename =
null,
string fileExtension =
null, Action<IContextualizedError> onError =
null, GameObject wrapperGameObject =
null,
AssetLoaderOptions assetLoaderOptions =
null,
object customContextData =
null)
228 var assetLoaderContext =
new AssetLoaderContext
230 Options = assetLoaderOptions ?? CreateDefaultLoaderOptions(),
233 FileExtension = fileExtension ?? FileUtils.GetFileExtension(filename,
false),
234 BasePath = FileUtils.GetFileDirectory(filename),
235 CustomData = customContextData,
236 HandleError = HandleError,
238 WrapperGameObject = wrapperGameObject,
243 if (assetLoaderContext.Options.ForceGCCollectionWhileLoading)
245 GCHelper.GetInstance()?.RegisterLoading();
247 LoadModel(assetLoaderContext);
248 if (assetLoaderContext.Reader !=
null)
250 ProcessRootModel(assetLoaderContext);
253 catch (Exception exception)
255 HandleError(
new ContextualizedError<AssetLoaderContext>(exception, assetLoaderContext));
257 return assetLoaderContext;
261 private static Object LoadOrCreateScriptableObject(
string type,
string subFolder)
263 string mappersFilePath;
264 var triLibMapperAssets = AssetDatabase.FindAssets(
"TriLibMappersPlaceholder");
265 if (triLibMapperAssets.Length > 0)
267 mappersFilePath = AssetDatabase.GUIDToAssetPath(triLibMapperAssets[0]);
271 throw new Exception(
"Could not find \"TriLibMappersPlaceholder\" file. Please re-import TriLib package.");
273 var mappersDirectory = $
"{FileUtils.GetFileDirectory(mappersFilePath)}";
274 var assetDirectory = $
"{mappersDirectory}/{subFolder}";
275 if (!AssetDatabase.IsValidFolder(assetDirectory))
277 AssetDatabase.CreateFolder(mappersDirectory, subFolder);
279 var assetPath = $
"{assetDirectory}/{type}.asset";
280 var scriptableObject = AssetDatabase.LoadAssetAtPath(assetPath, typeof(
Object));
281 if (scriptableObject ==
null)
283 scriptableObject = ScriptableObject.CreateInstance(type);
284 AssetDatabase.CreateAsset(scriptableObject, assetPath);
286 return scriptableObject;
293 public static AssetLoaderOptions CreateDefaultLoaderOptions(
bool generateAssets =
false)
300 byNameRootBoneMapper = (
ByNameRootBoneMapper)LoadOrCreateScriptableObject(
"ByNameRootBoneMapper",
"RootBone");
309 byNameRootBoneMapper.name =
"ByNameRootBoneMapper";
310 assetLoaderOptions.RootBoneMapper = byNameRootBoneMapper;
311 if (MaterialMapper.RegisteredMappers.Count == 0)
313 Debug.LogWarning(
"Please add at least one MaterialMapper name to the MaterialMapper.RegisteredMappers static field to create the right MaterialMapper for the Render Pipeline you are using.");
317 var materialMappers =
new List<MaterialMapper>();
318 foreach (var materialMapperName
in MaterialMapper.RegisteredMappers)
320 if (materialMapperName ==
null)
324 MaterialMapper materialMapper;
328 materialMapper = (MaterialMapper)LoadOrCreateScriptableObject(materialMapperName,
"Material");
332 materialMapper = (MaterialMapper)ScriptableObject.CreateInstance(materialMapperName);
335 materialMapper = ScriptableObject.CreateInstance(materialMapperName) as MaterialMapper;
337 if (materialMapper !=
null)
339 materialMapper.name = materialMapperName;
340 if (materialMapper.IsCompatible(
null))
342 materialMappers.Add(materialMapper);
343 assetLoaderOptions.FixedAllocations.Add(materialMapper);
348 var assetPath = AssetDatabase.GetAssetPath(materialMapper);
349 if (assetPath ==
null)
351 Object.DestroyImmediate(materialMapper);
354 Object.Destroy(materialMapper);
360 if (materialMappers.Count == 0)
362 Debug.LogWarning(
"TriLib could not find any suitable MaterialMapper on the project.");
366 assetLoaderOptions.MaterialMappers = materialMappers.ToArray();
369 assetLoaderOptions.FixedAllocations.Add(assetLoaderOptions);
370 return assetLoaderOptions;
375 private static void ProcessModel(AssetLoaderContext assetLoaderContext)
377 if (assetLoaderContext.RootModel !=
null)
379 ParseModel(assetLoaderContext, assetLoaderContext.WrapperGameObject !=
null ? assetLoaderContext.WrapperGameObject.transform :
null, assetLoaderContext.RootModel, assetLoaderContext.RootModel, out assetLoaderContext.RootGameObject);
380 assetLoaderContext.RootGameObject.transform.localScale = Vector3.one;
381 if (assetLoaderContext.Options.AnimationType != AnimationType.None || assetLoaderContext.Options.ImportBlendShapes)
383 SetupRootBone(assetLoaderContext);
384 SetupModelBones(assetLoaderContext, assetLoaderContext.RootModel);
385 SetupModelLod(assetLoaderContext, assetLoaderContext.RootModel);
386 BuildGameObjectsPaths(assetLoaderContext);
387 SetupRig(assetLoaderContext);
389 if (assetLoaderContext.Options.Static)
391 assetLoaderContext.RootGameObject.isStatic =
true;
394 assetLoaderContext.OnLoad?.Invoke(assetLoaderContext);
400 private static void SetupModelLod(AssetLoaderContext assetLoaderContext, IModel model)
402 if (model.Children !=
null && model.Children.Count > 0)
404 var lodModels =
new Dictionary<int, Renderer[]>(model.Children.Count);
405 var minLod =
int.MaxValue;
407 for (var i = 0; i < model.Children.Count; i++)
409 var child = model.Children[i];
410 var match = Regex.Match(child.Name,
"_LOD(?<number>[0-9]+)|LOD_(?<number>[0-9]+)");
413 var lodNumber = Convert.ToInt32(match.Groups[
"number"].Value);
414 if (lodModels.ContainsKey(lodNumber))
419 minLod = Mathf.Min(lodNumber, minLod);
420 maxLod = Mathf.Max(lodNumber, maxLod);
421 lodModels.Add(lodNumber, assetLoaderContext.GameObjects[child].GetComponentsInChildren<Renderer>());
425 if (lodModels.Count > 1)
427 var newGameObject = assetLoaderContext.GameObjects[model];
428 var lods =
new LOD[lodModels.Count + 1];
429 var lodGroup = newGameObject.AddComponent<LODGroup>();
431 var lastPosition = 1f;
432 for (var i = minLod; i <= maxLod; i++)
434 if (lodModels.TryGetValue(i, out var renderers))
436 lods[index++] =
new LOD(lastPosition, renderers);
437 lastPosition *= 0.5f;
440 lods[index] =
new LOD(lastPosition,
null);
441 lodGroup.SetLODs(lods);
448 private static void SetupRootBone(AssetLoaderContext assetLoaderContext)
450 var bones =
new List<Transform>(assetLoaderContext.Models.Count);
451 assetLoaderContext.RootModel.GetBones(assetLoaderContext, bones);
452 assetLoaderContext.BoneTransforms = bones.ToArray();
453 if (assetLoaderContext.Options.RootBoneMapper !=
null)
455 assetLoaderContext.RootBone = assetLoaderContext.Options.RootBoneMapper.Map(assetLoaderContext);
461 private static void BuildGameObjectsPaths(AssetLoaderContext assetLoaderContext)
463 using (var values = assetLoaderContext.GameObjects.Values.GetEnumerator())
465 while (values.MoveNext())
467 assetLoaderContext.GameObjectPaths.Add(values.Current, values.Current.transform.BuildPath(assetLoaderContext.RootGameObject.transform));
474 private static void SetupRig(AssetLoaderContext assetLoaderContext)
476 var animations = assetLoaderContext.RootModel.AllAnimations;
477 AnimationClip[] animationClips =
null;
478 switch (assetLoaderContext.Options.AnimationType)
480 case AnimationType.Legacy:
482 if (animations !=
null)
484 animationClips =
new AnimationClip[animations.Count];
485 var unityAnimation = assetLoaderContext.RootGameObject.AddComponent<Animation>();
486 for (var i = 0; i < animations.Count; i++)
488 var triLibAnimation = animations[i];
489 var animationClip = ParseAnimation(assetLoaderContext, triLibAnimation);
490 unityAnimation.AddClip(animationClip, animationClip.name);
491 unityAnimation.clip = animationClip;
492 unityAnimation.wrapMode = assetLoaderContext.Options.AnimationWrapMode;
493 animationClips[i] = animationClip;
494 assetLoaderContext.Reader.UpdateLoadingPercentage(i, assetLoaderContext.Reader.LoadingStepsCount + ProcessingAnimationsStep, animations.Count);
499 case AnimationType.Generic:
501 var animator = assetLoaderContext.RootGameObject.AddComponent<Animator>();
502 if (assetLoaderContext.Options.AvatarDefinition == AvatarDefinitionType.CopyFromOtherAvatar)
504 animator.avatar = assetLoaderContext.Options.Avatar;
508 SetupGenericAvatar(assetLoaderContext, animator);
510 if (animations !=
null)
512 animationClips =
new AnimationClip[animations.Count];
513 for (var i = 0; i < animations.Count; i++)
515 var triLibAnimation = animations[i];
516 var animationClip = ParseAnimation(assetLoaderContext, triLibAnimation);
517 animationClips[i] = animationClip;
518 assetLoaderContext.Reader.UpdateLoadingPercentage(i, assetLoaderContext.Reader.LoadingStepsCount + ProcessingAnimationsStep, animations.Count);
523 case AnimationType.Humanoid:
525 var animator = assetLoaderContext.RootGameObject.AddComponent<Animator>();
526 if (assetLoaderContext.Options.AvatarDefinition == AvatarDefinitionType.CopyFromOtherAvatar)
528 animator.avatar = assetLoaderContext.Options.Avatar;
530 else if (assetLoaderContext.Options.HumanoidAvatarMapper !=
null)
532 SetupHumanoidAvatar(assetLoaderContext, animator);
534 if (animations !=
null)
536 animationClips =
new AnimationClip[animations.Count];
537 for (var i = 0; i < animations.Count; i++)
539 var originalAnimation = animations[i];
540 var animationClip = ParseAnimation(assetLoaderContext, originalAnimation);
541 animationClips[i] = animationClip;
542 assetLoaderContext.Reader.UpdateLoadingPercentage(i, assetLoaderContext.Reader.LoadingStepsCount + ProcessingAnimationsStep, animations.Count);
548 if (animationClips !=
null)
550 if (assetLoaderContext.Options.AnimationClipMappers !=
null)
552 for (var i = 0; i < assetLoaderContext.Options.AnimationClipMappers.Length; i++)
554 var animationClipMapper = assetLoaderContext.Options.AnimationClipMappers[i];
555 if (animationClipMapper ==
null)
560 animationClips = animationClipMapper.MapArray(assetLoaderContext, animationClips);
564 for (var i = 0; i < animationClips.Length; i++)
566 var animationClip = animationClips[i];
567 assetLoaderContext.Allocations.Add(animationClip);
575 private static SkeletonBone CreateSkeletonBone(Transform boneTransform)
577 var skeletonBone =
new SkeletonBone
579 name = boneTransform.name,
580 position = boneTransform.localPosition,
581 rotation = boneTransform.localRotation,
582 scale = boneTransform.localScale
591 private static HumanBone CreateHumanBone(BoneMapping boneMapping,
string boneName)
593 var humanBone =
new HumanBone
596 humanName = GetHumanBodyName(boneMapping.HumanBone),
599 useDefaultValues = boneMapping.HumanLimit.useDefaultValues,
600 axisLength = boneMapping.HumanLimit.axisLength,
601 center = boneMapping.HumanLimit.center,
602 max = boneMapping.HumanLimit.max,
603 min = boneMapping.HumanLimit.min
612 private static string GetHumanBodyName(HumanBodyBones humanBodyBones)
614 return HumanTrait.BoneName[(int)humanBodyBones];
620 private static void SetupGenericAvatar(AssetLoaderContext assetLoaderContext, Animator animator)
622 var parent = assetLoaderContext.RootGameObject.transform.parent;
623 assetLoaderContext.RootGameObject.transform.SetParent(
null,
true);
624 var avatar = AvatarBuilder.BuildGenericAvatar(assetLoaderContext.RootGameObject, assetLoaderContext.RootBone !=
null ? assetLoaderContext.RootBone.name :
"");
625 avatar.name = $
"{assetLoaderContext.RootGameObject.name}Avatar";
626 animator.avatar = avatar;
627 assetLoaderContext.RootGameObject.transform.SetParent(parent,
true);
633 private static void SetupHumanoidAvatar(AssetLoaderContext assetLoaderContext, Animator animator)
635 var mapping = assetLoaderContext.Options.HumanoidAvatarMapper.Map(assetLoaderContext);
636 if (mapping.Count > 0)
638 var parent = assetLoaderContext.RootGameObject.transform.parent;
639 var rootGameObjectPosition = assetLoaderContext.RootGameObject.transform.position;
640 assetLoaderContext.RootGameObject.transform.SetParent(
null,
false);
641 assetLoaderContext.Options.HumanoidAvatarMapper.PostSetup(assetLoaderContext, mapping);
642 Transform hipsTransform =
null;
643 var humanBones =
new HumanBone[mapping.Count];
645 using (var keys = mapping.Keys.GetEnumerator())
646 using (var values = mapping.Values.GetEnumerator())
648 while (keys.MoveNext() && values.MoveNext())
650 if (keys.Current.HumanBone == HumanBodyBones.Hips)
652 hipsTransform = values.Current;
654 humanBones[boneIndex++] = CreateHumanBone(keys.Current, values.Current.name);
657 if (hipsTransform !=
null)
659 var hipsRotation = hipsTransform.rotation;
660 var skeletonBones =
new Dictionary<Transform, SkeletonBone>();
661 var extraTransforms =
new List<Transform>();
662 var parentTransform = hipsTransform.parent;
663 while (parentTransform !=
null)
665 extraTransforms.Add(parentTransform);
666 parentTransform = parentTransform.parent;
668 for (var i = extraTransforms.Count - 1; i >= 0; i--)
670 var extraTransform = extraTransforms[i];
671 if (!skeletonBones.ContainsKey(extraTransform))
673 extraTransform.up = Vector3.up;
674 extraTransform.forward = Vector3.forward;
675 skeletonBones.Add(extraTransform, CreateSkeletonBone(extraTransform));
678 var hipsRotationOffset = hipsRotation * Quaternion.Inverse(hipsTransform.rotation);
679 hipsTransform.rotation = hipsRotationOffset * hipsTransform.rotation;
680 hipsTransform.position = hipsRotationOffset * hipsTransform.position;
681 using (var keys = assetLoaderContext.GameObjects.Keys.GetEnumerator())
682 using (var values = assetLoaderContext.GameObjects.Values.GetEnumerator())
683 while (keys.MoveNext() && values.MoveNext())
685 if (keys.Current.IsBone)
687 if (!skeletonBones.ContainsKey(values.Current.transform))
689 skeletonBones.Add(values.Current.transform, CreateSkeletonBone(values.Current.transform));
693 var triLibHumanDescription = assetLoaderContext.Options.HumanDescription ??
new General.HumanDescription();
696 armStretch = triLibHumanDescription.armStretch,
697 feetSpacing = triLibHumanDescription.feetSpacing,
698 hasTranslationDoF = triLibHumanDescription.hasTranslationDof,
699 legStretch = triLibHumanDescription.legStretch,
700 lowerArmTwist = triLibHumanDescription.lowerArmTwist,
701 lowerLegTwist = triLibHumanDescription.lowerLegTwist,
702 upperArmTwist = triLibHumanDescription.upperArmTwist,
703 upperLegTwist = triLibHumanDescription.upperLegTwist,
704 skeleton = skeletonBones.Values.ToArray(),
707 var avatar = AvatarBuilder.BuildHumanAvatar(assetLoaderContext.RootGameObject, humanDescription);
708 avatar.name = $
"{assetLoaderContext.RootGameObject.name}Avatar";
709 animator.avatar = avatar;
711 assetLoaderContext.RootGameObject.transform.SetParent(parent,
false);
712 assetLoaderContext.RootGameObject.transform.position = rootGameObjectPosition;
722 private static void ParseModel(AssetLoaderContext assetLoaderContext, Transform parentTransform, IRootModel rootModel, IModel model, out GameObject newGameObject)
724 newGameObject =
new GameObject(model.Name);
725 assetLoaderContext.GameObjects.Add(model, newGameObject);
726 assetLoaderContext.Models.Add(newGameObject, model);
727 newGameObject.transform.parent = parentTransform;
728 newGameObject.transform.localPosition = model.LocalPosition;
729 newGameObject.transform.localRotation = model.LocalRotation;
730 newGameObject.transform.localScale = model.LocalScale;
731 if (model.GeometryGroup !=
null)
733 ParseGeometry(assetLoaderContext, newGameObject, rootModel, model);
735 if (model.Children !=
null && model.Children.Count > 0)
737 for (var i = 0; i < model.Children.Count; i++)
739 var child = model.Children[i];
740 ParseModel(assetLoaderContext, newGameObject.transform, rootModel, child, out _);
748 private static void SetupModelBones(AssetLoaderContext assetLoaderContext, IModel model)
750 var loadedGameObject = assetLoaderContext.GameObjects[model];
751 var skinnedMeshRenderer = loadedGameObject.GetComponent<SkinnedMeshRenderer>();
752 if (skinnedMeshRenderer !=
null)
754 var bones = model.Bones;
755 if (bones !=
null && bones.Count > 0)
758 var gameObjectBones =
new Transform[bones.Count];
759 for (var i = 0; i < bones.Count; i++)
762 gameObjectBones[boneIndex++] = assetLoaderContext.GameObjects[bone].transform;
764 skinnedMeshRenderer.bones = gameObjectBones;
765 skinnedMeshRenderer.rootBone = assetLoaderContext.RootBone;
768 if (model.Children !=
null && model.Children.Count > 0)
770 for (var i = 0; i < model.Children.Count; i++)
772 var subModel = model.Children[i];
773 SetupModelBones(assetLoaderContext, subModel);
782 private static AnimationClip ParseAnimation(AssetLoaderContext assetLoaderContext, IAnimation animation)
784 var animationClip =
new AnimationClip { name = animation.Name, legacy = assetLoaderContext.Options.AnimationType == AnimationType.Legacy, frameRate = animation.FrameRate };
785 var animationCurveBindings = animation.AnimationCurveBindings;
786 if (animationCurveBindings ==
null)
788 return animationClip;
790 var rootModel = assetLoaderContext.RootBone !=
null ? assetLoaderContext.Models[assetLoaderContext.RootBone.gameObject] :
null;
791 for (var i = 0; i < animationCurveBindings.Count; i++)
793 var animationCurveBinding = animationCurveBindings[i];
794 var animationCurves = animationCurveBinding.AnimationCurves;
795 var gameObject = assetLoaderContext.GameObjects[animationCurveBinding.Model];
796 for (var j = 0; j < animationCurves.Count; j++)
798 var animationCurve = animationCurves[j];
799 var unityAnimationCurve = animationCurve.AnimationCurve;
800 var gameObjectPath = assetLoaderContext.GameObjectPaths[gameObject];
801 var propertyName = animationCurve.Property;
802 var propertyType = animationCurve.Property.StartsWith(
"blendShape.") ? typeof(SkinnedMeshRenderer) : typeof(Transform);
803 if (assetLoaderContext.Options.AnimationType == AnimationType.Generic)
805 TryToRemapGenericCurve(rootModel, animationCurve, animationCurveBinding, ref gameObjectPath, ref propertyName, ref propertyType);
807 animationClip.SetCurve(gameObjectPath, propertyType, propertyName, unityAnimationCurve);
810 if (assetLoaderContext.Options.EnsureQuaternionContinuity)
812 animationClip.EnsureQuaternionContinuity();
814 return animationClip;
824 private static void TryToRemapGenericCurve(IModel rootBone, IAnimationCurve animationCurve, IAnimationCurveBinding animationCurveBinding, ref
string gameObjectPath, ref
string propertyName, ref Type propertyType)
826 if (animationCurveBinding.Model == rootBone)
829 switch (animationCurve.Property)
831 case Constants.LocalPositionXProperty:
832 propertyName = Constants.RootPositionXProperty;
835 case Constants.LocalPositionYProperty:
836 propertyName = Constants.RootPositionYProperty;
839 case Constants.LocalPositionZProperty:
840 propertyName = Constants.RootPositionZProperty;
843 case Constants.LocalRotationXProperty:
844 propertyName = Constants.RootRotationXProperty;
847 case Constants.LocalRotationYProperty:
848 propertyName = Constants.RootRotationYProperty;
851 case Constants.LocalRotationZProperty:
852 propertyName = Constants.RootRotationZProperty;
855 case Constants.LocalRotationWProperty:
856 propertyName = Constants.RootRotationWProperty;
863 propertyType = typeof(Animator);
873 private static void ParseGeometry(AssetLoaderContext assetLoaderContext, GameObject meshGameObject, IRootModel rootModel, IModel meshModel)
875 var geometryGroup = meshModel.GeometryGroup;
876 if (geometryGroup.GeometriesData !=
null)
878 var mesh = geometryGroup.GenerateMesh(assetLoaderContext, meshModel.BindPoses);
879 assetLoaderContext.Allocations.Add(mesh);
880 if (assetLoaderContext.Options.ReadAndWriteEnabled)
884 if (assetLoaderContext.Options.LipSyncMappers !=
null)
886 for (var i = 0; i < assetLoaderContext.Options.LipSyncMappers.Length; i++)
888 var lipSyncMapper = assetLoaderContext.Options.LipSyncMappers[i];
889 if (lipSyncMapper ==
null)
893 if (lipSyncMapper.Map(assetLoaderContext, geometryGroup, out var visemeToBlendTargets))
895 var lipSyncMapping = meshGameObject.AddComponent<LipSyncMapping>();
896 lipSyncMapping.VisemeToBlendTargets = visemeToBlendTargets;
902 if (assetLoaderContext.Options.GenerateColliders)
904 if (assetLoaderContext.RootModel.AllAnimations !=
null && assetLoaderContext.RootModel.AllAnimations.Count > 0)
906 if (assetLoaderContext.Options.ShowLoadingWarnings)
908 Debug.LogWarning(
"Adding a MeshCollider to an animated object.");
910 var meshCollider = meshGameObject.AddComponent<MeshCollider>();
911 meshCollider.sharedMesh = mesh;
912 meshCollider.convex = assetLoaderContext.Options.ConvexColliders;
915 Renderer renderer =
null;
916 if (assetLoaderContext.Options.AnimationType != AnimationType.None || assetLoaderContext.Options.ImportBlendShapes)
918 var bones = meshModel.Bones;
919 var geometryGroupBlendShapeGeometryBindings = geometryGroup.BlendShapeKeys;
920 if (bones !=
null && bones.Count > 0 || geometryGroupBlendShapeGeometryBindings !=
null && geometryGroupBlendShapeGeometryBindings.Count > 0)
922 var skinnedMeshRenderer = meshGameObject.AddComponent<SkinnedMeshRenderer>();
923 skinnedMeshRenderer.sharedMesh = mesh;
924 skinnedMeshRenderer.enabled = !assetLoaderContext.Options.ImportVisibility || meshModel.Visibility;
925 renderer = skinnedMeshRenderer;
928 if (renderer ==
null)
930 var meshFilter = meshGameObject.AddComponent<MeshFilter>();
931 meshFilter.sharedMesh = mesh;
932 var meshRenderer = meshGameObject.AddComponent<MeshRenderer>();
933 meshRenderer.enabled = !assetLoaderContext.Options.ImportVisibility || meshModel.Visibility;
934 renderer = meshRenderer;
936 Material loadingMaterial =
null;
937 if (assetLoaderContext.Options.MaterialMappers !=
null)
939 for (var i = 0; i < assetLoaderContext.Options.MaterialMappers.Length; i++)
941 var mapper = assetLoaderContext.Options.MaterialMappers[i];
942 if (mapper !=
null && mapper.IsCompatible(
null))
944 loadingMaterial = mapper.LoadingMaterial;
949 var unityMaterials =
new Material[geometryGroup.GeometriesData.Count];
950 if (loadingMaterial ==
null)
952 if (assetLoaderContext.Options.ShowLoadingWarnings)
954 Debug.LogWarning(
"Could not find a suitable loading Material.");
959 for (var i = 0; i < unityMaterials.Length; i++)
961 unityMaterials[i] = loadingMaterial;
964 renderer.sharedMaterials = unityMaterials;
965 var materialIndices = meshModel.MaterialIndices;
966 foreach (var geometryData
in geometryGroup.GeometriesData)
968 var geometry = geometryData.Value;
969 if (geometry ==
null)
973 var geometryIndex = geometry.Index;
979 var materialIndex = materialIndices[geometryIndex];
980 if (materialIndex < 0 || materialIndex >= rootModel.AllMaterials.Count)
984 var sourceMaterial = rootModel.AllMaterials[materialIndex];
985 if (sourceMaterial ==
null)
989 if (geometryIndex < 0 || geometryIndex >= renderer.sharedMaterials.Length)
993 var materialRenderersContext =
new MaterialRendererContext
995 Context = assetLoaderContext,
997 GeometryIndex = geometryIndex,
998 Material = sourceMaterial
1000 if (assetLoaderContext.MaterialRenderers.TryGetValue(sourceMaterial, out var materialRendererContextList))
1002 materialRendererContextList.Add(materialRenderersContext);
1006 assetLoaderContext.MaterialRenderers.Add(sourceMaterial,
new List<MaterialRendererContext> { materialRenderersContext });
1014 private static void LoadModel(AssetLoaderContext assetLoaderContext)
1016 if (assetLoaderContext.Stream ==
null &&
string.IsNullOrWhiteSpace(assetLoaderContext.Filename))
1018 throw new Exception(
"TriLib is unable to load the given file.");
1020 if (assetLoaderContext.Options.MaterialMappers !=
null)
1022 Array.Sort(assetLoaderContext.Options.MaterialMappers, (a, b) => a.CheckingOrder > b.CheckingOrder ? -1 : 1);
1026 if (assetLoaderContext.Options.ShowLoadingWarnings)
1028 Debug.LogWarning(
"Your AssetLoaderOptions instance has no MaterialMappers. TriLib can't process materials without them.");
1032 GltfReader.DracoDecompressorCallback = DracoMeshLoader.DracoDecompressorCallback;
1034 var fileExtension = assetLoaderContext.FileExtension ?? FileUtils.GetFileExtension(assetLoaderContext.Filename,
false);
1035 if (assetLoaderContext.Stream ==
null)
1037 var fileStream =
new FileStream(assetLoaderContext.Filename,
FileMode.Open, FileAccess.Read, FileShare.Read);
1038 assetLoaderContext.Stream = fileStream;
1039 var reader = Readers.FindReaderForExtension(fileExtension);
1042 reader.ExtraStepsCount = ExtraStepsCount;
1043 assetLoaderContext.RootModel = reader.ReadStream(fileStream, assetLoaderContext, assetLoaderContext.Filename, assetLoaderContext.OnProgress);
1048 var reader = Readers.FindReaderForExtension(fileExtension);
1051 reader.ExtraStepsCount = ExtraStepsCount;
1052 assetLoaderContext.RootModel = reader.ReadStream(assetLoaderContext.Stream, assetLoaderContext, assetLoaderContext.Filename, assetLoaderContext.OnProgress);
1056 throw new Exception(
"Could not find a suitable reader for the given model. Please fill the 'fileExtension' parameter when calling any model loading method.");
1063 private static void ProcessRootModel(AssetLoaderContext assetLoaderContext)
1065 ProcessModel(assetLoaderContext);
1066 if (assetLoaderContext.Async)
1068 ThreadUtils.RunThread(assetLoaderContext, ref assetLoaderContext.CancellationToken, ProcessTextures,
null, assetLoaderContext.HandleError ?? assetLoaderContext.OnError, assetLoaderContext.Options.Timeout);
1072 ProcessTextures(assetLoaderContext);
1080 private static void TryProcessMaterials(AssetLoaderContext assetLoaderContext)
1082 var allMaterialsLoaded = assetLoaderContext.RootModel?.AllTextures ==
null || assetLoaderContext.LoadedTexturesCount == assetLoaderContext.RootModel.AllTextures.Count;
1083 if (!assetLoaderContext.MaterialsProcessed && allMaterialsLoaded)
1085 assetLoaderContext.MaterialsProcessed =
true;
1086 if (assetLoaderContext.RootModel?.AllMaterials !=
null)
1088 ProcessMaterialRenderers(assetLoaderContext);
1090 if (assetLoaderContext.Options.AddAssetUnloader && assetLoaderContext.RootGameObject !=
null || assetLoaderContext.WrapperGameObject !=
null)
1092 var gameObject = assetLoaderContext.RootGameObject ?? assetLoaderContext.WrapperGameObject;
1093 var assetUnloader = gameObject.AddComponent<AssetUnloader>();
1094 assetUnloader.Id = AssetUnloader.GetNextId();
1095 assetUnloader.Allocations = assetLoaderContext.Allocations;
1097 foreach (var allocation
in assetLoaderContext.Allocations)
1099 if (allocation is Texture2D texture2D)
1101 TextureUtils.FinishTexture2D(
new TextureLoadingContext {UnityTexture = texture2D, Context = assetLoaderContext});
1104 assetLoaderContext.Reader.UpdateLoadingPercentage(1f, assetLoaderContext.Reader.LoadingStepsCount + FinalStep);
1105 assetLoaderContext.OnMaterialsLoad?.Invoke(assetLoaderContext);
1106 Cleanup(assetLoaderContext);
1114 private static void ProcessTextures(AssetLoaderContext assetLoaderContext)
1116 if (assetLoaderContext.RootModel?.AllTextures !=
null)
1118 for (var i = 0; i < assetLoaderContext.RootModel.AllTextures.Count; i++)
1120 var texture = assetLoaderContext.RootModel.AllTextures[i];
1121 TextureLoadingContext textureLoadingContext =
null;
1122 if (assetLoaderContext.Options.TextureMapper !=
null)
1124 textureLoadingContext = assetLoaderContext.Options.TextureMapper.Map(assetLoaderContext, texture);
1126 if (textureLoadingContext ==
null)
1128 textureLoadingContext =
new TextureLoadingContext
1130 Context = assetLoaderContext,
1134 StbImage.LoadTexture(textureLoadingContext);
1135 assetLoaderContext.Reader.UpdateLoadingPercentage(i, assetLoaderContext.Reader.LoadingStepsCount + ProcessingTexturesStep, assetLoaderContext.RootModel.AllTextures.Count);
1138 if (assetLoaderContext.Async)
1140 Dispatcher.InvokeAsync(
new ContextualizedAction<AssetLoaderContext>(TryProcessMaterials, assetLoaderContext));
1144 TryProcessMaterials(assetLoaderContext);
1152 private static void ProcessMaterialRenderers(AssetLoaderContext assetLoaderContext)
1154 if (assetLoaderContext.Options.MaterialMappers !=
null)
1156 for (var i = 0; i < assetLoaderContext.RootModel.AllMaterials.Count; i++)
1158 var material = assetLoaderContext.RootModel.AllMaterials[i];
1159 MaterialMapper usedMaterialMapper =
null;
1160 var materialMapperContext =
new MaterialMapperContext
1162 Context = assetLoaderContext,
1165 for (var j = 0; j < assetLoaderContext.Options.MaterialMappers.Length; j++)
1167 var materialMapper = assetLoaderContext.Options.MaterialMappers[j];
1168 if (materialMapper ==
null || !materialMapper.IsCompatible(materialMapperContext))
1173 materialMapper.Map(materialMapperContext);
1174 usedMaterialMapper = materialMapper;
1178 if (usedMaterialMapper !=
null)
1180 if (assetLoaderContext.MaterialRenderers.TryGetValue(material, out var materialRendererList))
1182 for (var j = 0; j < materialRendererList.Count; j++)
1184 var materialRendererContext = materialRendererList[j];
1185 usedMaterialMapper.ApplyMaterialToRenderer(materialRendererContext, materialMapperContext);
1189 assetLoaderContext.Reader.UpdateLoadingPercentage(i, assetLoaderContext.Reader.LoadingStepsCount + ProcessingRenderersStep, assetLoaderContext.RootModel.AllMaterials.Count);
1192 else if (assetLoaderContext.Options.ShowLoadingWarnings)
1194 Debug.LogWarning(
"The given AssetLoaderOptions contains no MaterialMapper. Materials will not be created.");
1200 private static void HandleError(IContextualizedError error)
1202 var exception = error.GetInnerException();
1203 if (error.GetContext() is IAssetLoaderContext iassetLoaderContext)
1205 var assetLoaderContext = iassetLoaderContext.Context;
1206 if (assetLoaderContext !=
null)
1208 Cleanup(assetLoaderContext);
1209 if (assetLoaderContext.Options.DestroyOnError && assetLoaderContext.RootGameObject !=
null)
1212 Object.DestroyImmediate(assetLoaderContext.RootGameObject);
1214 Object.Destroy(assetLoaderContext.RootGameObject);
1216 assetLoaderContext.RootGameObject =
null;
1218 if (assetLoaderContext.OnError !=
null)
1220 if (assetLoaderContext.Async)
1222 Dispatcher.InvokeAsync(
new ContextualizedAction<IContextualizedError>(assetLoaderContext.OnError, error));
1226 assetLoaderContext.OnError(error);
1233 var contextualizedAction =
new ContextualizedAction<ContextualizedError<object>>(Rethrow,
new ContextualizedError<object>(exception,
null));
1234 Dispatcher.InvokeAsync(contextualizedAction);
1243 private static void Cleanup(AssetLoaderContext assetLoaderContext)
1245 if (assetLoaderContext.Stream !=
null && assetLoaderContext.Options.CloseStreamAutomatically)
1249 assetLoaderContext.Stream.Dispose();
1256 if (Application.isPlaying)
1258 GCHelper.GetInstance().UnRegisterLoading(assetLoaderContext.Options.GCHelperCollectionInterval);
1265 private static void Rethrow<T>(ContextualizedError<T> contextualizedError)
1267 throw contextualizedError;
UnityEngine.HumanDescription HumanDescription
System.IO.FileMode FileMode
TriLibCore.AssetLoaderOptions AssetLoaderOptions
Represents a Mapper that searches for a root bone on the Models by the bone names.