diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index aa51a81..29cf4aa 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -11,26 +11,28 @@ jobs: CIBW_ARCHS: auto64 CIBW_TEST_REQUIRES: pytest numpy CIBW_TEST_COMMAND: pytest -v {project}/tests - CIBW_BEFORE_BUILD_LINUX: "yum install -y cmake wget; pip install cython; bash {project}/ci/embree3.bash" - CIBW_REPAIR_WHEEL_COMMAND_LINUX: "LD_LIBRARY_PATH=/root/embree/lib; auditwheel repair -w {dest_dir} {wheel}" + CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28 + CIBW_MANYLINUX_I686_IMAGE: manylinux_2_28 + CIBW_BEFORE_BUILD_LINUX: "yum install -y cmake wget; pip install cython; bash {project}/ci/embree.bash" + CIBW_REPAIR_WHEEL_COMMAND_LINUX: "LD_LIBRARY_PATH=/root/embree4/lib; auditwheel repair -w {dest_dir} {wheel}" CIBW_BEFORE_BUILD_WINDOWS: "pip install delvewheel" - CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "delvewheel repair --add-path embree3\\bin; --no-mangle tbb12.dll;embree3.dll -w {dest_dir} {wheel}" + CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "delvewheel repair --add-path embree4\\bin --no-mangle tbb12.dll;embree4.dll -w {dest_dir} {wheel}" strategy: matrix: os: [ubuntu-latest, windows-latest] steps: - - uses: actions/checkout@v1 - - uses: actions/setup-python@v1 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 name: Install Python with: - python-version: '3.7' + python-version: '3.11' - name: Install cibuildwheel run: | - python -m pip install cibuildwheel==2.3.1 + python -m pip install cibuildwheel==2.12.3 - name: Install Embree On Windows if: matrix.os == 'windows-latest' run: | - ci/embree3.bat + ci/embree.bat - name: Build wheels run: | python -m cibuildwheel --output-dir wheelhouse diff --git a/ci/embree3.bash b/ci/embree.bash similarity index 64% rename from ci/embree3.bash rename to ci/embree.bash index af1baa9..ec4d576 100644 --- a/ci/embree3.bash +++ b/ci/embree.bash @@ -1,7 +1,13 @@ #!/bin/bash set -xe -VERSION="3.13.3" + +# if embree4 exits exit early +if [ -d "/root/embree4" ]; then + exit 0; +fi + +VERSION="4.1.0" rm -rf /tmp/embree.tar.gz rm -rf ~/embree @@ -11,6 +17,6 @@ cd /tmp tar -zxvf embree.tar.gz rm -f embree.tar.gz -mv embree-${VERSION}.x86_64.linux ~/embree +mv embree-${VERSION}.x86_64.linux ~/embree4 diff --git a/ci/embree.bat b/ci/embree.bat new file mode 100644 index 0000000..250d8c7 --- /dev/null +++ b/ci/embree.bat @@ -0,0 +1,6 @@ +mkdir embree4 +cd embree4 + +curl -L -o embree.zip https://github.com/embree/embree/releases/download/v4.1.0/embree-4.1.0.x64.windows.zip + +7z x embree.zip diff --git a/ci/embree3.bat b/ci/embree3.bat deleted file mode 100644 index 400d529..0000000 --- a/ci/embree3.bat +++ /dev/null @@ -1,3 +0,0 @@ -curl -L -o embree.zip https://github.com/embree/embree/releases/download/v3.13.3/embree-3.13.3.x64.vc14.windows.zip -7z x embree.zip -move embree-3.13.3.x64.vc14.windows embree3 diff --git a/embree.pyx b/embree/wrapper.pyx similarity index 65% rename from embree.pyx rename to embree/wrapper.pyx index 39ce71c..f923108 100644 --- a/embree.pyx +++ b/embree/wrapper.pyx @@ -9,7 +9,8 @@ from enum import Enum from libc.stdio cimport printf from libc.stdlib cimport free -__version__ = '0.0.3' +# https://github.com/embree/embree/blob/0fcb306c9176221219dd15e27fe0527ed334948f/doc/src/api.md?plain=1#L290 +__version__ = '4.1.0' # In this section, we define an aligned memory allocation function, # "aligned_alloc". This should be used throughout this .pyx file to @@ -56,7 +57,7 @@ ELSE: DEF RTC_MAX_INSTANCE_LEVEL_COUNT = 1 -cdef extern from "embree3/rtcore.h": +cdef extern from "embree4/rtcore.h": cdef struct RTCBufferTy: pass @@ -204,10 +205,14 @@ cdef extern from "embree3/rtcore.h": RTC_SCENE_FLAG_ROBUST = (1 << 2) RTC_SCENE_FLAG_CONTEXT_FILTER_FUNCTION = (1 << 3) - cdef enum RTCIntersectContextFlags: - RTC_INTERSECT_CONTEXT_FLAG_NONE = 0, - RTC_INTERSECT_CONTEXT_FLAG_INCOHERENT = (0 << 0) - RTC_INTERSECT_CONTEXT_FLAG_COHERENT = (1 << 0) + cdef enum RTCRayQueryFlags: + # matching intel_ray_flags_t layout + RTC_RAY_QUERY_FLAG_NONE = 0, + RTC_RAY_QUERY_FLAG_INVOKE_ARGUMENT_FILTER = (1 << 1) # enable argument filter for each geometry + + # embree specific flags + RTC_RAY_QUERY_FLAG_INCOHERENT = (0 << 16) # optimize for incoherent rays + RTC_RAY_QUERY_FLAG_COHERENT = (1 << 16) # optimize for coherent rays cdef struct RTCRay: float org_x @@ -237,33 +242,6 @@ cdef extern from "embree3/rtcore.h": RTCRay ray RTCHit hit - cdef struct RTCRayNp: - float *org_x - float *org_y - float *org_z - float *tnear - float *dir_x - float *dir_y - float *dir_z - float *time - float *tfar - unsigned int *mask - unsigned int *id - unsigned int *flags - - cdef struct RTCHitNp: - float *Ng_x - float *Ng_y - float *Ng_z - float *u - float *v - unsigned int *primID - unsigned int *geomID - unsigned int *instID[RTC_MAX_INSTANCE_LEVEL_COUNT] - - cdef struct RTCRayHitNp: - RTCRayNp ray - RTCHitNp hit cdef struct RTCRayN: pass @@ -271,20 +249,36 @@ cdef extern from "embree3/rtcore.h": cdef struct RTCHitN: pass + cdef struct RTCRayQueryContext: + unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT] + cdef struct RTCFilterFunctionNArguments: int* valid void* geometryUserPtr - const RTCIntersectContext* context + const RTCRayQueryContext* context RTCRayN* ray RTCHitN* hit unsigned int N ctypedef void(*RTCFilterFunctionN)(const RTCFilterFunctionNArguments*) - cdef struct RTCIntersectContext: - RTCIntersectContextFlags flags - RTCFilterFunctionN filter - unsigned int instID[RTC_MAX_INSTANCE_LEVEL_COUNT] + cdef struct RTCIntersectArguments: + RTCRayQueryFlags flags # intersection flags + #RTCFeatureFlags feature_mask # selectively enable features for traversal + const RTCRayQueryContext* context # optional pointer to ray query context + RTCFilterFunctionN filter # filter function to execute + #RTCIntersectFunctionN intersect # user geometry intersection callback to execute + + cdef struct RTCFilterFunctionNArguments: + int* valid + void* geometryUserPtr + const RTCRayQueryContext* context + RTCRayN* ray + RTCHitN* hit + unsigned int N + + + RTCBuffer rtcNewBuffer(RTCDevice, size_t) RTCBuffer rtcNewSharedBuffer(RTCDevice, void*, size_t) @@ -314,7 +308,7 @@ cdef extern from "embree3/rtcore.h": RTCFormat, size_t, size_t) void* rtcGetGeometryBufferData(RTCGeometry, RTCBufferType, unsigned) - void rtcInitIntersectContext(RTCIntersectContext*) + void rtcInitRayQueryContext(RTCRayQueryContext*) RTCScene rtcNewScene(RTCDevice) void rtcRetainScene(RTCScene) @@ -325,15 +319,9 @@ cdef extern from "embree3/rtcore.h": void rtcSetSceneBuildQuality(RTCScene, RTCBuildQuality) void rtcSetSceneFlags(RTCScene, RTCSceneFlags) - void rtcIntersect1(RTCScene, RTCIntersectContext*, RTCRayHit*) - void rtcIntersect1M(RTCScene, RTCIntersectContext*, RTCRayHit*, - unsigned, size_t) - void rtcOccluded1(RTCScene, RTCIntersectContext*, RTCRay*) - void rtcOccluded1M(RTCScene, RTCIntersectContext*, RTCRay*, unsigned, - size_t) - - void rtcIntersectNp(RTCScene, RTCIntersectContext*, RTCRayHitNp*, unsigned) - + void rtcIntersect1(RTCScene, RTCRayQueryContext*, RTCRayHit*) + + void rtcOccluded1(RTCScene, RTCRayQueryContext*, RTCRay*) INVALID_GEOMETRY_ID = -1 @@ -792,321 +780,14 @@ cdef class RayHit: self.geom_id, self.inst_id, self.normal, self.prim_id, self.uv ) -cdef class Ray1M: - cdef: - RTCRay *_ray - unsigned _M - - def __cinit__(self, unsigned M): - cdef size_t size = M*sizeof(RTCRay) - self._ray = aligned_alloc(size, 0x10) - if self._ray == NULL: - raise Exception('failed to allocate %d bytes' % (size,)) - self._M = M - - def __dealloc__(self): - aligned_free(self._ray) - - @property - def size(self): - return self._M - - def toarray(self): - return np.asarray( self._ray) - - @property - def org(self): - cdef float[:, :] mv = &self._ray[0].org_x - mv.strides[0] = sizeof(RTCRay) - return np.asarray(mv) - - @property - def tnear(self): - cdef float[:] mv = &self._ray[0].tnear - mv.strides[0] = sizeof(RTCRay) - return np.asarray(mv) - - @property - def dir(self): - cdef float[:, :] mv = &self._ray[0].dir_x - mv.strides[0] = sizeof(RTCRay) - return np.asarray(mv) - - @property - def time(self): - cdef float[:] mv = &self._ray[0].time - mv.strides[0] = sizeof(RTCRay) - return np.asarray(mv) - - @property - def tfar(self): - cdef float[:] mv = &self._ray[0].tfar - mv.strides[0] = sizeof(RTCRay) - return np.asarray(mv) - - @property - def mask(self): - cdef unsigned[:] mv = &self._ray[0].mask - mv.strides[0] = sizeof(RTCRay) - return np.asarray(mv) - - @property - def id(self): - cdef unsigned[:] mv = &self._ray[0].id - mv.strides[0] = sizeof(RTCRay) - return np.asarray(mv) - - @property - def flags(self): - cdef unsigned[:] mv = &self._ray[0].flags - mv.strides[0] = sizeof(RTCRay) - return np.asarray(mv) - -cdef class RayHit1M: - cdef: - RTCRayHit *_rayhit - unsigned _M - - def __cinit__(self, unsigned M): - cdef size_t size = M*sizeof(RTCRayHit) - self._rayhit = aligned_alloc(size, 0x10) - if self._rayhit == NULL: - raise Exception('failed to allocate %d bytes' % (size,)) - self._M = M - - def __dealloc__(self): - aligned_free(self._rayhit) - - @property - def size(self): - return self._M - - def toarray(self): - return np.asarray( self._rayhit) - - @property - def org(self): - cdef float[:, :] mv = &self._rayhit[0].ray.org_x - mv.strides[0] = sizeof(RTCRayHit) - return np.asarray(mv) - - @property - def tnear(self): - cdef float[:] mv = &self._rayhit[0].ray.tnear - mv.strides[0] = sizeof(RTCRayHit) - return np.asarray(mv) - - @property - def dir(self): - cdef float[:, :] mv = &self._rayhit[0].ray.dir_x - mv.strides[0] = sizeof(RTCRayHit) - return np.asarray(mv) - - @property - def time(self): - cdef float[:] mv = &self._rayhit[0].ray.time - mv.strides[0] = sizeof(RTCRayHit) - return np.asarray(mv) - - @property - def tfar(self): - cdef float[:] mv = &self._rayhit[0].ray.tfar - mv.strides[0] = sizeof(RTCRayHit) - return np.asarray(mv) - - @property - def mask(self): - cdef unsigned[:] mv = &self._rayhit[0].ray.mask - mv.strides[0] = sizeof(RTCRayHit) - return np.asarray(mv) - - @property - def id(self): - cdef unsigned[:] mv = &self._rayhit[0].ray.id - mv.strides[0] = sizeof(RTCRayHit) - return np.asarray(mv) - - @property - def flags(self): - cdef unsigned[:] mv = &self._rayhit[0].ray.flags - mv.strides[0] = sizeof(RTCRayHit) - return np.asarray(mv) - - @property - def normal(self): - cdef float[:, :] mv = &self._rayhit[0].hit.Ng_x - mv.strides[0] = sizeof(RTCRayHit) - return np.asarray(mv) - - @property - def uv(self): - cdef float[:, :] mv = &self._rayhit[0].hit.u - mv.strides[0] = sizeof(RTCRayHit) - return np.asarray(mv) - - @property - def prim_id(self): - cdef unsigned[:] mv = &self._rayhit[0].hit.primID - mv.strides[0] = sizeof(RTCRayHit) - return np.asarray(mv) - - @property - def geom_id(self): - cdef unsigned[:] mv = &self._rayhit[0].hit.geomID - mv.strides[0] = sizeof(RTCRayHit) - return np.asarray(mv) - - @property - def inst_id(self): - cdef unsigned[:, :] mv = \ - \ - &self._rayhit[0].hit.instID[0] - mv.strides[0] = sizeof(RTCRayHit) - return np.asarray(mv) - -cdef class RayHitNp: - cdef: - RTCRayHitNp _rayhit - unsigned N - - def __cinit__(self, unsigned N): - cdef RTCRayNp *ray = &self._rayhit.ray - ray.org_x = aligned_alloc(N*sizeof(float), 0x10) - ray.org_y = aligned_alloc(N*sizeof(float), 0x10) - ray.org_z = aligned_alloc(N*sizeof(float), 0x10) - ray.tnear = aligned_alloc(N*sizeof(float), 0x10) - ray.dir_x = aligned_alloc(N*sizeof(float), 0x10) - ray.dir_y = aligned_alloc(N*sizeof(float), 0x10) - ray.dir_z = aligned_alloc(N*sizeof(float), 0x10) - ray.time = aligned_alloc(N*sizeof(float), 0x10) - ray.tfar = aligned_alloc(N*sizeof(float), 0x10) - ray.mask = aligned_alloc(N*sizeof(unsigned), 0x10) - ray.id = aligned_alloc(N*sizeof(unsigned), 0x10) - ray.flags = aligned_alloc(N*sizeof(unsigned), 0x10) - - cdef RTCHitNp *hit = &self._rayhit.hit - hit.Ng_x = aligned_alloc(N*sizeof(float), 0x10) - hit.Ng_y = aligned_alloc(N*sizeof(float), 0x10) - hit.Ng_z = aligned_alloc(N*sizeof(float), 0x10) - hit.u = aligned_alloc(N*sizeof(float), 0x10) - hit.v = aligned_alloc(N*sizeof(float), 0x10) - hit.primID = aligned_alloc(N*sizeof(unsigned), 0x10) - hit.geomID = aligned_alloc(N*sizeof(unsigned), 0x10) - for i in range(RTC_MAX_INSTANCE_LEVEL_COUNT): - hit.instID[i] = aligned_alloc(N*sizeof(unsigned), 0x10) - - def __dealloc__(self): - aligned_free(self._rayhit.ray.org_x) - aligned_free(self._rayhit.ray.org_y) - aligned_free(self._rayhit.ray.org_z) - aligned_free(self._rayhit.ray.tnear) - aligned_free(self._rayhit.ray.dir_x) - aligned_free(self._rayhit.ray.dir_y) - aligned_free(self._rayhit.ray.dir_z) - aligned_free(self._rayhit.ray.time) - aligned_free(self._rayhit.ray.tfar) - aligned_free(self._rayhit.ray.mask) - aligned_free(self._rayhit.ray.id) - aligned_free(self._rayhit.ray.flags) - aligned_free(self._rayhit.hit.Ng_x) - aligned_free(self._rayhit.hit.Ng_y) - aligned_free(self._rayhit.hit.Ng_z) - aligned_free(self._rayhit.hit.u) - aligned_free(self._rayhit.hit.v) - aligned_free(self._rayhit.hit.primID) - aligned_free(self._rayhit.hit.geomID) - for i in range(RTC_MAX_INSTANCE_LEVEL_COUNT): - aligned_free(self._rayhit.hit.instID[i]) - - @property - def size(self): - return self._N - - @property - def org_x(self): - return np.asarray(self._rayhit.ray.org_x) - - @property - def org_y(self): - np.asarray(self._rayhit.ray.org_y) - @property - def org_z(self): - np.asarray(self._rayhit.ray.org_z) - - @property - def tnear(self): - np.asarray(self._rayhit.ray.tnear) - - @property - def dir_x(self): - np.asarray(self._rayhit.ray.dir_x) - - @property - def dir_y(self): - np.asarray(self._rayhit.ray.dir_y) - - @property - def dir_z(self): - np.asarray(self._rayhit.ray.dir_z) - - @property - def mask(self): - np.asarray(self._rayhit.ray.mask) - - @property - def flags(self): - np.asarray(self._rayhit.ray.flags) - - @property - def time(self): - np.asarray(self._rayhit.ray.time) - - @property - def tfar(self): - np.asarray(self._rayhit.ray.tfar) - - @property - def Ng_x(self): - np.asarray(self._rayhit.hit.Ng_x) - - @property - def Ng_y(self): - np.asarray(self._rayhit.hit.Ng_y) - - @property - def Ng_z(self): - np.asarray(self._rayhit.hit.Ng_z) - - @property - def u(self): - np.asarray(self._rayhit.hit.u) - - @property - def v(self): - np.asarray(self._rayhit.hit.v) - - @property - def primID(self): - np.asarray(self._rayhit.hit.primID) - - @property - def geomID(self): - np.asarray(self._rayhit.hit.geomID) - - @property - def instID(self): - return tuple( - self._rayhit.hit.instID[i] - for i in range(RTC_MAX_INSTANCE_LEVEL_COUNT) - ) cdef class IntersectContext: cdef: - RTCIntersectContext _context + RTCRayQueryContext _context def __cinit__(self): - rtcInitIntersectContext(&self._context) + rtcInitRayQueryContext(&self._context) @property def flags(self): @@ -1153,17 +834,7 @@ cdef class Scene: def intersect1(self, IntersectContext context, RayHit rayhit): rtcIntersect1(self._scene, &context._context, &rayhit._rayhit) - def intersect1M(self, IntersectContext context, RayHit1M rayhit): - rtcIntersect1M(self._scene, &context._context, rayhit._rayhit, - rayhit._M, sizeof(RTCRayHit)) - - def intersectNp(self, IntersectContext context, RayHitNp rayhit): - rtcIntersectNp(self._scene, &context._context, &rayhit._rayhit, - rayhit._N) - def occluded1(self, IntersectContext context, Ray ray): rtcOccluded1(self._scene, &context._context, &ray._ray) - def occluded1M(self, IntersectContext context, Ray1M ray): - rtcOccluded1M(self._scene, &context._context, ray._ray, ray._M, - sizeof(RTCRay)) + diff --git a/setup.py b/setup.py index 17b5236..371e302 100644 --- a/setup.py +++ b/setup.py @@ -9,23 +9,23 @@ os.path.dirname(__file__))) include = ['/opt/local/include', - os.path.expanduser('~/embree/include')] + os.path.expanduser('~/embree4/include')] library = ['/opt/local/lib', - os.path.expanduser('~/embree/lib')] + os.path.expanduser('~/embree4/lib')] if os.name == 'nt': include = [ - 'c:/Program Files/Intel/Embree3/include', - os.path.join(cwd, 'embree3', 'include')] + 'c:/Program Files/Intel/Embree4/include', + os.path.join(cwd, 'embree4', 'include')] library = [ - 'c:/Program Files/Intel/Embree3/lib', - os.path.join(cwd, 'embree3', 'lib')] + 'c:/Program Files/Intel/Embree4/lib', + os.path.join(cwd, 'embree4', 'lib')] extensions = [ Extension( - 'embree', - ['embree.pyx'], - libraries=['embree3'], + 'embree.wrapper', + sources = ['embree/wrapper.pyx'], + libraries=['embree4'], include_dirs=include, library_dirs=library ) @@ -33,7 +33,7 @@ with open(os.path.join(cwd, 'README.md'), 'r') as f: long_description = f.read() -with open(os.path.join(cwd, 'embree.pyx'), 'r') as f: +with open(os.path.join(cwd, 'embree', 'wrapper.pyx'), 'r') as f: # use eval to get a clean string of version from file __version__ = eval( next(line for line in f if diff --git a/tests/test_embree.py b/tests/test_embree.py deleted file mode 100644 index 6fe0fb7..0000000 --- a/tests/test_embree.py +++ /dev/null @@ -1,109 +0,0 @@ -import os - -import embree -import numpy as np -import unittest - - -cwd = os.path.expanduser( - os.path.abspath(os.path.dirname(__file__))) - - -np.seterr('raise') - - -class TestIntersect1M(unittest.TestCase): - def setUp(self): - - with open(os.path.join(cwd, 'data', 'sphere.npz'), 'rb') as f: - npz = np.load(f) - verts, faces = npz['V'], npz['F'] - - self.verts = verts.astype(np.float32) - self.num_verts = verts.shape[0] - - self.faces = faces.astype(np.uint32) - self.num_faces = faces.shape[0] - - self.centroids = self.verts[self.faces].mean(1) - - self.device = embree.Device() - self.scene = self.device.make_scene() - self.geometry = self.device.make_geometry(embree.GeometryType.Triangle) - - vertex_buffer = self.geometry.set_new_buffer( - embree.BufferType.Vertex, # buf_type - 0, # slot - embree.Format.Float3, # fmt - 3 * np.dtype('float32').itemsize, # byte_stride - self.num_verts) # item_count - vertex_buffer[:] = self.verts[:] - - index_buffer = self.geometry.set_new_buffer( - embree.BufferType.Index, # buf_type - 0, # slot - embree.Format.Uint3, # fmt - 3 * np.dtype('uint32').itemsize, # byte_stride, - self.num_faces) # item count - index_buffer[:] = self.faces[:] - - self.geometry.commit() - self.scene.attach_geometry(self.geometry) - self.geometry.release() - self.scene.commit() - - def test_ray_shot_from_sphere_center(self): - rayhit = embree.RayHit1M(self.num_faces) - rayhit.org[:] = 0 - rayhit.dir[:] = np.divide( - self.centroids, - np.sqrt(np.sum(self.centroids**2, axis=1)).reshape(-1, 1) - ) - rayhit.tnear[:] = 0 - rayhit.tfar[:] = np.inf - rayhit.flags[:] = 0 - rayhit.geom_id[:] = embree.INVALID_GEOMETRY_ID - - context = embree.IntersectContext() - self.scene.intersect1M(context, rayhit) - - self.assertTrue((rayhit.geom_id != embree.INVALID_GEOMETRY_ID).all()) - self.assertTrue((rayhit.prim_id == np.arange(self.num_faces)).all()) - - def test_rays_shot_between_sphere_facets(self): - N = self.num_faces - - rayhit = embree.RayHit1M(N**2) - - for i in range(N): - rayhit.org[N * i:N * (i + 1)] = self.centroids[i] - - for i in range(N): - D = self.centroids - self.centroids[i] - dist = np.sqrt(np.sum(D**2, axis=1)) - dist[dist == 0] = np.inf - D /= dist.reshape(-1, 1) - rayhit.dir[N * i:N * (i + 1)] = D - - rayhit.tnear[:] = 0.01 - rayhit.tfar[:] = np.inf - rayhit.flags[:] = 0 - rayhit.geom_id[:] = embree.INVALID_GEOMETRY_ID - - context = embree.IntersectContext() - self.scene.intersect1M(context, rayhit) - - for i in range(N): - geom_id = rayhit.geom_id[N * i:N * (i + 1)] - self.assertTrue(geom_id[i] == embree.INVALID_GEOMETRY_ID) - J = np.setdiff1d(np.arange(N), [i]) - self.assertTrue((geom_id[J] != embree.INVALID_GEOMETRY_ID).all()) - - for i in range(N): - prim_id = rayhit.prim_id[N * i:N * (i + 1)] - J = np.setdiff1d(np.arange(N), [i]) - self.assertTrue((prim_id[J] == np.arange(N)[J]).all()) - - -if __name__ == '__main__': - unittest.main()