Tanoda
AssetLoader.cs
Go to the documentation of this file.
1#pragma warning disable 168
2using System;
3using System.Collections.Generic;
4using System.IO;
5using System.Linq;
6using System.Text.RegularExpressions;
7using StbImageSharp;
8using TriLibCore.Extensions;
9using TriLibCore.General;
10using TriLibCore.Interfaces;
12using TriLibCore.Utils;
13using UnityEngine;
14using FileMode = System.IO.FileMode;
15using HumanDescription = UnityEngine.HumanDescription;
16using Object = UnityEngine.Object;
17#if TRILIB_DRACO
18using TriLibCore.Gltf.Reader;
19using TriLibCore.Gltf.Draco;
20#endif
21#if UNITY_EDITOR
22using UnityEditor;
23#endif
24namespace TriLibCore
25{
27 public static class AssetLoader
28 {
32 private const int ProcessingAnimationsStep = 0;
33
37 private const int ProcessingTexturesStep = 1;
38
42 private const int ProcessingRenderersStep = 2;
43
47 private const int FinalStep = 3;
48
52 private const int ExtraStepsCount = 4;
53
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)
65 {
66#if UNITY_WEBGL || UNITY_UWP || TRILIB_FORCE_SYNC
67 AssetLoaderContext assetLoaderContext = null;
68 try
69 {
70 assetLoaderContext = LoadModelFromFileNoThread(path, onError, wrapperGameObject, assetLoaderOptions ?? CreateDefaultLoaderOptions(), customContextData);
71 onLoad?.Invoke(assetLoaderContext);
72 onMaterialsLoad?.Invoke(assetLoaderContext);
73 }
74 catch (Exception exception)
75 {
76 if (exception is IContextualizedError contextualizedError)
77 {
78 HandleError(contextualizedError);
79 }
80 else
81 {
82 HandleError(new ContextualizedError<AssetLoaderContext>(exception, null));
83 }
84 }
85 return assetLoaderContext;
86#else
87 var assetLoaderContext = new AssetLoaderContext
88 {
89 Options = assetLoaderOptions ?? CreateDefaultLoaderOptions(),
90 Filename = path,
91 BasePath = FileUtils.GetFileDirectory(path),
92 WrapperGameObject = wrapperGameObject,
93 OnMaterialsLoad = onMaterialsLoad,
94 OnLoad = onLoad,
95 OnProgress = onProgress,
96 HandleError = HandleError,
97 OnError = onError,
98 CustomData = customContextData,
99 };
100 if (assetLoaderContext.Options.ForceGCCollectionWhileLoading)
101 {
102 GCHelper.GetInstance()?.RegisterLoading();
103 }
104#if TRILIB_USE_THREAD_NAMES
105 var threadName = "TriLib_LoadModelFromFile";
106#else
107 var threadName = string.Empty;
108#endif
109 assetLoaderContext.Tasks.Add(ThreadUtils.RunThread(assetLoaderContext, ref assetLoaderContext.CancellationToken, LoadModel, ProcessRootModel, HandleError, assetLoaderContext.Options.Timeout, threadName));
110 return assetLoaderContext;
111#endif
112 }
113
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)
127 {
128#if UNITY_WEBGL || UNITY_UWP || TRILIB_FORCE_SYNC
129 AssetLoaderContext assetLoaderContext = null;
130 try
131 {
132 assetLoaderContext = LoadModelFromStreamNoThread(stream, filename, fileExtension, onError, wrapperGameObject, assetLoaderOptions ?? CreateDefaultLoaderOptions(), customContextData);
133 onLoad?.Invoke(assetLoaderContext);
134 onMaterialsLoad?.Invoke(assetLoaderContext);
135 }
136 catch (Exception exception)
137 {
138 if (exception is IContextualizedError contextualizedError)
139 {
140 HandleError(contextualizedError);
141 }
142 else
143 {
144 HandleError(new ContextualizedError<AssetLoaderContext>(exception, null));
145 }
146 }
147 return assetLoaderContext;
148#else
149 var assetLoaderContext = new AssetLoaderContext
150 {
151 Options = assetLoaderOptions ?? CreateDefaultLoaderOptions(),
152 Stream = stream,
153 Filename = filename,
154 FileExtension = fileExtension ?? FileUtils.GetFileExtension(filename, false),
155 BasePath = FileUtils.GetFileDirectory(filename),
156 WrapperGameObject = wrapperGameObject,
157 OnMaterialsLoad = onMaterialsLoad,
158 OnLoad = onLoad,
159 OnProgress = onProgress,
160 HandleError = HandleError,
161 OnError = onError,
162 CustomData = customContextData
163 };
164 if (assetLoaderContext.Options.ForceGCCollectionWhileLoading)
165 {
166 GCHelper.GetInstance()?.RegisterLoading();
167 }
168#if TRILIB_USE_THREAD_NAMES
169 var threadName = "TriLib_LoadModelFromStream";
170#else
171 var threadName = string.Empty;
172#endif
173 assetLoaderContext.Tasks.Add(ThreadUtils.RunThread(assetLoaderContext, ref assetLoaderContext.CancellationToken, LoadModel, ProcessRootModel, HandleError, assetLoaderContext.Options.Timeout, threadName));
174 return assetLoaderContext;
175#endif
176 }
177
185 public static AssetLoaderContext LoadModelFromFileNoThread(string path, Action<IContextualizedError> onError = null, GameObject wrapperGameObject = null, AssetLoaderOptions assetLoaderOptions = null, object customContextData = null)
186 {
187 var assetLoaderContext = new AssetLoaderContext
188 {
189 Options = assetLoaderOptions ?? CreateDefaultLoaderOptions(),
190 Filename = path,
191 BasePath = FileUtils.GetFileDirectory(path),
192 CustomData = customContextData,
193 HandleError = HandleError,
194 OnError = onError,
195 WrapperGameObject = wrapperGameObject,
196 Async = false
197 };
198 try
199 {
200 if (assetLoaderContext.Options.ForceGCCollectionWhileLoading)
201 {
202 GCHelper.GetInstance()?.RegisterLoading();
203 }
204 LoadModel(assetLoaderContext);
205 if (assetLoaderContext.Reader != null)
206 {
207 ProcessRootModel(assetLoaderContext);
208 }
209 }
210 catch (Exception exception)
211 {
212 HandleError(new ContextualizedError<AssetLoaderContext>(exception, assetLoaderContext));
213 }
214 return assetLoaderContext;
215 }
216
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)
227 {
228 var assetLoaderContext = new AssetLoaderContext
229 {
230 Options = assetLoaderOptions ?? CreateDefaultLoaderOptions(),
231 Stream = stream,
232 Filename = filename,
233 FileExtension = fileExtension ?? FileUtils.GetFileExtension(filename, false),
234 BasePath = FileUtils.GetFileDirectory(filename),
235 CustomData = customContextData,
236 HandleError = HandleError,
237 OnError = onError,
238 WrapperGameObject = wrapperGameObject,
239 Async = false
240 };
241 try
242 {
243 if (assetLoaderContext.Options.ForceGCCollectionWhileLoading)
244 {
245 GCHelper.GetInstance()?.RegisterLoading();
246 }
247 LoadModel(assetLoaderContext);
248 if (assetLoaderContext.Reader != null)
249 {
250 ProcessRootModel(assetLoaderContext);
251 }
252 }
253 catch (Exception exception)
254 {
255 HandleError(new ContextualizedError<AssetLoaderContext>(exception, assetLoaderContext));
256 }
257 return assetLoaderContext;
258 }
259
260#if UNITY_EDITOR
261 private static Object LoadOrCreateScriptableObject(string type, string subFolder)
262 {
263 string mappersFilePath;
264 var triLibMapperAssets = AssetDatabase.FindAssets("TriLibMappersPlaceholder");
265 if (triLibMapperAssets.Length > 0)
266 {
267 mappersFilePath = AssetDatabase.GUIDToAssetPath(triLibMapperAssets[0]);
268 }
269 else
270 {
271 throw new Exception("Could not find \"TriLibMappersPlaceholder\" file. Please re-import TriLib package.");
272 }
273 var mappersDirectory = $"{FileUtils.GetFileDirectory(mappersFilePath)}";
274 var assetDirectory = $"{mappersDirectory}/{subFolder}";
275 if (!AssetDatabase.IsValidFolder(assetDirectory))
276 {
277 AssetDatabase.CreateFolder(mappersDirectory, subFolder);
278 }
279 var assetPath = $"{assetDirectory}/{type}.asset";
280 var scriptableObject = AssetDatabase.LoadAssetAtPath(assetPath, typeof(Object));
281 if (scriptableObject == null)
282 {
283 scriptableObject = ScriptableObject.CreateInstance(type);
284 AssetDatabase.CreateAsset(scriptableObject, assetPath);
285 }
286 return scriptableObject;
287 }
288#endif
289
293 public static AssetLoaderOptions CreateDefaultLoaderOptions(bool generateAssets = false)
294 {
295 var assetLoaderOptions = ScriptableObject.CreateInstance<AssetLoaderOptions>();
296 ByNameRootBoneMapper byNameRootBoneMapper;
297#if UNITY_EDITOR
298 if (generateAssets)
299 {
300 byNameRootBoneMapper = (ByNameRootBoneMapper)LoadOrCreateScriptableObject("ByNameRootBoneMapper", "RootBone");
301 }
302 else
303 {
304 byNameRootBoneMapper = ScriptableObject.CreateInstance<ByNameRootBoneMapper>();
305 }
306#else
307 byNameRootBoneMapper = ScriptableObject.CreateInstance<ByNameRootBoneMapper>();
308#endif
309 byNameRootBoneMapper.name = "ByNameRootBoneMapper";
310 assetLoaderOptions.RootBoneMapper = byNameRootBoneMapper;
311 if (MaterialMapper.RegisteredMappers.Count == 0)
312 {
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.");
314 }
315 else
316 {
317 var materialMappers = new List<MaterialMapper>();
318 foreach (var materialMapperName in MaterialMapper.RegisteredMappers)
319 {
320 if (materialMapperName == null)
321 {
322 continue;
323 }
324 MaterialMapper materialMapper;
325#if UNITY_EDITOR
326 if (generateAssets)
327 {
328 materialMapper = (MaterialMapper)LoadOrCreateScriptableObject(materialMapperName, "Material");
329 }
330 else
331 {
332 materialMapper = (MaterialMapper)ScriptableObject.CreateInstance(materialMapperName);
333 }
334#else
335 materialMapper = ScriptableObject.CreateInstance(materialMapperName) as MaterialMapper;
336#endif
337 if (materialMapper != null)
338 {
339 materialMapper.name = materialMapperName;
340 if (materialMapper.IsCompatible(null))
341 {
342 materialMappers.Add(materialMapper);
343 assetLoaderOptions.FixedAllocations.Add(materialMapper);
344 }
345 else
346 {
347#if UNITY_EDITOR
348 var assetPath = AssetDatabase.GetAssetPath(materialMapper);
349 if (assetPath == null)
350 {
351 Object.DestroyImmediate(materialMapper);
352 }
353#else
354 Object.Destroy(materialMapper);
355#endif
356 }
357 }
358 }
359
360 if (materialMappers.Count == 0)
361 {
362 Debug.LogWarning("TriLib could not find any suitable MaterialMapper on the project.");
363 }
364 else
365 {
366 assetLoaderOptions.MaterialMappers = materialMappers.ToArray();
367 }
368 }
369 assetLoaderOptions.FixedAllocations.Add(assetLoaderOptions);
370 return assetLoaderOptions;
371 }
372
375 private static void ProcessModel(AssetLoaderContext assetLoaderContext)
376 {
377 if (assetLoaderContext.RootModel != null)
378 {
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)
382 {
383 SetupRootBone(assetLoaderContext);
384 SetupModelBones(assetLoaderContext, assetLoaderContext.RootModel);
385 SetupModelLod(assetLoaderContext, assetLoaderContext.RootModel);
386 BuildGameObjectsPaths(assetLoaderContext);
387 SetupRig(assetLoaderContext);
388 }
389 if (assetLoaderContext.Options.Static)
390 {
391 assetLoaderContext.RootGameObject.isStatic = true;
392 }
393 }
394 assetLoaderContext.OnLoad?.Invoke(assetLoaderContext);
395 }
396
400 private static void SetupModelLod(AssetLoaderContext assetLoaderContext, IModel model)
401 {
402 if (model.Children != null && model.Children.Count > 0)
403 {
404 var lodModels = new Dictionary<int, Renderer[]>(model.Children.Count);
405 var minLod = int.MaxValue;
406 var maxLod = 0;
407 for (var i = 0; i < model.Children.Count; i++)
408 {
409 var child = model.Children[i];
410 var match = Regex.Match(child.Name, "_LOD(?<number>[0-9]+)|LOD_(?<number>[0-9]+)");
411 if (match.Success)
412 {
413 var lodNumber = Convert.ToInt32(match.Groups["number"].Value);
414 if (lodModels.ContainsKey(lodNumber))
415 {
416 continue;
417 }
418
419 minLod = Mathf.Min(lodNumber, minLod);
420 maxLod = Mathf.Max(lodNumber, maxLod);
421 lodModels.Add(lodNumber, assetLoaderContext.GameObjects[child].GetComponentsInChildren<Renderer>());
422 }
423 }
424
425 if (lodModels.Count > 1)
426 {
427 var newGameObject = assetLoaderContext.GameObjects[model];
428 var lods = new LOD[lodModels.Count + 1];
429 var lodGroup = newGameObject.AddComponent<LODGroup>();
430 var index = 0;
431 var lastPosition = 1f;
432 for (var i = minLod; i <= maxLod; i++)
433 {
434 if (lodModels.TryGetValue(i, out var renderers))
435 {
436 lods[index++] = new LOD(lastPosition, renderers);
437 lastPosition *= 0.5f;
438 }
439 }
440 lods[index] = new LOD(lastPosition, null);
441 lodGroup.SetLODs(lods);
442 }
443 }
444 }
445
448 private static void SetupRootBone(AssetLoaderContext assetLoaderContext)
449 {
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)
454 {
455 assetLoaderContext.RootBone = assetLoaderContext.Options.RootBoneMapper.Map(assetLoaderContext);
456 }
457 }
458
461 private static void BuildGameObjectsPaths(AssetLoaderContext assetLoaderContext)
462 {
463 using (var values = assetLoaderContext.GameObjects.Values.GetEnumerator())
464 {
465 while (values.MoveNext())
466 {
467 assetLoaderContext.GameObjectPaths.Add(values.Current, values.Current.transform.BuildPath(assetLoaderContext.RootGameObject.transform));
468 }
469 }
470 }
471
474 private static void SetupRig(AssetLoaderContext assetLoaderContext)
475 {
476 var animations = assetLoaderContext.RootModel.AllAnimations;
477 AnimationClip[] animationClips = null;
478 switch (assetLoaderContext.Options.AnimationType)
479 {
480 case AnimationType.Legacy:
481 {
482 if (animations != null)
483 {
484 animationClips = new AnimationClip[animations.Count];
485 var unityAnimation = assetLoaderContext.RootGameObject.AddComponent<Animation>();
486 for (var i = 0; i < animations.Count; i++)
487 {
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);
495 }
496 }
497 break;
498 }
499 case AnimationType.Generic:
500 {
501 var animator = assetLoaderContext.RootGameObject.AddComponent<Animator>();
502 if (assetLoaderContext.Options.AvatarDefinition == AvatarDefinitionType.CopyFromOtherAvatar)
503 {
504 animator.avatar = assetLoaderContext.Options.Avatar;
505 }
506 else
507 {
508 SetupGenericAvatar(assetLoaderContext, animator);
509 }
510 if (animations != null)
511 {
512 animationClips = new AnimationClip[animations.Count];
513 for (var i = 0; i < animations.Count; i++)
514 {
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);
519 }
520 }
521 break;
522 }
523 case AnimationType.Humanoid:
524 {
525 var animator = assetLoaderContext.RootGameObject.AddComponent<Animator>();
526 if (assetLoaderContext.Options.AvatarDefinition == AvatarDefinitionType.CopyFromOtherAvatar)
527 {
528 animator.avatar = assetLoaderContext.Options.Avatar;
529 }
530 else if (assetLoaderContext.Options.HumanoidAvatarMapper != null)
531 {
532 SetupHumanoidAvatar(assetLoaderContext, animator);
533 }
534 if (animations != null)
535 {
536 animationClips = new AnimationClip[animations.Count];
537 for (var i = 0; i < animations.Count; i++)
538 {
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);
543 }
544 }
545 break;
546 }
547 }
548 if (animationClips != null)
549 {
550 if (assetLoaderContext.Options.AnimationClipMappers != null)
551 {
552 for (var i = 0; i < assetLoaderContext.Options.AnimationClipMappers.Length; i++)
553 {
554 var animationClipMapper = assetLoaderContext.Options.AnimationClipMappers[i];
555 if (animationClipMapper == null)
556 {
557 continue;
558 }
559
560 animationClips = animationClipMapper.MapArray(assetLoaderContext, animationClips);
561 }
562 }
563
564 for (var i = 0; i < animationClips.Length; i++)
565 {
566 var animationClip = animationClips[i];
567 assetLoaderContext.Allocations.Add(animationClip);
568 }
569 }
570 }
571
575 private static SkeletonBone CreateSkeletonBone(Transform boneTransform)
576 {
577 var skeletonBone = new SkeletonBone
578 {
579 name = boneTransform.name,
580 position = boneTransform.localPosition,
581 rotation = boneTransform.localRotation,
582 scale = boneTransform.localScale
583 };
584 return skeletonBone;
585 }
586
591 private static HumanBone CreateHumanBone(BoneMapping boneMapping, string boneName)
592 {
593 var humanBone = new HumanBone
594 {
595 boneName = boneName,
596 humanName = GetHumanBodyName(boneMapping.HumanBone),
597 limit =
598 {
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
604 }
605 };
606 return humanBone;
607 }
608
612 private static string GetHumanBodyName(HumanBodyBones humanBodyBones)
613 {
614 return HumanTrait.BoneName[(int)humanBodyBones];
615 }
616
620 private static void SetupGenericAvatar(AssetLoaderContext assetLoaderContext, Animator animator)
621 {
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);
628 }
629
633 private static void SetupHumanoidAvatar(AssetLoaderContext assetLoaderContext, Animator animator)
634 {
635 var mapping = assetLoaderContext.Options.HumanoidAvatarMapper.Map(assetLoaderContext);
636 if (mapping.Count > 0)
637 {
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];
644 var boneIndex = 0;
645 using (var keys = mapping.Keys.GetEnumerator())
646 using (var values = mapping.Values.GetEnumerator())
647 {
648 while (keys.MoveNext() && values.MoveNext())
649 {
650 if (keys.Current.HumanBone == HumanBodyBones.Hips)
651 {
652 hipsTransform = values.Current;
653 }
654 humanBones[boneIndex++] = CreateHumanBone(keys.Current, values.Current.name);
655 }
656 }
657 if (hipsTransform != null)
658 {
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)
664 {
665 extraTransforms.Add(parentTransform);
666 parentTransform = parentTransform.parent;
667 }
668 for (var i = extraTransforms.Count - 1; i >= 0; i--)
669 {
670 var extraTransform = extraTransforms[i];
671 if (!skeletonBones.ContainsKey(extraTransform))
672 {
673 extraTransform.up = Vector3.up;
674 extraTransform.forward = Vector3.forward;
675 skeletonBones.Add(extraTransform, CreateSkeletonBone(extraTransform));
676 }
677 }
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())
684 {
685 if (keys.Current.IsBone)
686 {
687 if (!skeletonBones.ContainsKey(values.Current.transform))
688 {
689 skeletonBones.Add(values.Current.transform, CreateSkeletonBone(values.Current.transform));
690 }
691 }
692 }
693 var triLibHumanDescription = assetLoaderContext.Options.HumanDescription ?? new General.HumanDescription();
694 var humanDescription = new HumanDescription
695 {
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(),
705 human = humanBones
706 };
707 var avatar = AvatarBuilder.BuildHumanAvatar(assetLoaderContext.RootGameObject, humanDescription);
708 avatar.name = $"{assetLoaderContext.RootGameObject.name}Avatar";
709 animator.avatar = avatar;
710 }
711 assetLoaderContext.RootGameObject.transform.SetParent(parent, false);
712 assetLoaderContext.RootGameObject.transform.position = rootGameObjectPosition;
713 }
714 }
715
722 private static void ParseModel(AssetLoaderContext assetLoaderContext, Transform parentTransform, IRootModel rootModel, IModel model, out GameObject newGameObject)
723 {
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)
732 {
733 ParseGeometry(assetLoaderContext, newGameObject, rootModel, model);
734 }
735 if (model.Children != null && model.Children.Count > 0)
736 {
737 for (var i = 0; i < model.Children.Count; i++)
738 {
739 var child = model.Children[i];
740 ParseModel(assetLoaderContext, newGameObject.transform, rootModel, child, out _);
741 }
742 }
743 }
744
748 private static void SetupModelBones(AssetLoaderContext assetLoaderContext, IModel model)
749 {
750 var loadedGameObject = assetLoaderContext.GameObjects[model];
751 var skinnedMeshRenderer = loadedGameObject.GetComponent<SkinnedMeshRenderer>();
752 if (skinnedMeshRenderer != null)
753 {
754 var bones = model.Bones;
755 if (bones != null && bones.Count > 0)
756 {
757 var boneIndex = 0;
758 var gameObjectBones = new Transform[bones.Count];
759 for (var i = 0; i < bones.Count; i++)
760 {
761 var bone = bones[i];
762 gameObjectBones[boneIndex++] = assetLoaderContext.GameObjects[bone].transform;
763 }
764 skinnedMeshRenderer.bones = gameObjectBones;
765 skinnedMeshRenderer.rootBone = assetLoaderContext.RootBone;
766 }
767 }
768 if (model.Children != null && model.Children.Count > 0)
769 {
770 for (var i = 0; i < model.Children.Count; i++)
771 {
772 var subModel = model.Children[i];
773 SetupModelBones(assetLoaderContext, subModel);
774 }
775 }
776 }
777
782 private static AnimationClip ParseAnimation(AssetLoaderContext assetLoaderContext, IAnimation animation)
783 {
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)
787 {
788 return animationClip;
789 }
790 var rootModel = assetLoaderContext.RootBone != null ? assetLoaderContext.Models[assetLoaderContext.RootBone.gameObject] : null;
791 for (var i = 0; i < animationCurveBindings.Count; i++)
792 {
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++)
797 {
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)
804 {
805 TryToRemapGenericCurve(rootModel, animationCurve, animationCurveBinding, ref gameObjectPath, ref propertyName, ref propertyType);
806 }
807 animationClip.SetCurve(gameObjectPath, propertyType, propertyName, unityAnimationCurve);
808 }
809 }
810 if (assetLoaderContext.Options.EnsureQuaternionContinuity)
811 {
812 animationClip.EnsureQuaternionContinuity();
813 }
814 return animationClip;
815 }
816
824 private static void TryToRemapGenericCurve(IModel rootBone, IAnimationCurve animationCurve, IAnimationCurveBinding animationCurveBinding, ref string gameObjectPath, ref string propertyName, ref Type propertyType)
825 {
826 if (animationCurveBinding.Model == rootBone)
827 {
828 var remap = false;
829 switch (animationCurve.Property)
830 {
831 case Constants.LocalPositionXProperty:
832 propertyName = Constants.RootPositionXProperty;
833 remap = true;
834 break;
835 case Constants.LocalPositionYProperty:
836 propertyName = Constants.RootPositionYProperty;
837 remap = true;
838 break;
839 case Constants.LocalPositionZProperty:
840 propertyName = Constants.RootPositionZProperty;
841 remap = true;
842 break;
843 case Constants.LocalRotationXProperty:
844 propertyName = Constants.RootRotationXProperty;
845 remap = true;
846 break;
847 case Constants.LocalRotationYProperty:
848 propertyName = Constants.RootRotationYProperty;
849 remap = true;
850 break;
851 case Constants.LocalRotationZProperty:
852 propertyName = Constants.RootRotationZProperty;
853 remap = true;
854 break;
855 case Constants.LocalRotationWProperty:
856 propertyName = Constants.RootRotationWProperty;
857 remap = true;
858 break;
859 }
860 if (remap)
861 {
862 gameObjectPath = "";
863 propertyType = typeof(Animator);
864 }
865 }
866 }
867
873 private static void ParseGeometry(AssetLoaderContext assetLoaderContext, GameObject meshGameObject, IRootModel rootModel, IModel meshModel)
874 {
875 var geometryGroup = meshModel.GeometryGroup;
876 if (geometryGroup.GeometriesData != null)
877 {
878 var mesh = geometryGroup.GenerateMesh(assetLoaderContext, meshModel.BindPoses);
879 assetLoaderContext.Allocations.Add(mesh);
880 if (assetLoaderContext.Options.ReadAndWriteEnabled)
881 {
882 mesh.MarkDynamic();
883 }
884 if (assetLoaderContext.Options.LipSyncMappers != null)
885 {
886 for (var i = 0; i < assetLoaderContext.Options.LipSyncMappers.Length; i++)
887 {
888 var lipSyncMapper = assetLoaderContext.Options.LipSyncMappers[i];
889 if (lipSyncMapper == null)
890 {
891 continue;
892 }
893 if (lipSyncMapper.Map(assetLoaderContext, geometryGroup, out var visemeToBlendTargets))
894 {
895 var lipSyncMapping = meshGameObject.AddComponent<LipSyncMapping>();
896 lipSyncMapping.VisemeToBlendTargets = visemeToBlendTargets;
897 break;
898 }
899 }
900 }
901 //geometries = geometryGroup.HasNewInterface ? geometryGroup.TriangleGeometries : mesh.CombineGeometries(geometryGroup, meshModel.BindPoses, assetLoaderContext);
902 if (assetLoaderContext.Options.GenerateColliders)
903 {
904 if (assetLoaderContext.RootModel.AllAnimations != null && assetLoaderContext.RootModel.AllAnimations.Count > 0)
905 {
906 if (assetLoaderContext.Options.ShowLoadingWarnings)
907 {
908 Debug.LogWarning("Adding a MeshCollider to an animated object.");
909 }
910 var meshCollider = meshGameObject.AddComponent<MeshCollider>();
911 meshCollider.sharedMesh = mesh;
912 meshCollider.convex = assetLoaderContext.Options.ConvexColliders;
913 }
914 }
915 Renderer renderer = null;
916 if (assetLoaderContext.Options.AnimationType != AnimationType.None || assetLoaderContext.Options.ImportBlendShapes)
917 {
918 var bones = meshModel.Bones;
919 var geometryGroupBlendShapeGeometryBindings = geometryGroup.BlendShapeKeys;
920 if (bones != null && bones.Count > 0 || geometryGroupBlendShapeGeometryBindings != null && geometryGroupBlendShapeGeometryBindings.Count > 0)
921 {
922 var skinnedMeshRenderer = meshGameObject.AddComponent<SkinnedMeshRenderer>();
923 skinnedMeshRenderer.sharedMesh = mesh;
924 skinnedMeshRenderer.enabled = !assetLoaderContext.Options.ImportVisibility || meshModel.Visibility;
925 renderer = skinnedMeshRenderer;
926 }
927 }
928 if (renderer == null)
929 {
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;
935 }
936 Material loadingMaterial = null;
937 if (assetLoaderContext.Options.MaterialMappers != null)
938 {
939 for (var i = 0; i < assetLoaderContext.Options.MaterialMappers.Length; i++)
940 {
941 var mapper = assetLoaderContext.Options.MaterialMappers[i];
942 if (mapper != null && mapper.IsCompatible(null))
943 {
944 loadingMaterial = mapper.LoadingMaterial;
945 break;
946 }
947 }
948 }
949 var unityMaterials = new Material[geometryGroup.GeometriesData.Count];
950 if (loadingMaterial == null)
951 {
952 if (assetLoaderContext.Options.ShowLoadingWarnings)
953 {
954 Debug.LogWarning("Could not find a suitable loading Material.");
955 }
956 }
957 else
958 {
959 for (var i = 0; i < unityMaterials.Length; i++)
960 {
961 unityMaterials[i] = loadingMaterial;
962 }
963 }
964 renderer.sharedMaterials = unityMaterials;
965 var materialIndices = meshModel.MaterialIndices;
966 foreach (var geometryData in geometryGroup.GeometriesData)
967 {
968 var geometry = geometryData.Value;
969 if (geometry == null)
970 {
971 continue;
972 }
973 var geometryIndex = geometry.Index;
974 //var geometryIndex = geometry.Index;
975 //if (geometryIndex >= materialIndices.Count)
976 //{
977 // continue;
978 //}
979 var materialIndex = materialIndices[geometryIndex];
980 if (materialIndex < 0 || materialIndex >= rootModel.AllMaterials.Count)
981 {
982 continue;
983 }
984 var sourceMaterial = rootModel.AllMaterials[materialIndex];
985 if (sourceMaterial == null)
986 {
987 continue;
988 }
989 if (geometryIndex < 0 || geometryIndex >= renderer.sharedMaterials.Length)
990 {
991 continue;
992 }
993 var materialRenderersContext = new MaterialRendererContext
994 {
995 Context = assetLoaderContext,
996 Renderer = renderer,
997 GeometryIndex = geometryIndex,
998 Material = sourceMaterial
999 };
1000 if (assetLoaderContext.MaterialRenderers.TryGetValue(sourceMaterial, out var materialRendererContextList))
1001 {
1002 materialRendererContextList.Add(materialRenderersContext);
1003 }
1004 else
1005 {
1006 assetLoaderContext.MaterialRenderers.Add(sourceMaterial, new List<MaterialRendererContext> { materialRenderersContext });
1007 }
1008 }
1009 }
1010 }
1011
1014 private static void LoadModel(AssetLoaderContext assetLoaderContext)
1015 {
1016 if (assetLoaderContext.Stream == null && string.IsNullOrWhiteSpace(assetLoaderContext.Filename))
1017 {
1018 throw new Exception("TriLib is unable to load the given file.");
1019 }
1020 if (assetLoaderContext.Options.MaterialMappers != null)
1021 {
1022 Array.Sort(assetLoaderContext.Options.MaterialMappers, (a, b) => a.CheckingOrder > b.CheckingOrder ? -1 : 1);
1023 }
1024 else
1025 {
1026 if (assetLoaderContext.Options.ShowLoadingWarnings)
1027 {
1028 Debug.LogWarning("Your AssetLoaderOptions instance has no MaterialMappers. TriLib can't process materials without them.");
1029 }
1030 }
1031#if TRILIB_DRACO
1032 GltfReader.DracoDecompressorCallback = DracoMeshLoader.DracoDecompressorCallback;
1033#endif
1034 var fileExtension = assetLoaderContext.FileExtension ?? FileUtils.GetFileExtension(assetLoaderContext.Filename, false);
1035 if (assetLoaderContext.Stream == null)
1036 {
1037 var fileStream = new FileStream(assetLoaderContext.Filename, FileMode.Open, FileAccess.Read, FileShare.Read);
1038 assetLoaderContext.Stream = fileStream;
1039 var reader = Readers.FindReaderForExtension(fileExtension);
1040 if (reader != null)
1041 {
1042 reader.ExtraStepsCount = ExtraStepsCount;
1043 assetLoaderContext.RootModel = reader.ReadStream(fileStream, assetLoaderContext, assetLoaderContext.Filename, assetLoaderContext.OnProgress);
1044 }
1045 }
1046 else
1047 {
1048 var reader = Readers.FindReaderForExtension(fileExtension);
1049 if (reader != null)
1050 {
1051 reader.ExtraStepsCount = ExtraStepsCount;
1052 assetLoaderContext.RootModel = reader.ReadStream(assetLoaderContext.Stream, assetLoaderContext, assetLoaderContext.Filename, assetLoaderContext.OnProgress);
1053 }
1054 else
1055 {
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.");
1057 }
1058 }
1059 }
1060
1063 private static void ProcessRootModel(AssetLoaderContext assetLoaderContext)
1064 {
1065 ProcessModel(assetLoaderContext);
1066 if (assetLoaderContext.Async)
1067 {
1068 ThreadUtils.RunThread(assetLoaderContext, ref assetLoaderContext.CancellationToken, ProcessTextures, null, assetLoaderContext.HandleError ?? assetLoaderContext.OnError, assetLoaderContext.Options.Timeout);
1069 }
1070 else
1071 {
1072 ProcessTextures(assetLoaderContext);
1073 }
1074 }
1075
1080 private static void TryProcessMaterials(AssetLoaderContext assetLoaderContext)
1081 {
1082 var allMaterialsLoaded = assetLoaderContext.RootModel?.AllTextures == null || assetLoaderContext.LoadedTexturesCount == assetLoaderContext.RootModel.AllTextures.Count;
1083 if (!assetLoaderContext.MaterialsProcessed && allMaterialsLoaded)
1084 {
1085 assetLoaderContext.MaterialsProcessed = true;
1086 if (assetLoaderContext.RootModel?.AllMaterials != null)
1087 {
1088 ProcessMaterialRenderers(assetLoaderContext);
1089 }
1090 if (assetLoaderContext.Options.AddAssetUnloader && assetLoaderContext.RootGameObject != null || assetLoaderContext.WrapperGameObject != null)
1091 {
1092 var gameObject = assetLoaderContext.RootGameObject ?? assetLoaderContext.WrapperGameObject;
1093 var assetUnloader = gameObject.AddComponent<AssetUnloader>();
1094 assetUnloader.Id = AssetUnloader.GetNextId();
1095 assetUnloader.Allocations = assetLoaderContext.Allocations;
1096 }
1097 foreach (var allocation in assetLoaderContext.Allocations)
1098 {
1099 if (allocation is Texture2D texture2D)
1100 {
1101 TextureUtils.FinishTexture2D(new TextureLoadingContext {UnityTexture = texture2D, Context = assetLoaderContext});
1102 }
1103 }
1104 assetLoaderContext.Reader.UpdateLoadingPercentage(1f, assetLoaderContext.Reader.LoadingStepsCount + FinalStep);
1105 assetLoaderContext.OnMaterialsLoad?.Invoke(assetLoaderContext);
1106 Cleanup(assetLoaderContext);
1107 }
1108 }
1109
1114 private static void ProcessTextures(AssetLoaderContext assetLoaderContext)
1115 {
1116 if (assetLoaderContext.RootModel?.AllTextures != null)
1117 {
1118 for (var i = 0; i < assetLoaderContext.RootModel.AllTextures.Count; i++)
1119 {
1120 var texture = assetLoaderContext.RootModel.AllTextures[i];
1121 TextureLoadingContext textureLoadingContext = null;
1122 if (assetLoaderContext.Options.TextureMapper != null)
1123 {
1124 textureLoadingContext = assetLoaderContext.Options.TextureMapper.Map(assetLoaderContext, texture);
1125 }
1126 if (textureLoadingContext == null)
1127 {
1128 textureLoadingContext = new TextureLoadingContext
1129 {
1130 Context = assetLoaderContext,
1131 Texture = texture
1132 };
1133 }
1134 StbImage.LoadTexture(textureLoadingContext);
1135 assetLoaderContext.Reader.UpdateLoadingPercentage(i, assetLoaderContext.Reader.LoadingStepsCount + ProcessingTexturesStep, assetLoaderContext.RootModel.AllTextures.Count);
1136 }
1137 }
1138 if (assetLoaderContext.Async)
1139 {
1140 Dispatcher.InvokeAsync(new ContextualizedAction<AssetLoaderContext>(TryProcessMaterials, assetLoaderContext));
1141 }
1142 else
1143 {
1144 TryProcessMaterials(assetLoaderContext);
1145 }
1146 }
1147
1152 private static void ProcessMaterialRenderers(AssetLoaderContext assetLoaderContext)
1153 {
1154 if (assetLoaderContext.Options.MaterialMappers != null)
1155 {
1156 for (var i = 0; i < assetLoaderContext.RootModel.AllMaterials.Count; i++)
1157 {
1158 var material = assetLoaderContext.RootModel.AllMaterials[i];
1159 MaterialMapper usedMaterialMapper = null;
1160 var materialMapperContext = new MaterialMapperContext
1161 {
1162 Context = assetLoaderContext,
1163 Material = material
1164 };
1165 for (var j = 0; j < assetLoaderContext.Options.MaterialMappers.Length; j++)
1166 {
1167 var materialMapper = assetLoaderContext.Options.MaterialMappers[j];
1168 if (materialMapper == null || !materialMapper.IsCompatible(materialMapperContext))
1169 {
1170 continue;
1171 }
1172
1173 materialMapper.Map(materialMapperContext);
1174 usedMaterialMapper = materialMapper;
1175 break;
1176 }
1177
1178 if (usedMaterialMapper != null)
1179 {
1180 if (assetLoaderContext.MaterialRenderers.TryGetValue(material, out var materialRendererList))
1181 {
1182 for (var j = 0; j < materialRendererList.Count; j++)
1183 {
1184 var materialRendererContext = materialRendererList[j];
1185 usedMaterialMapper.ApplyMaterialToRenderer(materialRendererContext, materialMapperContext);
1186 }
1187 }
1188 }
1189 assetLoaderContext.Reader.UpdateLoadingPercentage(i, assetLoaderContext.Reader.LoadingStepsCount + ProcessingRenderersStep, assetLoaderContext.RootModel.AllMaterials.Count);
1190 }
1191 }
1192 else if (assetLoaderContext.Options.ShowLoadingWarnings)
1193 {
1194 Debug.LogWarning("The given AssetLoaderOptions contains no MaterialMapper. Materials will not be created.");
1195 }
1196 }
1197
1200 private static void HandleError(IContextualizedError error)
1201 {
1202 var exception = error.GetInnerException();
1203 if (error.GetContext() is IAssetLoaderContext iassetLoaderContext)
1204 {
1205 var assetLoaderContext = iassetLoaderContext.Context;
1206 if (assetLoaderContext != null)
1207 {
1208 Cleanup(assetLoaderContext);
1209 if (assetLoaderContext.Options.DestroyOnError && assetLoaderContext.RootGameObject != null)
1210 {
1211#if UNITY_EDITOR
1212 Object.DestroyImmediate(assetLoaderContext.RootGameObject);
1213#else
1214 Object.Destroy(assetLoaderContext.RootGameObject);
1215#endif
1216 assetLoaderContext.RootGameObject = null;
1217 }
1218 if (assetLoaderContext.OnError != null)
1219 {
1220 if (assetLoaderContext.Async)
1221 {
1222 Dispatcher.InvokeAsync(new ContextualizedAction<IContextualizedError>(assetLoaderContext.OnError, error));
1223 }
1224 else
1225 {
1226 assetLoaderContext.OnError(error);
1227 }
1228 }
1229 }
1230 }
1231 else
1232 {
1233 var contextualizedAction = new ContextualizedAction<ContextualizedError<object>>(Rethrow, new ContextualizedError<object>(exception, null));
1234 Dispatcher.InvokeAsync(contextualizedAction);
1235 }
1236 }
1237
1243 private static void Cleanup(AssetLoaderContext assetLoaderContext)
1244 {
1245 if (assetLoaderContext.Stream != null && assetLoaderContext.Options.CloseStreamAutomatically)
1246 {
1247 try
1248 {
1249 assetLoaderContext.Stream.Dispose();
1250 }
1251 catch (Exception e)
1252 {
1253
1254 }
1255 }
1256 if (Application.isPlaying)
1257 {
1258 GCHelper.GetInstance().UnRegisterLoading(assetLoaderContext.Options.GCHelperCollectionInterval);
1259 }
1260 }
1261
1265 private static void Rethrow<T>(ContextualizedError<T> contextualizedError)
1266 {
1267 throw contextualizedError;
1268 }
1269 }
1270}
UnityEngine.HumanDescription HumanDescription
Definition: AssetLoader.cs:15
System.IO.FileMode FileMode
Definition: AssetLoader.cs:14
UnityEngine.Debug Debug
Definition: TanodaServer.cs:19
TriLibCore.AssetLoaderOptions AssetLoaderOptions
Definition: TriLibLoader.cs:9
Represents a Mapper that searches for a root bone on the Models by the bone names.
UnityEngine.Object Object