usd2msd 0.8.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.
usd2msd/__init__.py ADDED
@@ -0,0 +1,31 @@
1
+ # Copyright (C) 2020-2025 Motphys Technology Co., Ltd. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+
16
+ from usd2msd.convert import Converter
17
+
18
+ __all__ = ["Converter"]
19
+
20
+
21
+ def register_plugin():
22
+ from importlib.resources import as_file, files
23
+
24
+ from pxr import Plug
25
+
26
+ plugin_path = files("usd2msd.assets").joinpath("usd_plugin")
27
+ with as_file(plugin_path) as path:
28
+ assert Plug.Registry().RegisterPlugins(str(path.absolute()))
29
+
30
+
31
+ register_plugin()
@@ -0,0 +1,16 @@
1
+ #usda 1.0
2
+ (
3
+
4
+ )
5
+
6
+ class "MotrixPhysicsGeomAPI"
7
+ {
8
+ float motrix:physics:geom:spring_k = 2500.0
9
+ float motrix:physics:geom:spring_d = 100.0
10
+ float motrix:physics:geom:impedance_d0 = 0.9
11
+ float motrix:physics:geom:impedance_dmax = 0.95
12
+ float motrix:physics:geom:impedance_width = 0.001
13
+ float motrix:physics:geom:impedance_middle = 0.5
14
+ float motrix:physics:geom:impedance_power = 2.0
15
+ int motrix:physics:geom:priority = 20
16
+ }
@@ -0,0 +1,16 @@
1
+ #usda 1.0
2
+ (
3
+
4
+ )
5
+
6
+ class "MotrixPhysicsGeomAPI"
7
+ {
8
+ float motrix:physics:geom:spring_k = 2500.0
9
+ float motrix:physics:geom:spring_d = 100.0
10
+ float motrix:physics:geom:impedance_d0 = 0.9
11
+ float motrix:physics:geom:impedance_dmax = 0.95
12
+ float motrix:physics:geom:impedance_width = 0.001
13
+ float motrix:physics:geom:impedance_middle = 0.5
14
+ float motrix:physics:geom:impedance_power = 2.0
15
+ int motrix:physics:geom:priority = 20
16
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "Plugins": [
3
+ {
4
+ "Info": {
5
+ "Types": {
6
+ "MotrixShaderAPI": {
7
+ "apiSchemaCanOnlyApplyTo": ["UsdShadeShader"],
8
+ "autoGenerated": false,
9
+ "bases": ["UsdAPISchemaBase"],
10
+ "schemaIdentifier": "MotrixShaderAPI",
11
+ "schemaKind": "singleApplyAPI"
12
+ }
13
+ }
14
+ },
15
+ "Name": "MotrixShade",
16
+ "ResourcePath": ".",
17
+ "Root": "shade",
18
+ "Type": "resource"
19
+ },
20
+ {
21
+ "Info": {
22
+ "Types": {
23
+ "MotrixPhysicsGeomAPI": {
24
+ "apiSchemaCanOnlyApplyTo": ["GPrim"],
25
+ "autoGenerated": false,
26
+ "bases": ["UsdAPISchemaBase"],
27
+ "schemaIdentifier": "MotrixPhysicsGeomAPI",
28
+ "schemaKind": "singleApplyAPI"
29
+ }
30
+ }
31
+ },
32
+ "Name": "MotrixPhysics",
33
+ "ResourcePath": ".",
34
+ "Root": "physics",
35
+ "Type": "resource"
36
+ }
37
+ ]
38
+ }
@@ -0,0 +1,10 @@
1
+ #usda 1.0
2
+ (
3
+
4
+ )
5
+
6
+ class "MotrixShaderAPI"
7
+ {
8
+ int motrix:shader:depth_bias = 0
9
+ }
10
+
usd2msd/common.py ADDED
@@ -0,0 +1,131 @@
1
+ # Copyright (C) 2020-2025 Motphys Technology Co., Ltd. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+
16
+ from collections.abc import Iterable
17
+ from math import isinf, pi
18
+ from pathlib import Path
19
+
20
+ import numpy as np
21
+ from numpy.typing import NDArray
22
+ from PIL import Image as pil
23
+ from PIL.Image import Image
24
+ from pxr.Sdf import PropertySpec, Spec
25
+ from pxr.Usd import Prim
26
+
27
+ import motrixsim.msd as msd
28
+
29
+ degree2radians = pi / 180
30
+
31
+
32
+ def linear_rgb_to_srgb(linear_rgb: NDArray) -> NDArray:
33
+ """Convert linear RGB values to sRGB space (vectorized).
34
+
35
+ USD's default source color space is Linear Rec.709 (lin_rec709_scene).
36
+ Bevy expects sRGB values for base_color, so we convert linear to sRGB.
37
+
38
+ Reference: https://openusd.org/release/user_guides/color_user_guide.html
39
+ """
40
+ linear_rgb = np.asarray(linear_rgb)
41
+ return np.where(
42
+ linear_rgb <= 0.0031308,
43
+ linear_rgb * 12.92,
44
+ 1.055 * np.power(linear_rgb, 1.0 / 2.4) - 0.055,
45
+ )
46
+
47
+
48
+ def rgb_append_a(rgb: NDArray) -> NDArray:
49
+ # Convert from linear to sRGB before storing
50
+ srgb = linear_rgb_to_srgb(rgb)
51
+ return np.append(srgb, 1)
52
+
53
+
54
+ def tint_brightness_to_rgba(tint: NDArray, brightness: float) -> NDArray:
55
+ """Combine OmniPBR diffuse_tint and albedo_brightness into RGBA color.
56
+
57
+ This is used as the base color multiplier when a diffuse texture is present.
58
+ Final color = diffuse_texture * diffuse_tint * albedo_brightness
59
+
60
+ Color space note:
61
+ - USD's default source color space is Linear Rec.709 (lin_rec709_scene).
62
+ - Reference: https://openusd.org/release/user_guides/color_user_guide.html
63
+ - We convert to sRGB because Bevy expects sRGB values for base_color.
64
+ """
65
+ rgb = tint * brightness
66
+ srgb = linear_rgb_to_srgb(rgb)
67
+ return np.append(srgb, 1.0)
68
+
69
+
70
+ def merge_rgba(rgb: NDArray, alpha: float, enable_opacity: bool) -> NDArray:
71
+ # Convert from linear to sRGB before storing
72
+ srgb = linear_rgb_to_srgb(rgb)
73
+ return np.append(srgb, alpha if enable_opacity else 1)
74
+
75
+
76
+ def merge_rgba_texture(rgb: Path | None, alpha: Path | None, enable_opacity: bool) -> Image | Path | None:
77
+ if enable_opacity:
78
+ if alpha is not None:
79
+ alpha_img = pil.open(alpha).convert("L")
80
+ if rgb is not None:
81
+ rgba_img = pil.open(rgb).convert("RGB")
82
+ rgba_img.putalpha(alpha_img)
83
+ return rgba_img
84
+ else:
85
+ rgba_img = pil.new("RGB", alpha_img.size, (255, 255, 255))
86
+ rgba_img.putalpha(alpha_img)
87
+ return rgba_img
88
+ else:
89
+ return rgb
90
+ else:
91
+ return rgb
92
+
93
+
94
+ def opacity_threshold2alpha_mode(threshold: float) -> msd.AlphaMode:
95
+ if threshold >= 1.0:
96
+ return msd.AlphaMode.opaque()
97
+ elif threshold > 0.0:
98
+ return msd.AlphaMode.mask(threshold)
99
+ else:
100
+ # Use Opaque instead of Blend when opacity_threshold <= 0.
101
+ # Blend mode causes incorrect depth sorting with double-sided materials,
102
+ # resulting in back faces being rendered in front of front faces.
103
+ return msd.AlphaMode.opaque()
104
+
105
+
106
+ def common_parent(paths: Iterable[Path]) -> Path:
107
+ split_paths = [i.parts for i in paths]
108
+ common_parts = []
109
+ for parts in zip(*split_paths):
110
+ if all(part == parts[0] for part in parts):
111
+ common_parts.append(parts[0])
112
+ else:
113
+ break
114
+
115
+ return Path(*common_parts)
116
+
117
+
118
+ def inf2None(x: float) -> float | None:
119
+ if isinf(x):
120
+ return None
121
+ return x
122
+
123
+
124
+ def last_spec(prim: Prim, prop_names: set[str] | None = None) -> Spec:
125
+ prim_specs = prim.GetPrimStack()
126
+ for prim_spec in prim_specs:
127
+ prop_spec: PropertySpec
128
+ for prop_spec in prim_spec.properties:
129
+ if not prop_names or prop_spec.name in prop_names:
130
+ return prim_spec
131
+ return prim_specs[-1]