unitytk 0.0.3__tar.gz → 0.0.4__tar.gz
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.
- {unitytk-0.0.3/unitytk.egg-info → unitytk-0.0.4}/PKG-INFO +2 -2
- {unitytk-0.0.3 → unitytk-0.0.4}/docs/README.md +1 -1
- {unitytk-0.0.3 → unitytk-0.0.4}/test/test_c130_fcr_integration.py +1 -1
- {unitytk-0.0.3 → unitytk-0.0.4}/test/test_deploy_templates.py +41 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/test/test_fade_window_preservation.py +3 -1
- {unitytk-0.0.3 → unitytk-0.0.4}/unitytk/__init__.py +1 -1
- {unitytk-0.0.3 → unitytk-0.0.4}/unitytk/template_deployer.py +98 -8
- {unitytk-0.0.3 → unitytk-0.0.4}/unitytk/templates/AudioEventController.cs +2 -3
- {unitytk-0.0.3 → unitytk-0.0.4}/unitytk/templates/EmissiveGroupController.cs +22 -5
- {unitytk-0.0.3 → unitytk-0.0.4}/unitytk/templates/LightmapMetadataController.cs +2 -2
- {unitytk-0.0.3 → unitytk-0.0.4}/unitytk/templates/README.md +12 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/unitytk/templates/RenderOpacityController.cs +17 -8
- {unitytk-0.0.3 → unitytk-0.0.4}/unitytk/templates/ShadowPlaneController.cs +2 -2
- {unitytk-0.0.3 → unitytk-0.0.4}/unitytk/templates/ShotMetadataController.cs +2 -2
- {unitytk-0.0.3 → unitytk-0.0.4}/unitytk/templates/UnitytkSettings.cs +90 -4
- {unitytk-0.0.3 → unitytk-0.0.4/unitytk.egg-info}/PKG-INFO +2 -2
- {unitytk-0.0.3 → unitytk-0.0.4}/LICENSE +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/MANIFEST.in +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/pyproject.toml +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/setup.cfg +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/test/test_asset_delivery.py +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/test/test_audio_events_integration.py +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/test/test_bool_to_fade_curve.py +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/test/test_c5m_visibility.py +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/test/test_clip_name_contract_integration.py +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/test/test_launcher.py +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/test/test_lightmap_metadata_integration.py +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/test/test_render_opacity_controller.py +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/test/test_render_opacity_e2e.py +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/test/test_render_opacity_integration.py +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/test/test_scene_builder.py +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/test/test_shadow_plane_integration.py +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/test/test_shared_material_standalone.py +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/test/test_shot_metadata_integration.py +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/test/test_shots_audio_sidebyside_integration.py +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/test/test_standalone_enduser.py +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/unitytk/asset_delivery.py +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/unitytk/launcher.py +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/unitytk/scene_builder.py +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/unitytk/templates/EmissiveGroups.hlsl +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/unitytk/templates/EmissiveGroupsLit.shader +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/unitytk/templates/M3trik.Unitytk.asmdef +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/unitytk/templates/__init__.py +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/unitytk.egg-info/SOURCES.txt +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/unitytk.egg-info/dependency_links.txt +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/unitytk.egg-info/requires.txt +0 -0
- {unitytk-0.0.3 → unitytk-0.0.4}/unitytk.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: unitytk
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: A modular Python toolkit for Unity interaction.
|
|
5
5
|
Author-email: Ryan Simpson <m3trik@outlook.com>
|
|
6
6
|
License: MIT
|
|
@@ -15,7 +15,7 @@ License-File: LICENSE
|
|
|
15
15
|
Requires-Dist: pythontk>=0.7.71
|
|
16
16
|
Dynamic: license-file
|
|
17
17
|
|
|
18
|
-
[](../test/)
|
|
19
19
|
|
|
20
20
|
# unitytk
|
|
21
21
|
|
|
@@ -641,7 +641,7 @@ class TestC130FcrIntegration(unittest.TestCase):
|
|
|
641
641
|
|
|
642
642
|
@classmethod
|
|
643
643
|
def setUpClass(cls):
|
|
644
|
-
# Repo-local temp_tests (already on O:
|
|
644
|
+
# Repo-local temp_tests (already on the O: drive; C: is often full).
|
|
645
645
|
_temp_base = os.path.join(os.path.dirname(__file__), "temp_tests")
|
|
646
646
|
os.makedirs(_temp_base, exist_ok=True)
|
|
647
647
|
cls.temp_dir = tempfile.mkdtemp(prefix="c130_fcr_integ_", dir=_temp_base)
|
|
@@ -92,6 +92,47 @@ class DeployTemplateTest(unittest.TestCase):
|
|
|
92
92
|
with open(shader, encoding="utf-8") as fh:
|
|
93
93
|
self.assertIn('#include "EmissiveGroups.hlsl"', fh.read())
|
|
94
94
|
|
|
95
|
+
def test_status_reports_not_installed_then_up_to_date(self):
|
|
96
|
+
"""The panels' Status action: absent → installed=False with the full
|
|
97
|
+
bundle missing; after deploy → current version, nothing missing."""
|
|
98
|
+
before = TemplateDeployer.status(self.project)
|
|
99
|
+
self.assertFalse(before["installed"])
|
|
100
|
+
self.assertFalse(before["up_to_date"])
|
|
101
|
+
self.assertIn("UnitytkSettings.cs", before["missing"])
|
|
102
|
+
|
|
103
|
+
TemplateDeployer.deploy_package(self.project)
|
|
104
|
+
after = TemplateDeployer.status(self.project)
|
|
105
|
+
self.assertTrue(after["installed"])
|
|
106
|
+
self.assertEqual(after["version"], unitytk.__version__)
|
|
107
|
+
self.assertEqual(after["missing"], [])
|
|
108
|
+
self.assertTrue(after["up_to_date"])
|
|
109
|
+
|
|
110
|
+
def test_status_flags_a_partial_deploy(self):
|
|
111
|
+
"""A deleted file shows in ``missing`` and clears ``up_to_date`` — the
|
|
112
|
+
compile-coupled set is only valid whole."""
|
|
113
|
+
TemplateDeployer.deploy_package(self.project)
|
|
114
|
+
os.remove(os.path.join(self.package, "UnitytkSettings.cs"))
|
|
115
|
+
info = TemplateDeployer.status(self.project)
|
|
116
|
+
self.assertEqual(info["missing"], ["UnitytkSettings.cs"])
|
|
117
|
+
self.assertFalse(info["up_to_date"])
|
|
118
|
+
|
|
119
|
+
def test_uninstall_removes_only_our_package(self):
|
|
120
|
+
"""Uninstall deletes the deployed folder (True), no-ops when absent
|
|
121
|
+
(False), and REFUSES a folder whose package.json is not ours — never
|
|
122
|
+
delete a directory we merely expected to own."""
|
|
123
|
+
self.assertFalse(TemplateDeployer.uninstall_package(self.project))
|
|
124
|
+
|
|
125
|
+
TemplateDeployer.deploy_package(self.project)
|
|
126
|
+
self.assertTrue(TemplateDeployer.uninstall_package(self.project))
|
|
127
|
+
self.assertFalse(os.path.isdir(self.package))
|
|
128
|
+
|
|
129
|
+
os.makedirs(self.package)
|
|
130
|
+
with open(os.path.join(self.package, "package.json"), "w") as fh:
|
|
131
|
+
json.dump({"name": "com.example.other"}, fh)
|
|
132
|
+
with self.assertRaises(ValueError):
|
|
133
|
+
TemplateDeployer.uninstall_package(self.project)
|
|
134
|
+
self.assertTrue(os.path.isdir(self.package), "foreign folder untouched")
|
|
135
|
+
|
|
95
136
|
def test_deploy_package_never_writes_project_config(self):
|
|
96
137
|
"""Per-project settings live in ProjectSettings/UnitytkSettings.json,
|
|
97
138
|
owned by the project — deploying must not create or clobber them."""
|
|
@@ -209,7 +209,9 @@ class TestFadeWindowPreservation(unittest.TestCase):
|
|
|
209
209
|
|
|
210
210
|
@classmethod
|
|
211
211
|
def setUpClass(cls):
|
|
212
|
-
|
|
212
|
+
# Repo-local temp_tests (repo_root is the WORKSPACE root, for sys.path
|
|
213
|
+
# only -- joining it here scattered Unity projects outside the repo).
|
|
214
|
+
base = os.path.join(os.path.dirname(__file__), "temp_tests")
|
|
213
215
|
os.makedirs(base, exist_ok=True)
|
|
214
216
|
cls.temp_dir = tempfile.mkdtemp(prefix="fade_window_", dir=base)
|
|
215
217
|
cls.fbx_path = None
|
|
@@ -22,6 +22,7 @@ from __future__ import annotations
|
|
|
22
22
|
|
|
23
23
|
import json
|
|
24
24
|
import os
|
|
25
|
+
import shutil
|
|
25
26
|
from importlib import resources
|
|
26
27
|
from typing import List
|
|
27
28
|
|
|
@@ -106,15 +107,10 @@ class TemplateDeployer:
|
|
|
106
107
|
f"Not a Unity project (no Assets/ folder): {project_root!r}"
|
|
107
108
|
)
|
|
108
109
|
|
|
109
|
-
package_dir =
|
|
110
|
-
root = resources.files(TEMPLATE_PACKAGE)
|
|
111
|
-
names = sorted(
|
|
112
|
-
entry.name
|
|
113
|
-
for entry in root.iterdir()
|
|
114
|
-
if entry.name.endswith(CODE_SUFFIXES) or entry.name == "README.md"
|
|
115
|
-
)
|
|
110
|
+
package_dir = cls.package_dir(project_root)
|
|
116
111
|
written = [
|
|
117
|
-
cls.deploy(name, os.path.join(package_dir, name))
|
|
112
|
+
cls.deploy(name, os.path.join(package_dir, name))
|
|
113
|
+
for name in cls._bundle_names()
|
|
118
114
|
]
|
|
119
115
|
|
|
120
116
|
manifest_path = os.path.join(package_dir, "package.json")
|
|
@@ -125,6 +121,100 @@ class TemplateDeployer:
|
|
|
125
121
|
|
|
126
122
|
return sorted(written)
|
|
127
123
|
|
|
124
|
+
@classmethod
|
|
125
|
+
def package_dir(cls, project_root: str) -> str:
|
|
126
|
+
"""The embedded package's folder inside *project_root* (may not exist)."""
|
|
127
|
+
return os.path.join(project_root, "Packages", UPM_PACKAGE_NAME)
|
|
128
|
+
|
|
129
|
+
@classmethod
|
|
130
|
+
def status(cls, project_root: str) -> dict:
|
|
131
|
+
"""Report the deployed state of the embedded package in *project_root*.
|
|
132
|
+
|
|
133
|
+
Never raises for an absent/partial deployment -- "not installed" is a
|
|
134
|
+
reportable state, not an error (the DCC panels' *Status* action renders
|
|
135
|
+
this dict into their log).
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
dict: ``installed`` (bool), ``package_dir`` (str),
|
|
139
|
+
``version`` (str or None -- the deployed ``package.json`` version),
|
|
140
|
+
``bundled_version`` (str -- this unitytk release),
|
|
141
|
+
``missing`` (list[str] -- bundled files absent from the deploy),
|
|
142
|
+
``up_to_date`` (bool -- current version and nothing missing).
|
|
143
|
+
"""
|
|
144
|
+
import unitytk
|
|
145
|
+
|
|
146
|
+
package_dir = cls.package_dir(project_root)
|
|
147
|
+
bundled = cls._bundle_names()
|
|
148
|
+
result = {
|
|
149
|
+
"installed": False,
|
|
150
|
+
"package_dir": package_dir,
|
|
151
|
+
"version": None,
|
|
152
|
+
"bundled_version": unitytk.__version__,
|
|
153
|
+
"missing": list(bundled),
|
|
154
|
+
"up_to_date": False,
|
|
155
|
+
}
|
|
156
|
+
manifest_path = os.path.join(package_dir, "package.json")
|
|
157
|
+
if not os.path.isfile(manifest_path):
|
|
158
|
+
return result
|
|
159
|
+
|
|
160
|
+
result["installed"] = True
|
|
161
|
+
try:
|
|
162
|
+
with open(manifest_path, encoding="utf-8") as fh:
|
|
163
|
+
result["version"] = json.load(fh).get("version")
|
|
164
|
+
except (OSError, ValueError):
|
|
165
|
+
pass # corrupt manifest: installed but version unknown
|
|
166
|
+
|
|
167
|
+
present = set(os.listdir(package_dir))
|
|
168
|
+
result["missing"] = [n for n in bundled if n not in present]
|
|
169
|
+
result["up_to_date"] = (
|
|
170
|
+
result["version"] == unitytk.__version__ and not result["missing"]
|
|
171
|
+
)
|
|
172
|
+
return result
|
|
173
|
+
|
|
174
|
+
@classmethod
|
|
175
|
+
def uninstall_package(cls, project_root: str) -> bool:
|
|
176
|
+
"""Remove the embedded package from *project_root*.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
bool: True when the package folder was removed; False when there
|
|
180
|
+
was nothing deployed.
|
|
181
|
+
|
|
182
|
+
Raises:
|
|
183
|
+
ValueError: the folder at the package path is NOT ours -- its
|
|
184
|
+
``package.json`` is absent or names a different package.
|
|
185
|
+
Refusing beats deleting a directory we merely expected to own.
|
|
186
|
+
"""
|
|
187
|
+
package_dir = cls.package_dir(project_root)
|
|
188
|
+
if not os.path.isdir(package_dir):
|
|
189
|
+
return False
|
|
190
|
+
|
|
191
|
+
name = None
|
|
192
|
+
try:
|
|
193
|
+
with open(
|
|
194
|
+
os.path.join(package_dir, "package.json"), encoding="utf-8"
|
|
195
|
+
) as fh:
|
|
196
|
+
name = json.load(fh).get("name")
|
|
197
|
+
except (OSError, ValueError):
|
|
198
|
+
pass
|
|
199
|
+
if name != UPM_PACKAGE_NAME:
|
|
200
|
+
raise ValueError(
|
|
201
|
+
f"Refusing to delete {package_dir!r}: its package.json does "
|
|
202
|
+
f"not identify it as {UPM_PACKAGE_NAME!r}."
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
shutil.rmtree(package_dir)
|
|
206
|
+
return True
|
|
207
|
+
|
|
208
|
+
@staticmethod
|
|
209
|
+
def _bundle_names() -> "List[str]":
|
|
210
|
+
"""The bundled deployables, sorted: code set + the catalog README."""
|
|
211
|
+
root = resources.files(TEMPLATE_PACKAGE)
|
|
212
|
+
return sorted(
|
|
213
|
+
entry.name
|
|
214
|
+
for entry in root.iterdir()
|
|
215
|
+
if entry.name.endswith(CODE_SUFFIXES) or entry.name == "README.md"
|
|
216
|
+
)
|
|
217
|
+
|
|
128
218
|
@staticmethod
|
|
129
219
|
def _package_manifest() -> dict:
|
|
130
220
|
"""The generated ``package.json`` -- version mirrors this release."""
|
|
@@ -230,9 +230,8 @@ public class AudioEventImporter : AssetPostprocessor
|
|
|
230
230
|
// so the next import of this asset would inject every event twice.
|
|
231
231
|
_pendingManifests.Remove(assetPath);
|
|
232
232
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
CarrierImport.EnableUserProps(assetImporter);
|
|
233
|
+
// String channel: user properties arrive with Unity's default
|
|
234
|
+
// importer settings (probe-verified) — nothing to configure here.
|
|
236
235
|
}
|
|
237
236
|
|
|
238
237
|
// Why we attach the controller to the prefab ROOT, not the
|
|
@@ -215,18 +215,35 @@ public class EmissiveGroupImporter : AssetPostprocessor
|
|
|
215
215
|
{
|
|
216
216
|
// Fresh-import reset: JSON stranded by an import that aborted between
|
|
217
217
|
// the callbacks must not resurrect on a re-export without the carrier.
|
|
218
|
+
// Touches NO importer settings — the manifest is a string channel that
|
|
219
|
+
// arrives with Unity's defaults; only Maya keyed weights need a flag,
|
|
220
|
+
// requested per-asset on detection below.
|
|
218
221
|
_pending.Remove(assetPath);
|
|
219
|
-
|
|
220
|
-
if (!ShouldProcess()) return;
|
|
221
|
-
CarrierImport.EnableUserProps(assetImporter);
|
|
222
222
|
}
|
|
223
223
|
|
|
224
224
|
void OnPostprocessGameObjectWithUserProperties(
|
|
225
225
|
GameObject go, string[] propNames, object[] values)
|
|
226
226
|
{
|
|
227
227
|
if (!ShouldProcess()) return;
|
|
228
|
-
if (CarrierImport.TryGetProp(propNames, values, ATTR_MANIFEST, out var json))
|
|
229
|
-
|
|
228
|
+
if (!CarrierImport.TryGetProp(propNames, values, ATTR_MANIFEST, out var json))
|
|
229
|
+
return;
|
|
230
|
+
_pending[assetPath] = json;
|
|
231
|
+
|
|
232
|
+
// Maya keyed weights ride animated custom properties (per-group
|
|
233
|
+
// `attr` records). Only such assets need the importer flag — request
|
|
234
|
+
// it here so unrelated FBX keep the user's own settings.
|
|
235
|
+
var payload = ParsePayload(json, assetPath);
|
|
236
|
+
if (payload != null)
|
|
237
|
+
{
|
|
238
|
+
foreach (var rec in payload.groups)
|
|
239
|
+
{
|
|
240
|
+
if (rec != null && !string.IsNullOrEmpty(rec.attr))
|
|
241
|
+
{
|
|
242
|
+
CarrierImport.RequestAnimatedCustomProperties(assetPath);
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
230
247
|
}
|
|
231
248
|
|
|
232
249
|
// --- Keyed weights: rebind DCC weight curves to the controller --------
|
|
@@ -134,8 +134,8 @@ public class LightmapMetadataImporter : AssetPostprocessor
|
|
|
134
134
|
// the callbacks must not resurrect on a re-export without the carrier.
|
|
135
135
|
_pending.Remove(assetPath);
|
|
136
136
|
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
// String channel: user properties arrive with Unity's default
|
|
138
|
+
// importer settings (probe-verified) — nothing to configure here.
|
|
139
139
|
// NOTE: deliberately does NOT touch generateSecondaryUV here — at
|
|
140
140
|
// preprocess we don't yet know if this FBX carries Maya lightmaps, and
|
|
141
141
|
// with no allowlist config every FBX is "allowed", so blindly flipping
|
|
@@ -82,6 +82,18 @@ Settings are stored as JSON in `ProjectSettings/UnitytkSettings.json` (missing
|
|
|
82
82
|
file/keys = everything on, all FBX). External tools can write it directly;
|
|
83
83
|
changes are picked up automatically (no restart needed).
|
|
84
84
|
|
|
85
|
+
### Safety: unrelated assets are never modified
|
|
86
|
+
|
|
87
|
+
The importers **never touch a model's import settings up front**. Channel
|
|
88
|
+
detection works with Unity's default settings, so an FBX that carries no
|
|
89
|
+
unitytk metadata comes through the pipeline byte-identical to a project
|
|
90
|
+
without unitytk installed. Only when an asset is detected to carry *animated*
|
|
91
|
+
channel data (opacity fades, Maya keyed emissive weights) are the two settings
|
|
92
|
+
that data needs — *Animated Custom Properties* on, *Resample Curves* off —
|
|
93
|
+
enabled **on that one asset**, with a single automatic reimport (logged).
|
|
94
|
+
Nothing is ever switched off, and no other asset's settings are read or
|
|
95
|
+
written.
|
|
96
|
+
|
|
85
97
|
---
|
|
86
98
|
|
|
87
99
|
## Importing an FBX
|
|
@@ -209,15 +209,11 @@ public class RenderOpacityImporter : AssetPostprocessor
|
|
|
209
209
|
// an import that aborts between the callbacks (another postprocessor
|
|
210
210
|
// throwing) strands destroyed Transform refs here — EnsureControllers
|
|
211
211
|
// would trip over them on the NEXT import of this asset.
|
|
212
|
+
//
|
|
213
|
+
// Deliberately touches NO importer settings: the settings this channel
|
|
214
|
+
// needs are requested per-asset on detection (see Step 1) and applied
|
|
215
|
+
// by UnitytkImportConfigurator — unrelated FBX are never modified.
|
|
212
216
|
_opacityTransforms.Remove(assetPath);
|
|
213
|
-
|
|
214
|
-
if (!ShouldProcess()) return;
|
|
215
|
-
|
|
216
|
-
CarrierImport.EnableUserProps(assetImporter);
|
|
217
|
-
|
|
218
|
-
var importer = assetImporter as ModelImporter;
|
|
219
|
-
if (importer != null && importer.resampleCurves)
|
|
220
|
-
importer.resampleCurves = false;
|
|
221
217
|
}
|
|
222
218
|
|
|
223
219
|
// --- Step 1: Record opacity transforms ------------------------------------
|
|
@@ -232,6 +228,13 @@ public class RenderOpacityImporter : AssetPostprocessor
|
|
|
232
228
|
if (!ShouldProcess()) return;
|
|
233
229
|
if (!CarrierImport.HasProp(propNames, ATTR_NAME)) return;
|
|
234
230
|
|
|
231
|
+
// This asset carries opacity data: it needs the custom-property curve
|
|
232
|
+
// (flattened 'opacity') and SPARSE visibility keys — the fade windows
|
|
233
|
+
// live in the gaps, which resampling destroys. Requested here, applied
|
|
234
|
+
// (with a one-time reimport of this asset only) after the batch.
|
|
235
|
+
CarrierImport.RequestAnimatedCustomProperties(assetPath);
|
|
236
|
+
CarrierImport.RequestSparseCurves(assetPath);
|
|
237
|
+
|
|
235
238
|
if (!_opacityTransforms.ContainsKey(assetPath))
|
|
236
239
|
_opacityTransforms[assetPath] = new HashSet<Transform>();
|
|
237
240
|
_opacityTransforms[assetPath].Add(go.transform);
|
|
@@ -253,6 +256,12 @@ public class RenderOpacityImporter : AssetPostprocessor
|
|
|
253
256
|
{
|
|
254
257
|
if (!ShouldProcess()) return;
|
|
255
258
|
|
|
259
|
+
// Detection pass: the importer settings this conversion depends on
|
|
260
|
+
// (custom-prop curves present, resampling off) aren't in effect yet —
|
|
261
|
+
// converting RESAMPLED visibility would fabricate wrong fades. The
|
|
262
|
+
// configured reimport that follows runs the real conversion.
|
|
263
|
+
if (CarrierImport.IsDetectionPass(assetPath, assetImporter)) return;
|
|
264
|
+
|
|
256
265
|
var opaPaths = EnsureControllers(assetPath, root.transform);
|
|
257
266
|
if (opaPaths.Count == 0) return;
|
|
258
267
|
|
|
@@ -116,8 +116,8 @@ public class ShadowPlaneImporter : AssetPostprocessor
|
|
|
116
116
|
// the callbacks must not resurrect on a re-export without the carrier.
|
|
117
117
|
_pending.Remove(assetPath);
|
|
118
118
|
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
// String channel: user properties arrive with Unity's default
|
|
120
|
+
// importer settings (probe-verified) — nothing to configure here.
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
// ---- silhouette texture: pin import settings by naming convention ----
|
|
@@ -105,8 +105,8 @@ public class ShotMetadataImporter : AssetPostprocessor
|
|
|
105
105
|
// the callbacks must not resurrect on a re-export without the carrier.
|
|
106
106
|
_pending.Remove(assetPath);
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
// String channel: user properties arrive with Unity's default
|
|
109
|
+
// importer settings (probe-verified) — nothing to configure here.
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
void OnPostprocessGameObjectWithUserProperties(
|
|
@@ -212,12 +212,83 @@ public static class CarrierImport
|
|
|
212
212
|
return false;
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
|
|
216
|
-
|
|
215
|
+
// ---- Per-asset import-settings scoping -------------------------------
|
|
216
|
+
//
|
|
217
|
+
// Channel DETECTION needs no importer settings: FBX user properties reach
|
|
218
|
+
// OnPostprocessGameObjectWithUserProperties even with Unity's defaults
|
|
219
|
+
// (verified against a live batch 6000.3: string channels AND the animated
|
|
220
|
+
// 'opacity' attr arrive with importAnimatedCustomProperties=false — only
|
|
221
|
+
// the flattened curve needs the flag). So unitytk never touches a model
|
|
222
|
+
// importer up front; a channel importer that discovers it needs a setting
|
|
223
|
+
// REQUESTS it here for that one asset, and the pending set is applied
|
|
224
|
+
// (with a single reimport of just that asset) after the import batch.
|
|
225
|
+
// Unrelated FBX are never modified, and nothing is ever switched OFF —
|
|
226
|
+
// the user's own import settings are left exactly as they had them.
|
|
227
|
+
|
|
228
|
+
static readonly HashSet<string> _needCustomProps = new HashSet<string>();
|
|
229
|
+
static readonly HashSet<string> _needSparseCurves = new HashSet<string>();
|
|
230
|
+
|
|
231
|
+
/// <summary>This asset needs animated custom-property curves
|
|
232
|
+
/// (opacity / Maya keyed emissive weights).</summary>
|
|
233
|
+
public static void RequestAnimatedCustomProperties(string assetPath)
|
|
234
|
+
=> _needCustomProps.Add(assetPath);
|
|
235
|
+
|
|
236
|
+
/// <summary>This asset needs sparse (unresampled) curves — the opacity
|
|
237
|
+
/// fade windows live in the gaps between keys.</summary>
|
|
238
|
+
public static void RequestSparseCurves(string assetPath)
|
|
239
|
+
=> _needSparseCurves.Add(assetPath);
|
|
240
|
+
|
|
241
|
+
/// <summary>True while *importer*'s current settings can't yet satisfy the
|
|
242
|
+
/// asset's requested channels — i.e. this pass is detection-only and a
|
|
243
|
+
/// configured reimport is coming. Curve-rewriting importers skip their
|
|
244
|
+
/// work on such a pass rather than transform resampled data.</summary>
|
|
245
|
+
public static bool IsDetectionPass(string assetPath, AssetImporter importer)
|
|
217
246
|
{
|
|
218
247
|
var mi = importer as ModelImporter;
|
|
219
|
-
if (mi
|
|
220
|
-
|
|
248
|
+
if (mi == null) return false;
|
|
249
|
+
if (_needCustomProps.Contains(assetPath) && !mi.importAnimatedCustomProperties)
|
|
250
|
+
return true;
|
|
251
|
+
if (_needSparseCurves.Contains(assetPath) && mi.resampleCurves)
|
|
252
|
+
return true;
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/// <summary>Apply every pending request; called from
|
|
257
|
+
/// <c>UnitytkImportConfigurator.OnPostprocessAllAssets</c> (after the
|
|
258
|
+
/// import batch, when reimporting is legal). Reimports only assets whose
|
|
259
|
+
/// settings actually changed, so a configured asset never loops.</summary>
|
|
260
|
+
public static void ApplyPendingImportSettings()
|
|
261
|
+
{
|
|
262
|
+
if (_needCustomProps.Count == 0 && _needSparseCurves.Count == 0)
|
|
263
|
+
return;
|
|
264
|
+
|
|
265
|
+
var paths = new HashSet<string>(_needCustomProps);
|
|
266
|
+
paths.UnionWith(_needSparseCurves);
|
|
267
|
+
foreach (var assetPath in paths)
|
|
268
|
+
{
|
|
269
|
+
bool wantProps = _needCustomProps.Remove(assetPath);
|
|
270
|
+
bool wantSparse = _needSparseCurves.Remove(assetPath);
|
|
271
|
+
var mi = AssetImporter.GetAtPath(assetPath) as ModelImporter;
|
|
272
|
+
if (mi == null) continue;
|
|
273
|
+
|
|
274
|
+
var enabled = new List<string>();
|
|
275
|
+
if (wantProps && !mi.importAnimatedCustomProperties)
|
|
276
|
+
{
|
|
277
|
+
mi.importAnimatedCustomProperties = true;
|
|
278
|
+
enabled.Add("Animated Custom Properties");
|
|
279
|
+
}
|
|
280
|
+
if (wantSparse && mi.resampleCurves)
|
|
281
|
+
{
|
|
282
|
+
mi.resampleCurves = false;
|
|
283
|
+
enabled.Add("unresampled curves (Resample Curves off)");
|
|
284
|
+
}
|
|
285
|
+
if (enabled.Count == 0) continue;
|
|
286
|
+
|
|
287
|
+
Debug.Log($"[unitytk] '{assetPath}' carries animated channel data — "
|
|
288
|
+
+ string.Join(" + ", enabled)
|
|
289
|
+
+ " set on this asset only; reimporting it once.");
|
|
290
|
+
mi.SaveAndReimport();
|
|
291
|
+
}
|
|
221
292
|
}
|
|
222
293
|
|
|
223
294
|
/// <summary>Warn when a payload comes from a newer producer schema than this importer.</summary>
|
|
@@ -231,6 +302,21 @@ public static class CarrierImport
|
|
|
231
302
|
}
|
|
232
303
|
}
|
|
233
304
|
|
|
305
|
+
/// <summary>
|
|
306
|
+
/// Applies the per-asset import-settings requests collected during the batch
|
|
307
|
+
/// (see the scoping notes on <see cref="CarrierImport"/>). Lives in its own
|
|
308
|
+
/// postprocessor because reimporting from OnPostprocessAllAssets is the
|
|
309
|
+
/// supported pattern; doing it mid-import is not.
|
|
310
|
+
/// </summary>
|
|
311
|
+
public class UnitytkImportConfigurator : AssetPostprocessor
|
|
312
|
+
{
|
|
313
|
+
static void OnPostprocessAllAssets(
|
|
314
|
+
string[] imported, string[] deleted, string[] moved, string[] movedFrom)
|
|
315
|
+
{
|
|
316
|
+
CarrierImport.ApplyPendingImportSettings();
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
234
320
|
/// <summary>
|
|
235
321
|
/// Edit ▸ Project Settings ▸ unitytk — the native home for "what is this
|
|
236
322
|
/// package doing to my imports": one toggle per channel plus the FBX
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: unitytk
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: A modular Python toolkit for Unity interaction.
|
|
5
5
|
Author-email: Ryan Simpson <m3trik@outlook.com>
|
|
6
6
|
License: MIT
|
|
@@ -15,7 +15,7 @@ License-File: LICENSE
|
|
|
15
15
|
Requires-Dist: pythontk>=0.7.71
|
|
16
16
|
Dynamic: license-file
|
|
17
17
|
|
|
18
|
-
[](../test/)
|
|
19
19
|
|
|
20
20
|
# unitytk
|
|
21
21
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|