fbxkit 0.1.0__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.
- fbxkit/__about__.py +7 -0
- fbxkit/__init__.py +210 -0
- fbxkit/__main__.py +8 -0
- fbxkit/builtin_ops.py +561 -0
- fbxkit/cli.py +429 -0
- fbxkit/document.py +448 -0
- fbxkit/errors.py +110 -0
- fbxkit/io.py +260 -0
- fbxkit/ops.py +231 -0
- fbxkit/params.py +239 -0
- fbxkit/patch.py +226 -0
- fbxkit/schema.py +316 -0
- fbxkit/selectors.py +337 -0
- fbxkit/serialize.py +143 -0
- fbxkit/session/__init__.py +51 -0
- fbxkit/session/base.py +233 -0
- fbxkit/session/fake.py +391 -0
- fbxkit/session/fbx.py +913 -0
- fbxkit/session/index.py +208 -0
- fbxkit/session/protocol.py +170 -0
- fbxkit-0.1.0.dist-info/METADATA +861 -0
- fbxkit-0.1.0.dist-info/RECORD +25 -0
- fbxkit-0.1.0.dist-info/WHEEL +4 -0
- fbxkit-0.1.0.dist-info/entry_points.txt +2 -0
- fbxkit-0.1.0.dist-info/licenses/LICENSE +21 -0
fbxkit/__about__.py
ADDED
fbxkit/__init__.py
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"""fbxkit -- lightweight, general-purpose CRUD for FBX files.
|
|
2
|
+
|
|
3
|
+
import fbxkit
|
|
4
|
+
|
|
5
|
+
with fbxkit.open("hero.fbx") as doc:
|
|
6
|
+
for node in doc.select("**[type=mesh]"): # 查
|
|
7
|
+
print(node.path)
|
|
8
|
+
doc.create("locator", parent="|root") # 增
|
|
9
|
+
doc.rename("**[type=mesh]", pattern="^SM_", replacement="") # 改
|
|
10
|
+
doc.delete("**|*_ref", required=False) # 删
|
|
11
|
+
doc.save() # same format, same version
|
|
12
|
+
|
|
13
|
+
The package is layered so that only one module in it imports the Autodesk SDK::
|
|
14
|
+
|
|
15
|
+
schema / selectors / params / serialize pure data and pure logic
|
|
16
|
+
ops / builtin_ops / patch operations, and patches as data
|
|
17
|
+
session/protocol.py the interface an operation may use
|
|
18
|
+
session/fbx.py <- the only import fbx | session/fake.py <- the stand-in
|
|
19
|
+
document.py / io.py / cli.py lifetime, disk facts, the CLI
|
|
20
|
+
|
|
21
|
+
Which means everything except actually opening a file runs, and is tested, on an
|
|
22
|
+
interpreter with no SDK installed at all.
|
|
23
|
+
|
|
24
|
+
**Logging.** The package attaches a ``NullHandler`` and stays silent; every call
|
|
25
|
+
is at DEBUG level, because a library that writes to its host's log at INFO ends
|
|
26
|
+
up in an artist's script editor. To look::
|
|
27
|
+
|
|
28
|
+
import logging
|
|
29
|
+
logging.basicConfig()
|
|
30
|
+
logging.getLogger("fbxkit").setLevel(logging.DEBUG)
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
import logging as _logging
|
|
34
|
+
|
|
35
|
+
# Importing this module is what puts the builtin operations in the registry.
|
|
36
|
+
from . import builtin_ops as _builtin_ops # noqa: F401 (imported for its side effect)
|
|
37
|
+
from .__about__ import __version__
|
|
38
|
+
from .document import FbxDocument, open_document
|
|
39
|
+
from .errors import (
|
|
40
|
+
AmbiguousMatch,
|
|
41
|
+
DocumentClosed,
|
|
42
|
+
FbxkitError,
|
|
43
|
+
FileOpenError,
|
|
44
|
+
FileSaveError,
|
|
45
|
+
NoMatch,
|
|
46
|
+
OperationError,
|
|
47
|
+
ParamError,
|
|
48
|
+
SchemaVersionError,
|
|
49
|
+
SdkUnavailable,
|
|
50
|
+
SelectorError,
|
|
51
|
+
UnknownOperation,
|
|
52
|
+
)
|
|
53
|
+
from .io import EXPORT_VERSIONS, file_info, sniff_format
|
|
54
|
+
from .ops import Operation, OperationRegistry, register, registry
|
|
55
|
+
from .patch import Patch, apply_operations, load_patch, loads_patch
|
|
56
|
+
from .schema import (
|
|
57
|
+
SCHEMA_VERSION,
|
|
58
|
+
Change,
|
|
59
|
+
Counters,
|
|
60
|
+
Diagnostic,
|
|
61
|
+
FileInfo,
|
|
62
|
+
MaterialInfo,
|
|
63
|
+
MeshInfo,
|
|
64
|
+
NodeInfo,
|
|
65
|
+
OperationReport,
|
|
66
|
+
PatchResult,
|
|
67
|
+
PropertyInfo,
|
|
68
|
+
SceneDocument,
|
|
69
|
+
SceneSettings,
|
|
70
|
+
TakeInfo,
|
|
71
|
+
TextureInfo,
|
|
72
|
+
)
|
|
73
|
+
from .selectors import Selector
|
|
74
|
+
from .serialize import dump, dumps, load, loads
|
|
75
|
+
from .session import FakeSession, NodeRef, SessionProtocol, demo_session, have_sdk
|
|
76
|
+
|
|
77
|
+
_logging.getLogger(__name__).addHandler(_logging.NullHandler())
|
|
78
|
+
|
|
79
|
+
__all__ = [
|
|
80
|
+
"__version__",
|
|
81
|
+
"SCHEMA_VERSION",
|
|
82
|
+
# entry points
|
|
83
|
+
"open",
|
|
84
|
+
"open_document",
|
|
85
|
+
"new",
|
|
86
|
+
"read",
|
|
87
|
+
"patch_file",
|
|
88
|
+
"FbxDocument",
|
|
89
|
+
# patches and operations
|
|
90
|
+
"Patch",
|
|
91
|
+
"apply_operations",
|
|
92
|
+
"load_patch",
|
|
93
|
+
"loads_patch",
|
|
94
|
+
"Operation",
|
|
95
|
+
"OperationRegistry",
|
|
96
|
+
"register",
|
|
97
|
+
"registry",
|
|
98
|
+
"available",
|
|
99
|
+
# selection
|
|
100
|
+
"Selector",
|
|
101
|
+
"NodeRef",
|
|
102
|
+
# schema
|
|
103
|
+
"SceneDocument",
|
|
104
|
+
"PatchResult",
|
|
105
|
+
"OperationReport",
|
|
106
|
+
"Change",
|
|
107
|
+
"NodeInfo",
|
|
108
|
+
"MeshInfo",
|
|
109
|
+
"MaterialInfo",
|
|
110
|
+
"TextureInfo",
|
|
111
|
+
"TakeInfo",
|
|
112
|
+
"SceneSettings",
|
|
113
|
+
"Counters",
|
|
114
|
+
"Diagnostic",
|
|
115
|
+
"FileInfo",
|
|
116
|
+
"PropertyInfo",
|
|
117
|
+
# serialization
|
|
118
|
+
"dumps",
|
|
119
|
+
"dump",
|
|
120
|
+
"loads",
|
|
121
|
+
"load",
|
|
122
|
+
# sessions
|
|
123
|
+
"SessionProtocol",
|
|
124
|
+
"FakeSession",
|
|
125
|
+
"demo_session",
|
|
126
|
+
"have_sdk",
|
|
127
|
+
# files
|
|
128
|
+
"file_info",
|
|
129
|
+
"sniff_format",
|
|
130
|
+
"EXPORT_VERSIONS",
|
|
131
|
+
# errors
|
|
132
|
+
"FbxkitError",
|
|
133
|
+
"SdkUnavailable",
|
|
134
|
+
"FileOpenError",
|
|
135
|
+
"FileSaveError",
|
|
136
|
+
"SelectorError",
|
|
137
|
+
"NoMatch",
|
|
138
|
+
"AmbiguousMatch",
|
|
139
|
+
"ParamError",
|
|
140
|
+
"UnknownOperation",
|
|
141
|
+
"OperationError",
|
|
142
|
+
"SchemaVersionError",
|
|
143
|
+
"DocumentClosed",
|
|
144
|
+
]
|
|
145
|
+
|
|
146
|
+
def open(path, compute_hash=True): # noqa: A001 -- fbxkit.open reads best at the call site
|
|
147
|
+
"""Open an FBX file for reading and editing.
|
|
148
|
+
|
|
149
|
+
Use it as a context manager. The scene holds an ``FbxManager`` that must be
|
|
150
|
+
destroyed exactly once, and ``with`` is what guarantees that::
|
|
151
|
+
|
|
152
|
+
with fbxkit.open("hero.fbx") as doc:
|
|
153
|
+
...
|
|
154
|
+
|
|
155
|
+
Raises :class:`FileOpenError` with a reason that names the actual cause --
|
|
156
|
+
the SDK reports a missing file, an empty file, a truncated file and a file
|
|
157
|
+
of unrelated bytes with one identical message, so fbxkit checks the disk
|
|
158
|
+
first.
|
|
159
|
+
"""
|
|
160
|
+
return open_document(path, compute_hash=compute_hash)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def new():
|
|
164
|
+
"""An empty scene, for building a file from nothing."""
|
|
165
|
+
from .session.fbx import FbxSession
|
|
166
|
+
|
|
167
|
+
return FbxDocument(FbxSession.empty())
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def read(path, include_properties=False, compute_hash=True):
|
|
171
|
+
"""Open, snapshot and close in one call -- the read-only path.
|
|
172
|
+
|
|
173
|
+
Returns a :class:`SceneDocument`: everything about the scene as plain data,
|
|
174
|
+
with the file already closed. This is the shape a backend stores and the one
|
|
175
|
+
a diff compares.
|
|
176
|
+
"""
|
|
177
|
+
with open(path, compute_hash=compute_hash) as document:
|
|
178
|
+
return document.document(include_properties=include_properties)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def patch_file(path, ops, output=None, dry_run=False, atomic=True, **save_args):
|
|
182
|
+
"""Apply a patch to a file and write the result. The batch entry point.
|
|
183
|
+
|
|
184
|
+
Nothing is written when the patch does not fully succeed, so the file on
|
|
185
|
+
disk is either fully patched or untouched::
|
|
186
|
+
|
|
187
|
+
result = fbxkit.patch_file("hero.fbx", [
|
|
188
|
+
{"op": "rename", "target": "**[type=mesh]",
|
|
189
|
+
"pattern": "^SM_", "replacement": ""},
|
|
190
|
+
{"op": "retarget_textures", "prefix": "//server/proj/tex"},
|
|
191
|
+
], output="hero_clean.fbx")
|
|
192
|
+
|
|
193
|
+
result.status # "ok" | "partial" | "error"
|
|
194
|
+
result.changed # how many individual changes were made
|
|
195
|
+
result.saved # whether the file was actually written
|
|
196
|
+
"""
|
|
197
|
+
with open(path) as document:
|
|
198
|
+
return document.apply_and_save(
|
|
199
|
+
ops, path=output, dry_run=dry_run, atomic=atomic, **save_args
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def available():
|
|
204
|
+
"""Every registered operation and its parameters, as data.
|
|
205
|
+
|
|
206
|
+
Serve this straight to a frontend to generate a form or validate a request,
|
|
207
|
+
instead of transcribing the parameter list on both sides and having the
|
|
208
|
+
copies go stale.
|
|
209
|
+
"""
|
|
210
|
+
return registry.available()
|