pyjallib 0.1.16__py3-none-any.whl → 0.1.19__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. pyjallib/__init__.py +4 -1
  2. pyjallib/exceptions.py +75 -0
  3. pyjallib/logger.py +288 -0
  4. pyjallib/max/__init__.py +8 -0
  5. pyjallib/max/autoClavicle.py +17 -5
  6. pyjallib/max/bip.py +0 -21
  7. pyjallib/max/bone.py +21 -1
  8. pyjallib/max/boneChain.py +2 -0
  9. pyjallib/max/constraint.py +27 -2
  10. pyjallib/max/elbow.py +105 -0
  11. pyjallib/max/groinBone.py +2 -0
  12. pyjallib/max/header.py +121 -113
  13. pyjallib/max/hip.py +2 -0
  14. pyjallib/max/inguinal.py +117 -0
  15. pyjallib/max/kneeBone.py +2 -0
  16. pyjallib/max/macro/jal_macro_bone.py +221 -8
  17. pyjallib/max/macro/jal_macro_constraint.py +30 -0
  18. pyjallib/max/mirror.py +20 -13
  19. pyjallib/max/shoulder.py +173 -0
  20. pyjallib/max/twistBone.py +22 -19
  21. pyjallib/max/volumeBone.py +12 -1
  22. pyjallib/max/wrist.py +113 -0
  23. pyjallib/nameToPath.py +78 -61
  24. pyjallib/naming.py +4 -1
  25. pyjallib/perforce.py +196 -347
  26. pyjallib/progressEvent.py +75 -0
  27. pyjallib/ue5/ConfigFiles/UE5NamingConfig.json +487 -0
  28. pyjallib/ue5/__init__.py +170 -0
  29. pyjallib/ue5/disableInterchangeFrameWork.py +82 -0
  30. pyjallib/ue5/inUnreal/__init__.py +95 -0
  31. pyjallib/ue5/inUnreal/animationImporter.py +206 -0
  32. pyjallib/ue5/inUnreal/baseImporter.py +187 -0
  33. pyjallib/ue5/inUnreal/importerSettings.py +179 -0
  34. pyjallib/ue5/inUnreal/skeletalMeshImporter.py +112 -0
  35. pyjallib/ue5/inUnreal/skeletonImporter.py +105 -0
  36. pyjallib/ue5/logger.py +241 -0
  37. pyjallib/ue5/templateProcessor.py +287 -0
  38. pyjallib/ue5/templates/__init__.py +109 -0
  39. pyjallib/ue5/templates/animImportTemplate.py +22 -0
  40. pyjallib/ue5/templates/batchAnimImportTemplate.py +21 -0
  41. pyjallib/ue5/templates/skeletalMeshImportTemplate.py +22 -0
  42. pyjallib/ue5/templates/skeletonImportTemplate.py +21 -0
  43. {pyjallib-0.1.16.dist-info → pyjallib-0.1.19.dist-info}/METADATA +1 -1
  44. pyjallib-0.1.19.dist-info/RECORD +72 -0
  45. pyjallib-0.1.16.dist-info/RECORD +0 -49
  46. {pyjallib-0.1.16.dist-info → pyjallib-0.1.19.dist-info}/WHEEL +0 -0
@@ -0,0 +1,170 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ Unreal Engine 5 패키지
6
+ Unreal Engine 5 작업을 위한 모듈 모음
7
+
8
+ 새로운 구조:
9
+ - Unreal Engine이 실행되지 않아도 사용 가능한 기능들 (templates, templateProcessor, logger 등)
10
+ - Unreal Engine이 필요한 기능들은 inUnreal 서브모듈에 위치
11
+ """
12
+
13
+ # Unreal Engine 없이도 사용 가능한 기본 모듈들
14
+ __all__ = []
15
+
16
+ # Logger (조건부 unreal 지원)
17
+ try:
18
+ from .logger import (
19
+ ue5_logger,
20
+ set_log_level,
21
+ set_ue5_log_level,
22
+ get_log_file_path,
23
+ set_log_file_path
24
+ )
25
+ __all__.extend([
26
+ 'ue5_logger',
27
+ 'set_log_level',
28
+ 'set_ue5_log_level',
29
+ 'get_log_file_path',
30
+ 'set_log_file_path'
31
+ ])
32
+ except ImportError as e:
33
+ ue5_logger = None
34
+ set_log_level = None
35
+ set_ue5_log_level = None
36
+ get_log_file_path = None
37
+ set_log_file_path = None
38
+ print(f"[PyJalLib] UE5 Logger를 로드할 수 없습니다: {e}")
39
+
40
+ # TemplateProcessor (Unreal 없이 사용 가능)
41
+ try:
42
+ from .templateProcessor import TemplateProcessor
43
+ __all__.append('TemplateProcessor')
44
+ except ImportError as e:
45
+ TemplateProcessor = None
46
+ if ue5_logger:
47
+ ue5_logger.error(f"TemplateProcessor를 로드할 수 없습니다: {e}")
48
+ else:
49
+ print(f"[PyJalLib] TemplateProcessor를 로드할 수 없습니다: {e}")
50
+
51
+ # Templates 모듈 (Unreal 없이 사용 가능)
52
+ try:
53
+ from .templates import (
54
+ get_template_path,
55
+ get_all_template_paths,
56
+ get_available_templates,
57
+ validate_template_name,
58
+ ANIM_IMPORT_TEMPLATE,
59
+ SKELETON_IMPORT_TEMPLATE,
60
+ SKELETAL_MESH_IMPORT_TEMPLATE
61
+ )
62
+ __all__.extend([
63
+ 'get_template_path',
64
+ 'get_all_template_paths',
65
+ 'get_available_templates',
66
+ 'validate_template_name',
67
+ 'ANIM_IMPORT_TEMPLATE',
68
+ 'SKELETON_IMPORT_TEMPLATE',
69
+ 'SKELETAL_MESH_IMPORT_TEMPLATE'
70
+ ])
71
+ except ImportError as e:
72
+ get_template_path = None
73
+ get_all_template_paths = None
74
+ get_available_templates = None
75
+ validate_template_name = None
76
+ ANIM_IMPORT_TEMPLATE = None
77
+ SKELETON_IMPORT_TEMPLATE = None
78
+ SKELETAL_MESH_IMPORT_TEMPLATE = None
79
+ if ue5_logger:
80
+ ue5_logger.error(f"Templates 모듈을 로드할 수 없습니다: {e}")
81
+ else:
82
+ print(f"[PyJalLib] Templates 모듈을 로드할 수 없습니다: {e}")
83
+
84
+ # disableInterchangeFrameWork (Unreal 없이 사용 가능)
85
+ try:
86
+ from .disableInterchangeFrameWork import add_disabled_plugins_to_uproject
87
+ __all__.append('add_disabled_plugins_to_uproject')
88
+ except ImportError as e:
89
+ add_disabled_plugins_to_uproject = None
90
+ if ue5_logger:
91
+ ue5_logger.error(f"disableInterchangeFrameWork를 로드할 수 없습니다: {e}")
92
+ else:
93
+ print(f"[PyJalLib] disableInterchangeFrameWork를 로드할 수 없습니다: {e}")
94
+
95
+ # UE5 의존성 상태를 확인할 수 있는 함수
96
+ def is_ue5_available() -> bool:
97
+ """
98
+ Unreal Engine 5가 사용 가능한지 확인합니다.
99
+
100
+ Returns:
101
+ bool: UE5가 사용 가능하면 True, 그렇지 않으면 False
102
+ """
103
+ try:
104
+ import unreal
105
+ return True
106
+ except ImportError:
107
+ return False
108
+
109
+ def get_available_modules() -> dict:
110
+ """
111
+ 현재 사용 가능한 모듈 목록을 반환합니다.
112
+
113
+ Returns:
114
+ dict: 모듈 카테고리별 사용 가능한 모듈 정보
115
+ """
116
+ available = {
117
+ 'core': [],
118
+ 'templates': [],
119
+ 'utils': [],
120
+ 'unreal_dependent': []
121
+ }
122
+
123
+ # Core 모듈들
124
+ if ue5_logger is not None:
125
+ available['core'].append('logger')
126
+ if TemplateProcessor is not None:
127
+ available['core'].append('TemplateProcessor')
128
+
129
+ # Templates 관련
130
+ if get_template_path is not None:
131
+ available['templates'].append('template_management')
132
+
133
+ # Utilities
134
+ if add_disabled_plugins_to_uproject is not None:
135
+ available['utils'].append('disableInterchangeFrameWork')
136
+
137
+ # Unreal 의존성 모듈들 (inUnreal 서브모듈에서 가져오기)
138
+ try:
139
+ from . import inUnreal
140
+ if hasattr(inUnreal, 'get_available_modules'):
141
+ available['unreal_dependent'] = inUnreal.get_available_modules()
142
+ except ImportError:
143
+ available['unreal_dependent'] = []
144
+
145
+ return available
146
+
147
+ def get_module_status() -> dict:
148
+ """
149
+ 모듈 상태 정보를 반환합니다.
150
+
151
+ Returns:
152
+ dict: 모듈 상태 정보
153
+ """
154
+ status = {
155
+ 'ue5_available': is_ue5_available(),
156
+ 'available_modules': get_available_modules(),
157
+ 'structure': {
158
+ 'core_modules': 'Unreal Engine 없이 사용 가능',
159
+ 'inUnreal_modules': 'Unreal Engine 필요',
160
+ 'templates': '템플릿 파일 관리'
161
+ }
162
+ }
163
+ return status
164
+
165
+ # 헬퍼 함수들도 __all__에 추가
166
+ __all__.extend([
167
+ 'is_ue5_available',
168
+ 'get_available_modules',
169
+ 'get_module_status'
170
+ ])
@@ -0,0 +1,82 @@
1
+ import json
2
+ from pathlib import Path
3
+ from .logger import ue5_logger
4
+
5
+ def add_disabled_plugins_to_uproject(uproject_path):
6
+ """
7
+ 특정 플러그인들을 Enabled=false 상태로 추가하여 temp_ 접두사가 붙은 새 파일로 저장
8
+
9
+ Args:
10
+ uproject_path (str): 원본 .uproject 파일 경로
11
+ """
12
+
13
+ # 비활성화 상태로 추가할 플러그인들
14
+ plugins_to_add = [
15
+ {
16
+ "Name": "MeshPainting",
17
+ "Enabled": False
18
+ },
19
+ {
20
+ "Name": "InterchangeEditor",
21
+ "Enabled": False,
22
+ "SupportedTargetPlatforms": [
23
+ "Win64",
24
+ "Linux",
25
+ "Mac"
26
+ ]
27
+ },
28
+ {
29
+ "Name": "GLTFExporter",
30
+ "Enabled": False
31
+ },
32
+ {
33
+ "Name": "InterchangeTests",
34
+ "Enabled": False
35
+ },
36
+ {
37
+ "Name": "Interchange",
38
+ "Enabled": False,
39
+ "SupportedTargetPlatforms": [
40
+ "Win64",
41
+ "Linux",
42
+ "Mac"
43
+ ]
44
+ },
45
+ {
46
+ "Name": "InterchangeAssets",
47
+ "Enabled": False
48
+ }
49
+ ]
50
+
51
+ # 파일 읽기
52
+ with open(uproject_path, 'r', encoding='utf-8') as f:
53
+ project_data = json.load(f)
54
+
55
+ # 기존 플러그인 이름들 확인
56
+ existing_plugin_names = set()
57
+ if 'Plugins' in project_data:
58
+ existing_plugin_names = {plugin['Name'] for plugin in project_data['Plugins']}
59
+
60
+ # 새 플러그인 추가 (중복 체크)
61
+ added_count = 0
62
+ for plugin in plugins_to_add:
63
+ if plugin['Name'] not in existing_plugin_names:
64
+ project_data['Plugins'].append(plugin)
65
+ added_count += 1
66
+ ue5_logger.info(f"추가됨 (비활성화): {plugin['Name']}")
67
+ else:
68
+ ue5_logger.info(f"이미 존재: {plugin['Name']}")
69
+
70
+ # 출력 파일 경로 생성 (temp_ 접두사 추가)
71
+ input_path = Path(uproject_path)
72
+ output_filename = f"temp_{input_path.name}"
73
+ output_path = input_path.parent / output_filename
74
+
75
+ # 파일 저장
76
+ with open(output_path, 'w', encoding='utf-8') as f:
77
+ json.dump(project_data, f, indent='\t', ensure_ascii=False)
78
+
79
+ ue5_logger.info(f"총 {added_count}개 플러그인 추가 (비활성화 상태)")
80
+ ue5_logger.info(f"출력 파일: {output_path}")
81
+
82
+ return output_path
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ UE5 inUnreal 패키지
6
+ Unreal Engine 5가 실행 중일 때만 사용 가능한 모듈들
7
+
8
+ 주의: 이 패키지의 모든 모듈은 Unreal Engine이 실행 중일 때만 임포트 가능합니다.
9
+ """
10
+
11
+ # UE5 가용성 확인
12
+ def is_ue5_available() -> bool:
13
+ """
14
+ Unreal Engine 5가 사용 가능한지 확인합니다.
15
+
16
+ Returns:
17
+ bool: UE5가 사용 가능하면 True, 그렇지 않으면 False
18
+ """
19
+ try:
20
+ import unreal
21
+ return True
22
+ except ImportError:
23
+ return False
24
+
25
+ # 기본적으로 사용 가능한 모듈들
26
+ __all__ = ['is_ue5_available']
27
+
28
+ # UE5가 사용 가능한 경우에만 모듈들을 임포트
29
+ if is_ue5_available():
30
+ try:
31
+ from .importerSettings import ImporterSettings
32
+ __all__.append('ImporterSettings')
33
+ except ImportError as e:
34
+ ImporterSettings = None
35
+ print(f"[PyJalLib] ImporterSettings 임포트 실패: {e}")
36
+
37
+ try:
38
+ from .baseImporter import BaseImporter
39
+ __all__.append('BaseImporter')
40
+ except ImportError as e:
41
+ BaseImporter = None
42
+ print(f"[PyJalLib] BaseImporter 임포트 실패: {e}")
43
+
44
+ try:
45
+ from .skeletonImporter import SkeletonImporter
46
+ __all__.append('SkeletonImporter')
47
+ except ImportError as e:
48
+ SkeletonImporter = None
49
+ print(f"[PyJalLib] SkeletonImporter 임포트 실패: {e}")
50
+
51
+ try:
52
+ from .skeletalMeshImporter import SkeletalMeshImporter
53
+ __all__.append('SkeletalMeshImporter')
54
+ except ImportError as e:
55
+ SkeletalMeshImporter = None
56
+ print(f"[PyJalLib] SkeletalMeshImporter 임포트 실패: {e}")
57
+
58
+ try:
59
+ from .animationImporter import AnimationImporter
60
+ __all__.append('AnimationImporter')
61
+ except ImportError as e:
62
+ AnimationImporter = None
63
+ print(f"[PyJalLib] AnimationImporter 임포트 실패: {e}")
64
+
65
+ else:
66
+ # UE5가 사용 불가능한 경우 모든 모듈을 None으로 설정
67
+ ImporterSettings = None
68
+ BaseImporter = None
69
+ SkeletonImporter = None
70
+ SkeletalMeshImporter = None
71
+ AnimationImporter = None
72
+ print("[PyJalLib] Unreal Engine이 실행되지 않았습니다. inUnreal 모듈들을 사용할 수 없습니다.")
73
+
74
+ def get_available_modules() -> list:
75
+ """
76
+ 현재 사용 가능한 모듈 목록을 반환합니다.
77
+
78
+ Returns:
79
+ list: 사용 가능한 모듈 이름 목록
80
+ """
81
+ available = []
82
+ if 'ImporterSettings' in __all__ and ImporterSettings is not None:
83
+ available.append('ImporterSettings')
84
+ if 'BaseImporter' in __all__ and BaseImporter is not None:
85
+ available.append('BaseImporter')
86
+ if 'SkeletonImporter' in __all__ and SkeletonImporter is not None:
87
+ available.append('SkeletonImporter')
88
+ if 'SkeletalMeshImporter' in __all__ and SkeletalMeshImporter is not None:
89
+ available.append('SkeletalMeshImporter')
90
+ if 'AnimationImporter' in __all__ and AnimationImporter is not None:
91
+ available.append('AnimationImporter')
92
+ return available
93
+
94
+ # 헬퍼 함수도 __all__에 추가
95
+ __all__.append('get_available_modules')
@@ -0,0 +1,206 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ UE5 애니메이션 임포터 모듈
6
+
7
+ 이 모듈은 FBX 파일에서 애니메이션 에셋을 UE5로 임포트하는 기능을 제공합니다.
8
+ PyJalLib의 naming 모듈을 사용하여 에셋 이름을 자동 생성합니다.
9
+ """
10
+
11
+ import unreal
12
+ from pathlib import Path
13
+ from typing import Optional, Dict, Any
14
+
15
+ # UE5 모듈 import
16
+ from .baseImporter import BaseImporter
17
+ from ..logger import ue5_logger
18
+ from .importerSettings import ImporterSettings
19
+
20
+ class AnimationImporter(BaseImporter):
21
+ def __init__(self, inContentRootPrefix: str, inFbxRootPrefix: str):
22
+ super().__init__(inContentRootPrefix, inFbxRootPrefix, "Animation")
23
+ ue5_logger.info("AnimationImporter 초기화 완료")
24
+
25
+ @property
26
+ def asset_type(self) -> str:
27
+ return "Animation"
28
+
29
+ def _create_batch_import_description(self, inFbxFiles: list[str], inAssetFullPaths: list[str]) -> str:
30
+ """
31
+ 배치 임포트용 간결한 디스크립션 생성
32
+
33
+ Args:
34
+ inFbxFiles (list[str]): 임포트된 FBX 파일 목록
35
+ inAssetFullPaths (list[str]): 임포트된 에셋 전체 경로 목록
36
+
37
+ Returns:
38
+ str: 간결한 디스크립션
39
+ """
40
+ totalCount = len(inFbxFiles)
41
+
42
+ if totalCount <= 3:
43
+ # 3개 이하면 모든 경로 표시
44
+ fbxList = ", ".join(inFbxFiles)
45
+ assetList = ", ".join(inAssetFullPaths)
46
+ return f"Animation Batch Import ({totalCount} files): {fbxList} -> {assetList}"
47
+ else:
48
+ # 3개 초과면 처음 3개만 표시하고 나머지는 개수로 표시
49
+ fbxList = ", ".join(inFbxFiles[:3]) + f" ... (and {totalCount - 3} more)"
50
+ assetList = ", ".join(inAssetFullPaths[:3]) + f" ... (and {totalCount - 3} more)"
51
+ return f"Animation Batch Import ({totalCount} files): {fbxList} -> {assetList}"
52
+
53
+ def create_import_task(self, inFbxFile: str, inDestinationPath: str, inFbxSkeletonPath: str):
54
+ """애니메이션 임포트를 위한 태스크 생성 - 스켈레톤 필수 지정"""
55
+ ue5_logger.debug(f"애니메이션 임포트 태스크 생성 시작: {inFbxFile}")
56
+
57
+ importOptions = self.importerSettings.load_options()
58
+ ue5_logger.debug("애니메이션 임포트 옵션 로드 완료")
59
+
60
+ # 스켈레톤 필수 설정
61
+ if inFbxSkeletonPath is None:
62
+ error_msg = "애니메이션 임포트에는 스켈레톤이 필수입니다"
63
+ ue5_logger.error(error_msg)
64
+ raise ValueError(error_msg)
65
+
66
+ skeletonPath = self.convert_fbx_path_to_skeleton_path(inFbxSkeletonPath)
67
+ skeletonAssetData = unreal.EditorAssetLibrary.find_asset_data(skeletonPath)
68
+ if not skeletonAssetData.is_valid():
69
+ error_msg = f"스켈레톤 에셋을 찾을 수 없음: {skeletonPath}"
70
+ ue5_logger.error(error_msg)
71
+ raise ValueError(error_msg)
72
+
73
+ animSkeleton = skeletonAssetData.get_asset()
74
+ importOptions.set_editor_property('skeleton', animSkeleton)
75
+ ue5_logger.debug(f"스켈레톤 설정됨: {animSkeleton.get_name()}")
76
+
77
+ # 에셋 이름 결정: FBX 파일 이름에서 확장자 제거
78
+ assetName = Path(inFbxFile).stem
79
+
80
+ task = unreal.AssetImportTask()
81
+ task.automated = True
82
+ task.destination_path = inDestinationPath
83
+ task.filename = inFbxFile
84
+ task.destination_name = assetName
85
+ task.replace_existing = True
86
+ task.save = True
87
+ task.options = importOptions
88
+
89
+ ue5_logger.debug(f"애니메이션 임포트 태스크 생성 완료: Destination={inDestinationPath}, AssetName={assetName}")
90
+ return task
91
+
92
+ def import_animation(self, inFbxFile: str, inFbxSkeletonPath: str, inAssetName: str = None, inDescription: str = None):
93
+ ue5_logger.info(f"애니메이션 임포트 시작: {inFbxFile}")
94
+
95
+ destinationPath, assetName = self._prepare_import_paths(inFbxFile, inAssetName)
96
+ assetFullPath = f"{destinationPath}/{assetName}"
97
+
98
+ # 기존 에셋이 있는 경우 소스 컨트롤에서 체크아웃
99
+ if unreal.Paths.file_exists(assetFullPath):
100
+ unreal.SourceControl.check_out_or_add_file(assetFullPath, silent=True)
101
+
102
+ task = self.create_import_task(inFbxFile, destinationPath, inFbxSkeletonPath)
103
+
104
+ ue5_logger.info(f"애니메이션 임포트 실행: {inFbxFile} -> {destinationPath}/{assetName}")
105
+ unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task])
106
+
107
+ result = task.get_objects()
108
+ if len(result) == 0:
109
+ error_msg = f"애니메이션 임포트 실패: {inFbxFile}"
110
+ ue5_logger.error(error_msg)
111
+ raise ValueError(error_msg)
112
+
113
+ importedObjectPaths = task.imported_object_paths
114
+ refObjectPaths = self.get_dirty_deps(assetFullPath)
115
+
116
+ allImportRelatedPaths = list(dict.fromkeys(importedObjectPaths + refObjectPaths))
117
+ for assetPath in allImportRelatedPaths:
118
+ unreal.SourceControl.check_out_or_add_file(assetPath, silent=True)
119
+
120
+ checkInDescription = f"Animation Imported by {inFbxFile} to {assetFullPath}"
121
+ if inDescription is not None:
122
+ checkInDescription = inDescription
123
+
124
+ allImportAbsPaths = []
125
+ for assetPath in allImportRelatedPaths:
126
+ assetObj = unreal.EditorAssetLibrary.load_asset(assetPath)
127
+ if assetObj is not None:
128
+ absPath = unreal.SystemLibrary.get_system_path(assetObj)
129
+ allImportAbsPaths.append(absPath)
130
+
131
+ unreal.SourceControl.check_in_files(allImportAbsPaths, checkInDescription, silent=True)
132
+
133
+ ue5_logger.info(f"애니메이션 임포트 성공: {inFbxFile} -> {len(result)}개 객체 생성")
134
+ return self._create_result_dict(inFbxFile, destinationPath, assetName, True)
135
+
136
+ def import_animations(self, inFbxFiles: list[str], inFbxSkeletonPaths: list[str], inAssetNames: list[str] = None, inDescription: str = None):
137
+ ue5_logger.info(f"애니메이션 임포트 시작: {inFbxFiles}")
138
+
139
+ if len(inFbxFiles) != len(inFbxSkeletonPaths):
140
+ error_msg = "애니메이션 임포트에는 파일과 스켈레톤이 같은 개수여야 합니다"
141
+ ue5_logger.error(error_msg)
142
+ raise ValueError(error_msg)
143
+
144
+ if inAssetNames is not None and len(inFbxFiles) != len(inAssetNames):
145
+ error_msg = "애니메이션 임포트에는 파일과 에셋 이름이 같은 개수여야 합니다"
146
+ ue5_logger.error(error_msg)
147
+ raise ValueError(error_msg)
148
+
149
+ destinationPaths = []
150
+ assetNames = []
151
+ assetFullPaths = []
152
+ tasks = []
153
+ for index, fbxFile in enumerate(inFbxFiles):
154
+ cusAssetName = None
155
+ if inAssetNames is not None:
156
+ cusAssetName = inAssetNames[index]
157
+ destinationPath, assetName = self._prepare_import_paths(fbxFile, cusAssetName)
158
+
159
+ destinationPaths.append(destinationPath)
160
+ assetNames.append(assetName)
161
+ assetFullPath = f"{destinationPath}/{assetName}"
162
+ assetFullPaths.append(assetFullPath)
163
+
164
+ if unreal.Paths.file_exists(assetFullPath):
165
+ unreal.SourceControl.check_out_or_add_file(assetFullPath, silent=True)
166
+
167
+ task = self.create_import_task(fbxFile, destinationPath, inFbxSkeletonPaths[index])
168
+ tasks.append(task)
169
+
170
+ unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks(tasks)
171
+
172
+ batchImportedAssetPaths = []
173
+ batchImporteAbsPaths = []
174
+ for index, task in enumerate(tasks):
175
+ result = task.get_objects()
176
+ if len(result) == 0:
177
+ error_msg = f"애니메이션 임포트 실패: {inFbxFiles[index]}"
178
+ ue5_logger.error(error_msg)
179
+ raise ValueError(error_msg)
180
+
181
+ importedObjectPaths = task.imported_object_paths
182
+ refObjectPaths = self.get_dirty_deps(assetFullPaths[index])
183
+
184
+
185
+ allImportRelatedPaths = list(dict.fromkeys(importedObjectPaths + refObjectPaths))
186
+ for assetPath in allImportRelatedPaths:
187
+ unreal.SourceControl.check_out_or_add_file(assetPath, silent=True)
188
+ batchImportedAssetPaths.append(assetPath)
189
+
190
+ batchImportedAssetPaths = list(dict.fromkeys(batchImportedAssetPaths))
191
+ for assetPath in batchImportedAssetPaths:
192
+ assetObj = unreal.EditorAssetLibrary.load_asset(assetPath)
193
+ if assetObj is not None:
194
+ absPath = unreal.SystemLibrary.get_system_path(assetObj)
195
+ batchImporteAbsPaths.append(absPath)
196
+
197
+ # 배치 임포트용 간결한 디스크립션 생성
198
+ if inDescription is not None:
199
+ checkInDescription = inDescription
200
+ else:
201
+ checkInDescription = self._create_batch_import_description(inFbxFiles, assetFullPaths)
202
+
203
+ checkinResult = unreal.SourceControl.check_in_files(batchImporteAbsPaths, checkInDescription, silent=True)
204
+ ue5_logger.info(f"배치 임포트 체크인 결과: {checkinResult}")
205
+
206
+ ue5_logger.info(f"애니메이션 임포트 완료: {inFbxFiles}")