OpenRCT2-ObjectCommon 0.1.3__tar.gz → 0.1.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.
Files changed (31) hide show
  1. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/PKG-INFO +2 -2
  2. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/openrct2_object_common/placement.py +8 -9
  3. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/pyproject.toml +2 -2
  4. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/tests/test_placement.py +32 -6
  5. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/.github/workflows/lint.yml +0 -0
  6. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/.github/workflows/publish.yml +0 -0
  7. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/.github/workflows/pytest.yml +0 -0
  8. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/.gitignore +0 -0
  9. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/.yamllint.yaml +0 -0
  10. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/LICENSE +0 -0
  11. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/README.md +0 -0
  12. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/openrct2_object_common/__init__.py +0 -0
  13. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/openrct2_object_common/blender/__init__.py +0 -0
  14. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/openrct2_object_common/blender/lights.py +0 -0
  15. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/openrct2_object_common/blender/mesh_extract.py +0 -0
  16. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/openrct2_object_common/blender/modal.py +0 -0
  17. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/openrct2_object_common/blender/props.py +0 -0
  18. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/openrct2_object_common/cli.py +0 -0
  19. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/openrct2_object_common/config.py +0 -0
  20. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/openrct2_object_common/objectjson.py +0 -0
  21. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/openrct2_object_common/parkobj.py +0 -0
  22. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/openrct2_object_common/py.typed +0 -0
  23. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/openrct2_object_common/testing.py +0 -0
  24. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/scripts/ci/set_version.py +0 -0
  25. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/tests/conftest.py +0 -0
  26. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/tests/test_blender.py +0 -0
  27. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/tests/test_blender_shared.py +0 -0
  28. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/tests/test_cli.py +0 -0
  29. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/tests/test_config.py +0 -0
  30. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/tests/test_objectjson.py +0 -0
  31. {openrct2_objectcommon-0.1.3 → openrct2_objectcommon-0.1.4}/tests/test_parkobj.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: OpenRCT2-ObjectCommon
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: Shared config, CLI, .parkobj, and object.json scaffolding for OpenRCT2 object generators.
5
5
  Project-URL: Homepage, https://github.com/alex-parisi/OpenRCT2-ObjectCommon
6
6
  Project-URL: Repository, https://github.com/alex-parisi/OpenRCT2-ObjectCommon
@@ -9,7 +9,7 @@ License-Expression: GPL-3.0-or-later
9
9
  License-File: LICENSE
10
10
  Requires-Python: >=3.11
11
11
  Requires-Dist: numpy>=1.26
12
- Requires-Dist: openrct2-x7-renderer>=0.3.1
12
+ Requires-Dist: openrct2-x7-renderer>=0.3.3
13
13
  Requires-Dist: pillow>=10.0
14
14
  Requires-Dist: pyyaml>=6.0
15
15
  Provides-Extra: blender
@@ -13,7 +13,7 @@ import math
13
13
 
14
14
  import numpy as np
15
15
  from numpy.typing import NDArray
16
- from openrct2_x7_renderer.geometry import rotate_x, rotate_y, rotate_z
16
+ from openrct2_x7_renderer.geometry import rotate_x, rotate_y, rotate_z, split_mesh_by_ghost
17
17
  from openrct2_x7_renderer.mesh import Mesh
18
18
  from openrct2_x7_renderer.ray_trace import SceneBuilder
19
19
  from openrct2_x7_renderer.types import Model
@@ -42,17 +42,16 @@ def add_model_to_scene(
42
42
  Placements whose ``mesh_index`` is ``-1`` (an empty slot) are skipped. With
43
43
  ``clamp_frame=True`` a placement with fewer frames falls back to its last
44
44
  frame (scenery's animated poses); with ``False`` the frame is indexed exactly
45
- (vehicle frames are uniform across placements). ``mask`` is the per-model
46
- ``MeshFlag`` bitmask (e.g. ghost / mask geometry).
45
+ (vehicle frames are uniform across placements). ``mask`` is the base per-model
46
+ ``MeshFlag`` bitmask (e.g. ghost / mask geometry); faces whose material is a
47
+ ghost are additionally split into their own ``MeshFlag.GHOST`` model.
47
48
  """
48
49
  for mesh_frames in model.meshes:
49
50
  idx = min(frame, len(mesh_frames) - 1) if clamp_frame else frame
50
51
  mf = mesh_frames[idx]
51
52
  if mf.mesh_index == -1:
52
53
  continue
53
- builder.add_model(
54
- meshes[mf.mesh_index],
55
- orientation_to_matrix(mf.orientation),
56
- mf.position.astype(np.float64),
57
- mask,
58
- )
54
+ matrix = orientation_to_matrix(mf.orientation)
55
+ position = mf.position.astype(np.float64)
56
+ for sub_mesh, sub_mask in split_mesh_by_ghost(meshes[mf.mesh_index], mask):
57
+ builder.add_model(sub_mesh, matrix, position, sub_mask)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "OpenRCT2-ObjectCommon"
3
- version = "0.1.3"
3
+ version = "0.1.4"
4
4
  description = "Shared config, CLI, .parkobj, and object.json scaffolding for OpenRCT2 object generators."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -9,7 +9,7 @@ authors = [
9
9
  { name = "Alex Parisi", email = "alex@atparisi.com" }
10
10
  ]
11
11
  dependencies = [
12
- "openrct2-x7-renderer>=0.3.1",
12
+ "openrct2-x7-renderer>=0.3.3",
13
13
  "numpy>=1.26",
14
14
  "pillow>=10.0",
15
15
  "pyyaml>=6.0",
@@ -7,6 +7,8 @@ scene is needed.
7
7
 
8
8
  import numpy as np
9
9
  from openrct2_object_common.placement import add_model_to_scene, orientation_to_matrix
10
+ from openrct2_x7_renderer.constants import MeshFlag
11
+ from openrct2_x7_renderer.mesh import Material, Mesh
10
12
  from openrct2_x7_renderer.types import MeshFrame, Model
11
13
 
12
14
 
@@ -20,6 +22,18 @@ class _StubBuilder:
20
22
  self.calls.append((mesh, matrix, translation, mask))
21
23
 
22
24
 
25
+ def _mesh(*, is_ghost: bool = False) -> Mesh:
26
+ """A one-triangle mesh with a single (optionally ghost) material."""
27
+ return Mesh(
28
+ vertices=np.zeros((3, 3), dtype=np.float32),
29
+ normals=np.zeros((3, 3), dtype=np.float32),
30
+ uvs=np.zeros((3, 2), dtype=np.float32),
31
+ faces=np.array([[0, 1, 2]], dtype=np.uint32),
32
+ face_materials=np.array([0], dtype=np.uint32),
33
+ materials=[Material(is_ghost=is_ghost)],
34
+ )
35
+
36
+
23
37
  def test_orientation_to_matrix_zero_is_identity():
24
38
  m = orientation_to_matrix(np.zeros(3, dtype=np.float64))
25
39
  assert m.shape == (3, 3)
@@ -33,7 +47,7 @@ def test_orientation_to_matrix_is_orthonormal():
33
47
 
34
48
 
35
49
  def test_add_model_places_each_referenced_mesh():
36
- meshes = ["mesh0", "mesh1"]
50
+ meshes = [_mesh(), _mesh()]
37
51
  model = Model(meshes=[
38
52
  [MeshFrame(mesh_index=0, position=np.array([1.0, 2.0, 3.0]))],
39
53
  [MeshFrame(mesh_index=1)],
@@ -41,21 +55,33 @@ def test_add_model_places_each_referenced_mesh():
41
55
  builder = _StubBuilder()
42
56
  add_model_to_scene(builder, meshes, model, mask=7)
43
57
 
44
- assert [c[0] for c in builder.calls] == ["mesh0", "mesh1"]
58
+ assert [c[0] for c in builder.calls] == meshes
45
59
  np.testing.assert_allclose(builder.calls[0][2], [1.0, 2.0, 3.0])
46
60
  assert all(c[3] == 7 for c in builder.calls)
47
61
 
48
62
 
49
63
  def test_add_model_skips_empty_slots():
64
+ only = _mesh()
50
65
  model = Model(meshes=[[MeshFrame(mesh_index=-1)], [MeshFrame(mesh_index=0)]])
51
66
  builder = _StubBuilder()
52
- add_model_to_scene(builder, ["only"], model)
53
- assert [c[0] for c in builder.calls] == ["only"]
67
+ add_model_to_scene(builder, [only], model)
68
+ assert [c[0] for c in builder.calls] == [only]
54
69
 
55
70
 
56
71
  def test_clamp_frame_falls_back_to_last_pose():
57
72
  # Placement has 2 frames; requesting frame 5 with clamp uses the last (idx 1).
73
+ a, b = _mesh(), _mesh()
58
74
  model = Model(meshes=[[MeshFrame(mesh_index=0), MeshFrame(mesh_index=1)]])
59
75
  builder = _StubBuilder()
60
- add_model_to_scene(builder, ["a", "b"], model, frame=5, clamp_frame=True)
61
- assert [c[0] for c in builder.calls] == ["b"]
76
+ add_model_to_scene(builder, [a, b], model, frame=5, clamp_frame=True)
77
+ assert [c[0] for c in builder.calls] == [b]
78
+
79
+
80
+ def test_add_model_marks_ghost_material_geometry():
81
+ # A wholly ghost placement is added once, carrying the base mask OR'd with
82
+ # MeshFlag.GHOST so the renderer traces through it.
83
+ model = Model(meshes=[[MeshFrame(mesh_index=0)]])
84
+ builder = _StubBuilder()
85
+ add_model_to_scene(builder, [_mesh(is_ghost=True)], model, mask=2)
86
+ assert len(builder.calls) == 1
87
+ assert builder.calls[0][3] == 2 | int(MeshFlag.GHOST)