rayd 0.2.2__tar.gz → 0.2.3__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 (135) hide show
  1. {rayd-0.2.2 → rayd-0.2.3}/PKG-INFO +1 -1
  2. {rayd-0.2.2 → rayd-0.2.3}/docs/api_reference.md +58 -0
  3. {rayd-0.2.2 → rayd-0.2.3}/docs/multi_bounce_reflection_trace.md +8 -1
  4. {rayd-0.2.2 → rayd-0.2.3}/docs/reflection_trace_gpu_dedup.md +4 -3
  5. {rayd-0.2.2 → rayd-0.2.3}/include/rayd/edge.h +9 -1
  6. {rayd-0.2.2 → rayd-0.2.3}/include/rayd/intersection.h +14 -2
  7. {rayd-0.2.2 → rayd-0.2.3}/include/rayd/reflection.h +10 -2
  8. {rayd-0.2.2 → rayd-0.2.3}/include/rayd/scene/scene.h +5 -0
  9. {rayd-0.2.2 → rayd-0.2.3}/include/rayd/types.h +20 -0
  10. {rayd-0.2.2 → rayd-0.2.3}/pyproject.toml +1 -1
  11. {rayd-0.2.2 → rayd-0.2.3}/rayd/torch/__init__.py +2 -0
  12. {rayd-0.2.2 → rayd-0.2.3}/rayd/torch/_native.py +24 -0
  13. {rayd-0.2.2 → rayd-0.2.3}/rayd/torch/scene.py +33 -1
  14. {rayd-0.2.2 → rayd-0.2.3}/rayd/torch/types.py +65 -0
  15. {rayd-0.2.2 → rayd-0.2.3}/src/rayd.cpp +36 -7
  16. {rayd-0.2.2 → rayd-0.2.3}/src/scene/scene.cpp +132 -8
  17. {rayd-0.2.2 → rayd-0.2.3}/tests/drjit/test_geometry.py +134 -1
  18. {rayd-0.2.2 → rayd-0.2.3}/tests/torch/test_geometry.py +128 -0
  19. {rayd-0.2.2 → rayd-0.2.3}/.github/constraints/drjit-build.txt +0 -0
  20. {rayd-0.2.2 → rayd-0.2.3}/.github/workflows/pypi.yml +0 -0
  21. {rayd-0.2.2 → rayd-0.2.3}/.gitignore +0 -0
  22. {rayd-0.2.2 → rayd-0.2.3}/CMakeLists.txt +0 -0
  23. {rayd-0.2.2 → rayd-0.2.3}/LICENSE +0 -0
  24. {rayd-0.2.2 → rayd-0.2.3}/README.md +0 -0
  25. {rayd-0.2.2 → rayd-0.2.3}/cmake/embed_ptx.cmake +0 -0
  26. {rayd-0.2.2 → rayd-0.2.3}/docs/build_optimization.md +0 -0
  27. {rayd-0.2.2 → rayd-0.2.3}/docs/edge_bvh_bottleneck_analysis.md +0 -0
  28. {rayd-0.2.2 → rayd-0.2.3}/docs/edge_bvh_lbvh_treelet_improvement_plan.md +0 -0
  29. {rayd-0.2.2 → rayd-0.2.3}/docs/edge_bvh_optimization_log.md +0 -0
  30. {rayd-0.2.2 → rayd-0.2.3}/docs/edge_bvh_stage3_preparation.md +0 -0
  31. {rayd-0.2.2 → rayd-0.2.3}/docs/performance_benchmark.json +0 -0
  32. {rayd-0.2.2 → rayd-0.2.3}/docs/performance_benchmark.png +0 -0
  33. {rayd-0.2.2 → rayd-0.2.3}/docs/slang_interop.md +0 -0
  34. {rayd-0.2.2 → rayd-0.2.3}/examples/README.md +0 -0
  35. {rayd-0.2.2 → rayd-0.2.3}/examples/basics/camera_edge_sampling_gradient.py +0 -0
  36. {rayd-0.2.2 → rayd-0.2.3}/examples/basics/nearest_edge_query.py +0 -0
  37. {rayd-0.2.2 → rayd-0.2.3}/examples/basics/ray_mesh_intersection.py +0 -0
  38. {rayd-0.2.2 → rayd-0.2.3}/examples/renderer/README.md +0 -0
  39. {rayd-0.2.2 → rayd-0.2.3}/examples/renderer/cornell_box.png +0 -0
  40. {rayd-0.2.2 → rayd-0.2.3}/examples/renderer/cornell_box.py +0 -0
  41. {rayd-0.2.2 → rayd-0.2.3}/examples/renderer/rfdt.png +0 -0
  42. {rayd-0.2.2 → rayd-0.2.3}/include/rayd/camera.h +0 -0
  43. {rayd-0.2.2 → rayd-0.2.3}/include/rayd/constants.h +0 -0
  44. {rayd-0.2.2 → rayd-0.2.3}/include/rayd/fwd.h +0 -0
  45. {rayd-0.2.2 → rayd-0.2.3}/include/rayd/mesh.h +0 -0
  46. {rayd-0.2.2 → rayd-0.2.3}/include/rayd/optix.h +0 -0
  47. {rayd-0.2.2 → rayd-0.2.3}/include/rayd/ray.h +0 -0
  48. {rayd-0.2.2 → rayd-0.2.3}/include/rayd/rayd.h +0 -0
  49. {rayd-0.2.2 → rayd-0.2.3}/include/rayd/scene/scene_edge.h +0 -0
  50. {rayd-0.2.2 → rayd-0.2.3}/include/rayd/scene/scene_optix.h +0 -0
  51. {rayd-0.2.2 → rayd-0.2.3}/include/rayd/slang/interop.h +0 -0
  52. {rayd-0.2.2 → rayd-0.2.3}/include/rayd/slang/interop_types.h +0 -0
  53. {rayd-0.2.2 → rayd-0.2.3}/include/rayd/slang/rayd.slang +0 -0
  54. {rayd-0.2.2 → rayd-0.2.3}/include/rayd/transform.h +0 -0
  55. {rayd-0.2.2 → rayd-0.2.3}/include/rayd/utils.h +0 -0
  56. {rayd-0.2.2 → rayd-0.2.3}/include/rayd_slang.slang +0 -0
  57. {rayd-0.2.2 → rayd-0.2.3}/rayd/__init__.py +0 -0
  58. {rayd-0.2.2 → rayd-0.2.3}/rayd/slang/__init__.py +0 -0
  59. {rayd-0.2.2 → rayd-0.2.3}/rayd/torch/_convert.py +0 -0
  60. {rayd-0.2.2 → rayd-0.2.3}/rayd/torch/_env.py +0 -0
  61. {rayd-0.2.2 → rayd-0.2.3}/rayd/torch/_interop_patch.py +0 -0
  62. {rayd-0.2.2 → rayd-0.2.3}/rayd/torch/_state.py +0 -0
  63. {rayd-0.2.2 → rayd-0.2.3}/rayd/torch/_util.py +0 -0
  64. {rayd-0.2.2 → rayd-0.2.3}/rayd/torch/camera.py +0 -0
  65. {rayd-0.2.2 → rayd-0.2.3}/rayd/torch/mesh.py +0 -0
  66. {rayd-0.2.2 → rayd-0.2.3}/src/camera.cpp +0 -0
  67. {rayd-0.2.2 → rayd-0.2.3}/src/mesh.cpp +0 -0
  68. {rayd-0.2.2 → rayd-0.2.3}/src/multipath/reflection_dedup.cu +0 -0
  69. {rayd-0.2.2 → rayd-0.2.3}/src/multipath/reflection_dedup.h +0 -0
  70. {rayd-0.2.2 → rayd-0.2.3}/src/multipath/reflection_trace.cu +0 -0
  71. {rayd-0.2.2 → rayd-0.2.3}/src/multipath/reflection_trace_host.cpp +0 -0
  72. {rayd-0.2.2 → rayd-0.2.3}/src/multipath/reflection_trace_host.h +0 -0
  73. {rayd-0.2.2 → rayd-0.2.3}/src/multipath/reflection_trace_params.h +0 -0
  74. {rayd-0.2.2 → rayd-0.2.3}/src/multipath/reflection_trace_ptx.h +0 -0
  75. {rayd-0.2.2 → rayd-0.2.3}/src/native_launch_audit.cpp +0 -0
  76. {rayd-0.2.2 → rayd-0.2.3}/src/native_launch_audit.h +0 -0
  77. {rayd-0.2.2 → rayd-0.2.3}/src/optix.cpp +0 -0
  78. {rayd-0.2.2 → rayd-0.2.3}/src/scene/edge_bvh.cu +0 -0
  79. {rayd-0.2.2 → rayd-0.2.3}/src/scene/edge_bvh.h +0 -0
  80. {rayd-0.2.2 → rayd-0.2.3}/src/scene/edge_bvh_config.h +0 -0
  81. {rayd-0.2.2 → rayd-0.2.3}/src/scene/experimental/edge_bvh_ploc.h +0 -0
  82. {rayd-0.2.2 → rayd-0.2.3}/src/scene/experimental/edge_bvh_ploc_build.inc +0 -0
  83. {rayd-0.2.2 → rayd-0.2.3}/src/scene/experimental/edge_bvh_ploc_kernels.inc +0 -0
  84. {rayd-0.2.2 → rayd-0.2.3}/src/scene/experimental/scene_edge_ploc.inc +0 -0
  85. {rayd-0.2.2 → rayd-0.2.3}/src/scene/scene_edge.cpp +0 -0
  86. {rayd-0.2.2 → rayd-0.2.3}/src/scene/scene_optix.cpp +0 -0
  87. {rayd-0.2.2 → rayd-0.2.3}/src/slang_interop.cpp +0 -0
  88. {rayd-0.2.2 → rayd-0.2.3}/tests/__init__.py +0 -0
  89. {rayd-0.2.2 → rayd-0.2.3}/tests/baseline_cases.py +0 -0
  90. {rayd-0.2.2 → rayd-0.2.3}/tests/baseline_utils.py +0 -0
  91. {rayd-0.2.2 → rayd-0.2.3}/tests/baselines/drjit_v0_4_6/camera/perspective_fov.json +0 -0
  92. {rayd-0.2.2 → rayd-0.2.3}/tests/baselines/drjit_v0_4_6/camera/perspective_intrinsic.json +0 -0
  93. {rayd-0.2.2 → rayd-0.2.3}/tests/baselines/drjit_v0_4_6/camera/sample_primary_ray.json +0 -0
  94. {rayd-0.2.2 → rayd-0.2.3}/tests/baselines/drjit_v0_4_6/camera/sample_to_world.json +0 -0
  95. {rayd-0.2.2 → rayd-0.2.3}/tests/baselines/drjit_v0_4_6/camera/world_to_sample.json +0 -0
  96. {rayd-0.2.2 → rayd-0.2.3}/tests/baselines/drjit_v0_4_6/edges/primary_edge_sampling.json +0 -0
  97. {rayd-0.2.2 → rayd-0.2.3}/tests/baselines/drjit_v0_4_6/edges/secondary_edges.json +0 -0
  98. {rayd-0.2.2 → rayd-0.2.3}/tests/baselines/drjit_v0_4_6/geometry/batched_hits.json +0 -0
  99. {rayd-0.2.2 → rayd-0.2.3}/tests/baselines/drjit_v0_4_6/geometry/constant_hit.json +0 -0
  100. {rayd-0.2.2 → rayd-0.2.3}/tests/baselines/drjit_v0_4_6/geometry/degenerate.json +0 -0
  101. {rayd-0.2.2 → rayd-0.2.3}/tests/baselines/drjit_v0_4_6/geometry/miss.json +0 -0
  102. {rayd-0.2.2 → rayd-0.2.3}/tests/baselines/drjit_v0_4_6/geometry/multi_mesh.json +0 -0
  103. {rayd-0.2.2 → rayd-0.2.3}/tests/baselines/drjit_v0_4_6/geometry/uv.json +0 -0
  104. {rayd-0.2.2 → rayd-0.2.3}/tests/baselines/drjit_v0_4_6/gradients/transform_gradients.json +0 -0
  105. {rayd-0.2.2 → rayd-0.2.3}/tests/baselines/drjit_v0_4_6/gradients/vertex_gradients.json +0 -0
  106. {rayd-0.2.2 → rayd-0.2.3}/tests/baselines/drjit_v0_4_6/manifest.json +0 -0
  107. {rayd-0.2.2 → rayd-0.2.3}/tests/baselines/drjit_v0_4_6/stress/repeated_run_summary.json +0 -0
  108. {rayd-0.2.2 → rayd-0.2.3}/tests/baselines/drjit_v0_4_6/transforms/composition_left_right.json +0 -0
  109. {rayd-0.2.2 → rayd-0.2.3}/tests/baselines/drjit_v0_4_6/transforms/direction_transform_checks.json +0 -0
  110. {rayd-0.2.2 → rayd-0.2.3}/tests/baselines/drjit_v0_4_6/transforms/matrix_entry_checks.json +0 -0
  111. {rayd-0.2.2 → rayd-0.2.3}/tests/baselines/drjit_v0_4_6/transforms/point_transform_checks.json +0 -0
  112. {rayd-0.2.2 → rayd-0.2.3}/tests/benchmark_edge_bvh_cutoff_compare.py +0 -0
  113. {rayd-0.2.2 → rayd-0.2.3}/tests/benchmark_edge_bvh_pressure.py +0 -0
  114. {rayd-0.2.2 → rayd-0.2.3}/tests/benchmark_edge_bvh_stages.py +0 -0
  115. {rayd-0.2.2 → rayd-0.2.3}/tests/benchmark_edge_queries.py +0 -0
  116. {rayd-0.2.2 → rayd-0.2.3}/tests/benchmark_support.py +0 -0
  117. {rayd-0.2.2 → rayd-0.2.3}/tests/drjit/__init__.py +0 -0
  118. {rayd-0.2.2 → rayd-0.2.3}/tests/drjit/test_baseline.py +0 -0
  119. {rayd-0.2.2 → rayd-0.2.3}/tests/profile_kernel_launches.py +0 -0
  120. {rayd-0.2.2 → rayd-0.2.3}/tests/slang/__init__.py +0 -0
  121. {rayd-0.2.2 → rayd-0.2.3}/tests/slang/cases/load_module_error.py +0 -0
  122. {rayd-0.2.2 → rayd-0.2.3}/tests/slang/cases/slang_ad_autograd.py +0 -0
  123. {rayd-0.2.2 → rayd-0.2.3}/tests/slang/cases/slang_ad_multi_param.py +0 -0
  124. {rayd-0.2.2 → rayd-0.2.3}/tests/slang/cases/slang_ad_optimize.py +0 -0
  125. {rayd-0.2.2 → rayd-0.2.3}/tests/slang/cases/slang_ad_struct_return.py +0 -0
  126. {rayd-0.2.2 → rayd-0.2.3}/tests/slang/cases/slang_rayd_intersect.py +0 -0
  127. {rayd-0.2.2 → rayd-0.2.3}/tests/slang/cases/slang_rayd_nearest_edge.py +0 -0
  128. {rayd-0.2.2 → rayd-0.2.3}/tests/slang/shaders/rayd_query.slang +0 -0
  129. {rayd-0.2.2 → rayd-0.2.3}/tests/slang/shaders/rayd_query_ad.slang +0 -0
  130. {rayd-0.2.2 → rayd-0.2.3}/tests/slang/test_interop.slang +0 -0
  131. {rayd-0.2.2 → rayd-0.2.3}/tests/slang/test_slang.py +0 -0
  132. {rayd-0.2.2 → rayd-0.2.3}/tests/test_project_metadata.py +0 -0
  133. {rayd-0.2.2 → rayd-0.2.3}/tests/test_torch_interop_patch.py +0 -0
  134. {rayd-0.2.2 → rayd-0.2.3}/tests/torch/__init__.py +0 -0
  135. {rayd-0.2.2 → rayd-0.2.3}/tests/torch/test_edge_bvh_pressure.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rayd
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: RayD: minimalist differentiable ray tracing package wrapping Dr.Jit and OptiX.
5
5
  Author-Email: Xingyu Chen <xic063@ucsd.edu>
6
6
  License: BSD-3-Clause
@@ -16,10 +16,14 @@ Public top-level exports:
16
16
  - `Camera`
17
17
  - `Ray`
18
18
  - `Intersection`
19
+ - `ReflectionChain`
19
20
  - `NearestPointEdge`
20
21
  - `NearestRayEdge`
21
22
  - `PrimaryEdgeSample`
22
23
  - `SecondaryEdgeInfo`
24
+ - `SceneEdgeInfo`
25
+ - `SceneEdgeTopology`
26
+ - `SceneGlobalGeometry`
23
27
 
24
28
  All array-valued inputs and outputs use Dr.Jit CUDA arrays or tensors.
25
29
 
@@ -137,8 +141,15 @@ Methods:
137
141
  - `is_ready() -> bool`
138
142
  - `has_pending_updates() -> bool`
139
143
  - `intersect(ray, active=True) -> Intersection`
144
+ - `trace_reflections(ray, max_bounces, active=True, *, deduplicate=False, canonical_prim_table=None, image_source_tolerance=1e-5) -> ReflectionChain`
140
145
  - `nearest_edge(point, active=True) -> NearestPointEdge`
141
146
  - `nearest_edge(ray, active=True) -> NearestRayEdge`
147
+ - `edge_info() -> SceneEdgeInfo`
148
+ - `edge_topology() -> SceneEdgeTopology`
149
+ - `global_geometry() -> SceneGlobalGeometry`
150
+ - `mesh_face_offsets()`
151
+ - `mesh_edge_offsets()`
152
+ - `mesh_vertex_offsets()`
142
153
 
143
154
  Properties:
144
155
 
@@ -251,11 +262,58 @@ Fields:
251
262
  - `barycentric`
252
263
  - `shape_id`
253
264
  - `prim_id`
265
+ - `local_prim_id`
266
+ - `global_prim_id`
254
267
 
255
268
  Methods:
256
269
 
257
270
  - `is_valid()`
258
271
 
272
+ Notes:
273
+
274
+ - `prim_id` is kept as the mesh-local triangle id for compatibility.
275
+ - `local_prim_id` is identical to `prim_id`.
276
+ - `global_prim_id` is the scene-global triangle id.
277
+
278
+ ### `ReflectionChain`
279
+
280
+ Result of `Scene.trace_reflections(...)`.
281
+
282
+ Fields:
283
+
284
+ - `bounce_count`
285
+ - `discovery_count`
286
+ - `representative_ray_index`
287
+ - `t`
288
+ - `hit_points`
289
+ - `geo_normals`
290
+ - `image_sources`
291
+ - `plane_points`
292
+ - `plane_normals`
293
+ - `shape_ids`
294
+ - `prim_ids`
295
+ - `local_prim_ids`
296
+ - `global_prim_ids`
297
+
298
+ Notes:
299
+
300
+ - `prim_ids` remains mesh-local for compatibility.
301
+ - `local_prim_ids` is identical to `prim_ids`.
302
+ - `global_prim_ids` is the scene-global triangle id chain.
303
+
304
+ ### `SceneGlobalGeometry`
305
+
306
+ Canonical scene-global geometry cache exposed by `Scene.global_geometry()`.
307
+
308
+ Fields:
309
+
310
+ - `vertices`
311
+ - `faces`
312
+ - `face_normal`
313
+ - `shape_id`
314
+ - `local_prim_id`
315
+ - `global_prim_id`
316
+
259
317
  ## Nearest-Edge Types
260
318
 
261
319
  ### `NearestPointEdge`
@@ -46,12 +46,19 @@ ReflectionChain trace_reflections(const RayDetached &ray,
46
46
  MaskDetached active = true) const;
47
47
  ```
48
48
 
49
+ Current API note:
50
+
51
+ - `prim_ids` is mesh-local for compatibility.
52
+ - `local_prim_ids` is identical to `prim_ids`.
53
+ - `global_prim_ids` is the scene-global triangle-id chain.
54
+
49
55
  ### Python (nanobind)
50
56
 
51
57
  ```python
52
58
  chain = scene.trace_reflections(ray, max_bounces=3)
53
59
  chain.bounce_count # drjit.cuda.Int [N]
54
60
  chain.prim_ids # drjit.cuda.Int [N*B]
61
+ chain.global_prim_ids# drjit.cuda.Int [N*B]
55
62
  chain.hit_points # drjit.cuda.Array3f [N*B]
56
63
  chain.image_sources # drjit.cuda.Array3f [N*B]
57
64
  # ...
@@ -63,6 +70,7 @@ chain.image_sources # drjit.cuda.Array3f [N*B]
63
70
  chain = scene.trace_reflections(ray, max_bounces=3)
64
71
  chain.bounce_count # torch.Tensor int32 [N]
65
72
  chain.prim_ids # torch.Tensor int32 [N, B]
73
+ chain.global_prim_ids# torch.Tensor int32 [N, B]
66
74
  chain.hit_points # torch.Tensor float32 [N, B, 3]
67
75
  chain.image_sources # torch.Tensor float32 [N, B, 3]
68
76
  # ...
@@ -1036,4 +1044,3 @@ file(WRITE "${OUTPUT_FILE}"
1036
1044
  )
1037
1045
  ```
1038
1046
 
1039
-
@@ -91,7 +91,8 @@ chain = scene.trace_reflections(ray, max_bounces=3,
91
91
  # PyTorch
92
92
  chain = scene.trace_reflections(ray, max_bounces=3, deduplicate=True)
93
93
  chain.ray_count # M (unique paths, not input ray count)
94
- chain.prim_ids # [M, B]
94
+ chain.prim_ids # [M, B] (mesh-local)
95
+ chain.global_prim_ids # [M, B] (scene-global)
95
96
  chain.image_sources # [M, B, 3]
96
97
  chain.discovery_count # [M]
97
98
  ```
@@ -110,8 +111,8 @@ New CUDA kernel: `reflection_dedup_build_keys`
110
111
 
111
112
  For each ray `i`:
112
113
  1. If `bounce_count[i] == 0` → key = `UINT64_MAX` (dead lane, sorted last)
113
- 2. Optionally remap prim_ids through `canonical_prim_table`
114
- 3. Hash the prim_id chain into a 64-bit key:
114
+ 2. Build / remap the scene-global prim chain through `canonical_prim_table`
115
+ 3. Hash the global prim-id chain into a 64-bit key:
115
116
 
116
117
  ```cuda
117
118
  __global__ void reflection_dedup_build_keys(
@@ -173,24 +173,32 @@ struct SceneEdgeInfo {
173
173
  struct SceneEdgeTopology {
174
174
  IntDetached v0;
175
175
  IntDetached v1;
176
+ IntDetached v0_global;
177
+ IntDetached v1_global;
176
178
  IntDetached face0_local;
177
179
  IntDetached face1_local;
178
180
  IntDetached face0_global;
179
181
  IntDetached face1_global;
180
182
  IntDetached opposite_vertex0;
181
183
  IntDetached opposite_vertex1;
184
+ IntDetached opposite_vertex0_global;
185
+ IntDetached opposite_vertex1_global;
182
186
 
183
187
  int size() const { return v0.size(); }
184
188
 
185
189
  DRJIT_STRUCT(SceneEdgeTopology,
186
190
  v0,
187
191
  v1,
192
+ v0_global,
193
+ v1_global,
188
194
  face0_local,
189
195
  face1_local,
190
196
  face0_global,
191
197
  face1_global,
192
198
  opposite_vertex0,
193
- opposite_vertex1)
199
+ opposite_vertex1,
200
+ opposite_vertex0_global,
201
+ opposite_vertex1_global)
194
202
  };
195
203
 
196
204
  } // namespace rayd
@@ -43,8 +43,20 @@ struct IntersectionData {
43
43
  Vec3f barycentric = zeros<Vec3f>(1);
44
44
  Int_ shape_id = full<Int_>(-1, 1);
45
45
  Int_ prim_id = full<Int_>(-1, 1);
46
-
47
- DRJIT_STRUCT(IntersectionData, t, p, n, geo_n, uv, barycentric, shape_id, prim_id)
46
+ Int_ local_prim_id = full<Int_>(-1, 1);
47
+ Int_ global_prim_id = full<Int_>(-1, 1);
48
+
49
+ DRJIT_STRUCT(IntersectionData,
50
+ t,
51
+ p,
52
+ n,
53
+ geo_n,
54
+ uv,
55
+ barycentric,
56
+ shape_id,
57
+ prim_id,
58
+ local_prim_id,
59
+ global_prim_id)
48
60
  };
49
61
 
50
62
  } // namespace rayd
@@ -32,6 +32,8 @@ struct ReflectionBounceData {
32
32
  Vec3f plane_normals = zeros<Vec3f>(1);
33
33
  Int_ shape_ids = full<Int_>(-1, 1);
34
34
  Int_ prim_ids = full<Int_>(-1, 1);
35
+ Int_ local_prim_ids = full<Int_>(-1, 1);
36
+ Int_ global_prim_ids = full<Int_>(-1, 1);
35
37
 
36
38
  DRJIT_STRUCT(ReflectionBounceData,
37
39
  t,
@@ -41,7 +43,9 @@ struct ReflectionBounceData {
41
43
  plane_points,
42
44
  plane_normals,
43
45
  shape_ids,
44
- prim_ids)
46
+ prim_ids,
47
+ local_prim_ids,
48
+ global_prim_ids)
45
49
  };
46
50
 
47
51
  template <typename Float_>
@@ -70,6 +74,8 @@ struct ReflectionChainData {
70
74
  Vec3f plane_normals = zeros<Vec3f>(1);
71
75
  Int_ shape_ids = full<Int_>(-1, 1);
72
76
  Int_ prim_ids = full<Int_>(-1, 1);
77
+ Int_ local_prim_ids = full<Int_>(-1, 1);
78
+ Int_ global_prim_ids = full<Int_>(-1, 1);
73
79
 
74
80
  DRJIT_STRUCT(ReflectionChainData,
75
81
  bounce_count,
@@ -82,7 +88,9 @@ struct ReflectionChainData {
82
88
  plane_points,
83
89
  plane_normals,
84
90
  shape_ids,
85
- prim_ids)
91
+ prim_ids,
92
+ local_prim_ids,
93
+ global_prim_ids)
86
94
  };
87
95
 
88
96
  template <typename Float_>
@@ -57,6 +57,8 @@ public:
57
57
  const MaskDetached &edge_mask() const;
58
58
  const IntDetached &mesh_face_offsets() const { return face_offsets_; }
59
59
  const IntDetached &mesh_edge_offsets() const { return edge_offsets_; }
60
+ const IntDetached &mesh_vertex_offsets() const { return vertex_offsets_; }
61
+ const SceneGlobalGeometry &global_geometry() const;
60
62
  uint64_t version() const { return scene_version_; }
61
63
  uint64_t edge_version() const { return edge_version_; }
62
64
  VectoriT<3, true> triangle_edge_indices(const IntDetached &prim_id, bool global = true) const;
@@ -107,6 +109,7 @@ private:
107
109
  bool vertices_dirty = false;
108
110
  bool transform_dirty = false;
109
111
  mutable bool edge_dirty = false;
112
+ int vertex_offset = 0;
110
113
  int face_offset = 0;
111
114
  int edge_offset = 0;
112
115
  };
@@ -126,12 +129,14 @@ private:
126
129
 
127
130
  IntDetached face_offsets_;
128
131
  IntDetached edge_offsets_;
132
+ IntDetached vertex_offsets_;
129
133
  TriangleInfo triangle_info_;
130
134
  TriangleInfoDetached triangle_info_detached_;
131
135
  TriangleUV triangle_uv_;
132
136
  TriangleUVDetached triangle_uv_detached_;
133
137
  Mask triangle_face_normal_mask_;
134
138
  MaskDetached triangle_face_normal_mask_detached_;
139
+ SceneGlobalGeometry global_geometry_;
135
140
  SecondaryEdgeInfo edge_info_;
136
141
  SceneEdgeTopology edge_topology_;
137
142
  IntDetached edge_shape_ids_;
@@ -130,4 +130,24 @@ using TriangleUVDetached = TriangleUvData<FloatDetached>;
130
130
  template <bool Detached_>
131
131
  using TriangleUVT = std::conditional_t<Detached_, TriangleUVDetached, TriangleUV>;
132
132
 
133
+ struct SceneGlobalGeometry {
134
+ Vector3f vertices;
135
+ Vector3iDetached faces;
136
+ Vector3f face_normal;
137
+ IntDetached shape_id;
138
+ IntDetached local_prim_id;
139
+ IntDetached global_prim_id;
140
+
141
+ int vertex_count() const { return vertices.x().size(); }
142
+ int face_count() const { return global_prim_id.size(); }
143
+
144
+ DRJIT_STRUCT(SceneGlobalGeometry,
145
+ vertices,
146
+ faces,
147
+ face_normal,
148
+ shape_id,
149
+ local_prim_id,
150
+ global_prim_id)
151
+ };
152
+
133
153
  } // namespace rayd
@@ -5,7 +5,7 @@ build-backend = "scikit_build_core.build"
5
5
 
6
6
  [project]
7
7
  name = "rayd"
8
- version = "0.2.2"
8
+ version = "0.2.3"
9
9
  description = "RayD: minimalist differentiable ray tracing package wrapping Dr.Jit and OptiX."
10
10
  readme = "README.md"
11
11
  authors = [{ name = "Xingyu Chen", email = "xic063@ucsd.edu" }]
@@ -24,6 +24,7 @@ from .types import (
24
24
  SceneSyncProfile,
25
25
  SceneEdgeInfo,
26
26
  SceneEdgeTopology,
27
+ SceneGlobalGeometry,
27
28
  SecondaryEdgeInfo,
28
29
  )
29
30
  from .mesh import Mesh
@@ -50,6 +51,7 @@ __all__ = [
50
51
  "SceneSyncProfile",
51
52
  "SceneEdgeInfo",
52
53
  "SceneEdgeTopology",
54
+ "SceneGlobalGeometry",
53
55
  "SecondaryEdgeInfo",
54
56
  "set_device",
55
57
  ]
@@ -37,6 +37,7 @@ from .types import (
37
37
  Ray,
38
38
  SceneEdgeInfo,
39
39
  SceneEdgeTopology,
40
+ SceneGlobalGeometry,
40
41
  SecondaryEdgeInfo,
41
42
  )
42
43
  from ._state import _MeshState, _CameraState
@@ -319,6 +320,8 @@ def _intersection_from_native(its: Any) -> Intersection:
319
320
  barycentric=_vec3_to_tensor(its.barycentric),
320
321
  shape_id=_scalar_array_to_tensor(its.shape_id),
321
322
  prim_id=_scalar_array_to_tensor(its.prim_id),
323
+ local_prim_id=_scalar_array_to_tensor(its.local_prim_id),
324
+ global_prim_id=_scalar_array_to_tensor(its.global_prim_id),
322
325
  )
323
326
 
324
327
 
@@ -337,6 +340,8 @@ def _reflection_chain_from_native(chain: Any) -> ReflectionChain:
337
340
  plane_normals = _vec3_to_tensor(chain.plane_normals)
338
341
  shape_ids = _scalar_array_to_tensor(chain.shape_ids)
339
342
  prim_ids = _scalar_array_to_tensor(chain.prim_ids)
343
+ local_prim_ids = _scalar_array_to_tensor(chain.local_prim_ids)
344
+ global_prim_ids = _scalar_array_to_tensor(chain.global_prim_ids)
340
345
 
341
346
  if ray_count <= 0 and hasattr(bounce_count, "numel"):
342
347
  ray_count = int(bounce_count.numel())
@@ -358,6 +363,8 @@ def _reflection_chain_from_native(chain: Any) -> ReflectionChain:
358
363
  plane_normals = dr.reshape(type(plane_normals), plane_normals, shape=(ray_count, max_bounces, 3))
359
364
  shape_ids = dr.reshape(type(shape_ids), shape_ids, shape=(ray_count, max_bounces))
360
365
  prim_ids = dr.reshape(type(prim_ids), prim_ids, shape=(ray_count, max_bounces))
366
+ local_prim_ids = dr.reshape(type(local_prim_ids), local_prim_ids, shape=(ray_count, max_bounces))
367
+ global_prim_ids = dr.reshape(type(global_prim_ids), global_prim_ids, shape=(ray_count, max_bounces))
361
368
 
362
369
  return ReflectionChain(
363
370
  bounce_count=bounce_count,
@@ -371,6 +378,8 @@ def _reflection_chain_from_native(chain: Any) -> ReflectionChain:
371
378
  plane_normals=plane_normals,
372
379
  shape_ids=shape_ids,
373
380
  prim_ids=prim_ids,
381
+ local_prim_ids=local_prim_ids,
382
+ global_prim_ids=global_prim_ids,
374
383
  max_bounces=max_bounces,
375
384
  ray_count=ray_count,
376
385
  )
@@ -433,12 +442,27 @@ def _scene_edge_topology_from_native(info: Any) -> SceneEdgeTopology:
433
442
  return SceneEdgeTopology(
434
443
  v0=_scalar_array_to_tensor(info.v0),
435
444
  v1=_scalar_array_to_tensor(info.v1),
445
+ v0_global=_scalar_array_to_tensor(info.v0_global),
446
+ v1_global=_scalar_array_to_tensor(info.v1_global),
436
447
  face0_local=_scalar_array_to_tensor(info.face0_local),
437
448
  face1_local=_scalar_array_to_tensor(info.face1_local),
438
449
  face0_global=_scalar_array_to_tensor(info.face0_global),
439
450
  face1_global=_scalar_array_to_tensor(info.face1_global),
440
451
  opposite_vertex0=_scalar_array_to_tensor(info.opposite_vertex0),
441
452
  opposite_vertex1=_scalar_array_to_tensor(info.opposite_vertex1),
453
+ opposite_vertex0_global=_scalar_array_to_tensor(info.opposite_vertex0_global),
454
+ opposite_vertex1_global=_scalar_array_to_tensor(info.opposite_vertex1_global),
455
+ )
456
+
457
+
458
+ def _scene_global_geometry_from_native(geometry: Any) -> SceneGlobalGeometry:
459
+ return SceneGlobalGeometry(
460
+ vertices=_vec3_to_tensor(geometry.vertices),
461
+ faces=_vec3_to_tensor(geometry.faces),
462
+ face_normal=_vec3_to_tensor(geometry.face_normal),
463
+ shape_id=_scalar_array_to_tensor(geometry.shape_id),
464
+ local_prim_id=_scalar_array_to_tensor(geometry.local_prim_id),
465
+ global_prim_id=_scalar_array_to_tensor(geometry.global_prim_id),
442
466
  )
443
467
 
444
468
 
@@ -12,11 +12,20 @@ from ._util import (
12
12
  _normalize_vector_tensor,
13
13
  )
14
14
  from ._convert import _scalar_array_to_tensor, _tensor_to_mask, _tensor_to_matrix4, _tensor_to_vec3, _to_torch_struct
15
- from .types import ReflectionChain, Intersection, Ray, SceneSyncProfile, SceneEdgeInfo, SceneEdgeTopology
15
+ from .types import (
16
+ ReflectionChain,
17
+ Intersection,
18
+ Ray,
19
+ SceneSyncProfile,
20
+ SceneEdgeInfo,
21
+ SceneEdgeTopology,
22
+ SceneGlobalGeometry,
23
+ )
16
24
  from ._state import _MeshState
17
25
  from ._native import (
18
26
  _allocate_native_scene_cache_id,
19
27
  _build_native_mesh,
28
+ _prepare_native_scene_cache,
20
29
  _scene_cache_refresh_policy,
21
30
  _mesh_to_world_tensor,
22
31
  _ray_batch_size,
@@ -25,6 +34,7 @@ from ._native import (
25
34
  _scene_cache_tokens,
26
35
  _scene_edge_info_from_native,
27
36
  _scene_edge_topology_from_native,
37
+ _scene_global_geometry_from_native,
28
38
  _scene_intersect_impl,
29
39
  _scene_trace_reflections_impl,
30
40
  _scene_nearest_point_impl,
@@ -250,6 +260,28 @@ class Scene:
250
260
  raise RuntimeError("Scene.mesh_edge_offsets(): internal detached scene is unavailable.")
251
261
  return _scalar_array_to_tensor(self._native_scene.mesh_edge_offsets()).torch()
252
262
 
263
+ def mesh_vertex_offsets(self) -> _torch.Tensor:
264
+ self._require_ready()
265
+ if self._native_scene is None:
266
+ raise RuntimeError("Scene.mesh_vertex_offsets(): internal detached scene is unavailable.")
267
+ return _scalar_array_to_tensor(self._native_scene.mesh_vertex_offsets()).torch()
268
+
269
+ def global_geometry(self) -> SceneGlobalGeometry:
270
+ self._require_query_ready()
271
+ mesh_states, topology_token, rebuild_token, vertex_tokens, left_tokens, right_tokens, refresh_policy, edge_mask = self._query_cache_inputs()
272
+ scene = _prepare_native_scene_cache(
273
+ self._query_cache_id,
274
+ mesh_states,
275
+ topology_token,
276
+ rebuild_token,
277
+ vertex_tokens,
278
+ left_tokens,
279
+ right_tokens,
280
+ refresh_policy,
281
+ edge_mask,
282
+ )
283
+ return _to_torch_struct(_scene_global_geometry_from_native(scene.global_geometry()))
284
+
253
285
  def triangle_edge_indices(self, prim_id: Any, global_: bool = True, **kwargs: Any) -> tuple[_torch.Tensor, _torch.Tensor, _torch.Tensor]:
254
286
  self._require_ready()
255
287
  if "global" in kwargs:
@@ -33,6 +33,8 @@ class Intersection(_StructRepr):
33
33
  "barycentric": object,
34
34
  "shape_id": object,
35
35
  "prim_id": object,
36
+ "local_prim_id": object,
37
+ "global_prim_id": object,
36
38
  }
37
39
 
38
40
  def __init__(
@@ -45,6 +47,8 @@ class Intersection(_StructRepr):
45
47
  barycentric: Any = None,
46
48
  shape_id: Any = None,
47
49
  prim_id: Any = None,
50
+ local_prim_id: Any = None,
51
+ global_prim_id: Any = None,
48
52
  ):
49
53
  self.t = t
50
54
  self.p = p
@@ -54,6 +58,8 @@ class Intersection(_StructRepr):
54
58
  self.barycentric = barycentric
55
59
  self.shape_id = shape_id
56
60
  self.prim_id = prim_id
61
+ self.local_prim_id = local_prim_id
62
+ self.global_prim_id = global_prim_id
57
63
 
58
64
  def is_valid(self) -> Any:
59
65
  return self.prim_id >= 0
@@ -72,6 +78,8 @@ class ReflectionChain(_StructRepr):
72
78
  "plane_normals": object,
73
79
  "shape_ids": object,
74
80
  "prim_ids": object,
81
+ "local_prim_ids": object,
82
+ "global_prim_ids": object,
75
83
  }
76
84
 
77
85
  def __init__(
@@ -87,6 +95,8 @@ class ReflectionChain(_StructRepr):
87
95
  plane_normals: Any = None,
88
96
  shape_ids: Any = None,
89
97
  prim_ids: Any = None,
98
+ local_prim_ids: Any = None,
99
+ global_prim_ids: Any = None,
90
100
  max_bounces: int = 0,
91
101
  ray_count: int = 0,
92
102
  ):
@@ -101,6 +111,8 @@ class ReflectionChain(_StructRepr):
101
111
  self.plane_normals = plane_normals
102
112
  self.shape_ids = shape_ids
103
113
  self.prim_ids = prim_ids
114
+ self.local_prim_ids = local_prim_ids
115
+ self.global_prim_ids = global_prim_ids
104
116
  self._max_bounces_hint = int(max_bounces)
105
117
  self._ray_count_hint = int(ray_count)
106
118
 
@@ -311,33 +323,45 @@ class SceneEdgeTopology(_StructRepr):
311
323
  DRJIT_STRUCT = {
312
324
  "v0": object,
313
325
  "v1": object,
326
+ "v0_global": object,
327
+ "v1_global": object,
314
328
  "face0_local": object,
315
329
  "face1_local": object,
316
330
  "face0_global": object,
317
331
  "face1_global": object,
318
332
  "opposite_vertex0": object,
319
333
  "opposite_vertex1": object,
334
+ "opposite_vertex0_global": object,
335
+ "opposite_vertex1_global": object,
320
336
  }
321
337
 
322
338
  def __init__(
323
339
  self,
324
340
  v0: Any = None,
325
341
  v1: Any = None,
342
+ v0_global: Any = None,
343
+ v1_global: Any = None,
326
344
  face0_local: Any = None,
327
345
  face1_local: Any = None,
328
346
  face0_global: Any = None,
329
347
  face1_global: Any = None,
330
348
  opposite_vertex0: Any = None,
331
349
  opposite_vertex1: Any = None,
350
+ opposite_vertex0_global: Any = None,
351
+ opposite_vertex1_global: Any = None,
332
352
  ):
333
353
  self.v0 = v0
334
354
  self.v1 = v1
355
+ self.v0_global = v0_global
356
+ self.v1_global = v1_global
335
357
  self.face0_local = face0_local
336
358
  self.face1_local = face1_local
337
359
  self.face0_global = face0_global
338
360
  self.face1_global = face1_global
339
361
  self.opposite_vertex0 = opposite_vertex0
340
362
  self.opposite_vertex1 = opposite_vertex1
363
+ self.opposite_vertex0_global = opposite_vertex0_global
364
+ self.opposite_vertex1_global = opposite_vertex1_global
341
365
 
342
366
  def size(self) -> int:
343
367
  if self.v0 is None:
@@ -346,6 +370,47 @@ class SceneEdgeTopology(_StructRepr):
346
370
  return int(_shape_tuple(self.v0)[0])
347
371
 
348
372
 
373
+ class SceneGlobalGeometry(_StructRepr):
374
+ DRJIT_STRUCT = {
375
+ "vertices": object,
376
+ "faces": object,
377
+ "face_normal": object,
378
+ "shape_id": object,
379
+ "local_prim_id": object,
380
+ "global_prim_id": object,
381
+ }
382
+
383
+ def __init__(
384
+ self,
385
+ vertices: Any = None,
386
+ faces: Any = None,
387
+ face_normal: Any = None,
388
+ shape_id: Any = None,
389
+ local_prim_id: Any = None,
390
+ global_prim_id: Any = None,
391
+ ):
392
+ self.vertices = vertices
393
+ self.faces = faces
394
+ self.face_normal = face_normal
395
+ self.shape_id = shape_id
396
+ self.local_prim_id = local_prim_id
397
+ self.global_prim_id = global_prim_id
398
+
399
+ def vertex_count(self) -> int:
400
+ if self.vertices is None:
401
+ return 0
402
+ from ._util import _shape_tuple
403
+ shape = _shape_tuple(self.vertices)
404
+ return int(shape[0]) if len(shape) >= 1 else 0
405
+
406
+ def face_count(self) -> int:
407
+ if self.global_prim_id is None:
408
+ return 0
409
+ from ._util import _shape_tuple
410
+ shape = _shape_tuple(self.global_prim_id)
411
+ return int(shape[0]) if len(shape) >= 1 else 0
412
+
413
+
349
414
  class SceneSyncProfile:
350
415
  _FIELDS = (
351
416
  "mesh_update_ms",