diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index b29b5e3..3baacf3 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -8,16 +8,16 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-15-intel] + os: [ubuntu-latest, windows-latest, macos-15-intel, macos-latest] steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 name: Install Python with: - python-version: '3.13' + python-version: '3.14' - name: Install cibuildwheel run: | - python -m pip install cibuildwheel==3.2.1 + python -m pip install cibuildwheel==3.4.0 - name: Build wheel run: | python -m cibuildwheel --output-dir dist diff --git a/ci/embree.json b/ci/embree.json deleted file mode 100644 index 53d9654..0000000 --- a/ci/embree.json +++ /dev/null @@ -1,37 +0,0 @@ -[ - { - "extract_skip": ["bin/*", "doc/*"], - "name": "embree2", - "platform": "linux", - "sha256": "2c4bdacd8f3c3480991b99e85b8f584975ac181373a75f3e9675bf7efae501fe", - "strip_components": 1, - "target": "../embree2", - "url": "https://github.com/embree/embree/releases/download/v2.17.7/embree-2.17.7.x86_64.linux.tar.gz" - }, - { - "extract_skip": ["bin/*", "doc/*"], - "name": "embree2", - "platform": "mac", - "sha256": "48a2e81d6ccc8782c37f811afe2290969b288ee7264fdf5273eac349921c05df", - "strip_components": 1, - "target": "../embree2", - "url": "https://github.com/embree/embree/releases/download/v2.17.7/embree-2.17.7.x86_64.macosx.tar.gz" - }, - { - "name": "embree2", - "platform": "windows", - "sha256": "6fb7c828eef6cfe7186a5c002b52157a0d67471cf2c3072c3dced7d480071907", - "target": "../embree2", - "strip_components": 1, - "url": "https://github.com/embree/embree/releases/download/v2.17.7/embree-2.17.7.x64.windows.zip" - }, - { - "extract_skip": ["bin/*", "doc/*"], - "name": "embree4", - "platform": "linux", - "sha256": "524842e2f141dca0db584c33a0821176373e7058f3ec2201bfb19d9e9a1b80b9", - "strip_components": 1, - "target": "../embree4", - "url": "https://github.com/embree/embree/releases/download/v4.0.0/embree-4.0.0.x86_64.linux.tar.gz" - } -] diff --git a/embreex/mesh_construction.pyx b/embreex/mesh_construction.pyx index 69e9ec6..c936c0a 100644 --- a/embreex/mesh_construction.pyx +++ b/embreex/mesh_construction.pyx @@ -1,3 +1,4 @@ +# cython: embedsignature=True # distutils: language=c++ cimport numpy as np @@ -5,7 +6,6 @@ cimport rtcore as rtc cimport rtcore_ray as rtcr cimport rtcore_scene as rtcs cimport rtcore_geometry as rtcg -cimport rtcore_geometry_user as rtcgu from rtcore cimport Vertex, Triangle @@ -69,27 +69,31 @@ cdef class TriangleMesh: # In this scheme, we don't share any vertices. This leads to cracks, # but also means we have exactly three times as many vertices as # triangles. - cdef unsigned int mesh = rtcg.rtcNewTriangleMesh(scene.scene_i, - rtcg.RTC_GEOMETRY_STATIC, nt, nt*3, 1) + cdef rtcg.RTCGeometry geom = rtcg.rtcNewGeometry(scene.device.device, + rtcg.RTC_GEOMETRY_TYPE_TRIANGLE) - cdef Vertex* vertices = rtcg.rtcMapBuffer(scene.scene_i, mesh, - rtcg.RTC_VERTEX_BUFFER) + cdef Vertex* vertices = rtcg.rtcSetNewGeometryBuffer(geom, + rtcg.RTC_BUFFER_TYPE_VERTEX, 0, rtcg.RTC_FORMAT_FLOAT3, + 4 * sizeof(float), nt * 3) for i in range(nt): for j in range(3): vertices[i*3 + j].x = tri_vertices[i,j,0] vertices[i*3 + j].y = tri_vertices[i,j,1] vertices[i*3 + j].z = tri_vertices[i,j,2] - rtcg.rtcUnmapBuffer(scene.scene_i, mesh, rtcg.RTC_VERTEX_BUFFER) - cdef Triangle* triangles = rtcg.rtcMapBuffer(scene.scene_i, - mesh, rtcg.RTC_INDEX_BUFFER) + cdef Triangle* triangles = rtcg.rtcSetNewGeometryBuffer(geom, + rtcg.RTC_BUFFER_TYPE_INDEX, 0, rtcg.RTC_FORMAT_UINT3, + 3 * sizeof(unsigned int), nt) for i in range(nt): triangles[i].v0 = i*3 + 0 triangles[i].v1 = i*3 + 1 triangles[i].v2 = i*3 + 2 - rtcg.rtcUnmapBuffer(scene.scene_i, mesh, rtcg.RTC_INDEX_BUFFER) + rtcg.rtcCommitGeometry(geom) + cdef unsigned int mesh = rtcg.rtcAttachGeometry(scene.scene_i, geom) + rtcg.rtcReleaseGeometry(geom) + self.vertices = vertices self.indices = triangles self.mesh = mesh @@ -101,30 +105,34 @@ cdef class TriangleMesh: cdef int nv = tri_vertices.shape[0] cdef int nt = tri_indices.shape[0] - cdef unsigned int mesh = rtcg.rtcNewTriangleMesh(scene.scene_i, - rtcg.RTC_GEOMETRY_STATIC, nt, nv, 1) + cdef rtcg.RTCGeometry geom = rtcg.rtcNewGeometry(scene.device.device, + rtcg.RTC_GEOMETRY_TYPE_TRIANGLE) # set up vertex and triangle arrays. In this case, we just read # them directly from the inputs - cdef Vertex* vertices = rtcg.rtcMapBuffer(scene.scene_i, mesh, - rtcg.RTC_VERTEX_BUFFER) + cdef Vertex* vertices = rtcg.rtcSetNewGeometryBuffer(geom, + rtcg.RTC_BUFFER_TYPE_VERTEX, 0, + rtcg.RTC_FORMAT_FLOAT3, + 4 * sizeof(float), nv) for i in range(nv): vertices[i].x = tri_vertices[i, 0] vertices[i].y = tri_vertices[i, 1] vertices[i].z = tri_vertices[i, 2] - rtcg.rtcUnmapBuffer(scene.scene_i, mesh, rtcg.RTC_VERTEX_BUFFER) - cdef Triangle* triangles = rtcg.rtcMapBuffer(scene.scene_i, - mesh, rtcg.RTC_INDEX_BUFFER) + cdef Triangle* triangles = rtcg.rtcSetNewGeometryBuffer(geom, + rtcg.RTC_BUFFER_TYPE_INDEX, 0, rtcg.RTC_FORMAT_UINT3, + 3 * sizeof(unsigned int), nt) for i in range(nt): triangles[i].v0 = tri_indices[i][0] triangles[i].v1 = tri_indices[i][1] triangles[i].v2 = tri_indices[i][2] - rtcg.rtcUnmapBuffer(scene.scene_i, mesh, rtcg.RTC_INDEX_BUFFER) + rtcg.rtcCommitGeometry(geom) + cdef unsigned int mesh = rtcg.rtcAttachGeometry(scene.scene_i, geom) + rtcg.rtcReleaseGeometry(geom) self.vertices = vertices self.indices = triangles @@ -187,22 +195,23 @@ cdef class ElementMesh(TriangleMesh): # into two triangles. cdef int nt = 6*2*ne - cdef unsigned int mesh = rtcg.rtcNewTriangleMesh(scene.scene_i, - rtcg.RTC_GEOMETRY_STATIC, nt, nv, 1) + cdef rtcg.RTCGeometry geom = rtcg.rtcNewGeometry(scene.device.device, + rtcg.RTC_GEOMETRY_TYPE_TRIANGLE) # first just copy over the vertices - cdef Vertex* vertices = rtcg.rtcMapBuffer(scene.scene_i, mesh, - rtcg.RTC_VERTEX_BUFFER) + cdef Vertex* vertices = rtcg.rtcSetNewGeometryBuffer(geom, + rtcg.RTC_BUFFER_TYPE_VERTEX, 0, rtcg.RTC_FORMAT_FLOAT3, + 4 * sizeof(float), nv) for i in range(nv): vertices[i].x = quad_vertices[i, 0] vertices[i].y = quad_vertices[i, 1] vertices[i].z = quad_vertices[i, 2] - rtcg.rtcUnmapBuffer(scene.scene_i, mesh, rtcg.RTC_VERTEX_BUFFER) # now build up the triangles - cdef Triangle* triangles = rtcg.rtcMapBuffer(scene.scene_i, - mesh, rtcg.RTC_INDEX_BUFFER) + cdef Triangle* triangles = rtcg.rtcSetNewGeometryBuffer(geom, + rtcg.RTC_BUFFER_TYPE_INDEX, 0, rtcg.RTC_FORMAT_UINT3, + 3 * sizeof(unsigned int), nt) for i in range(ne): for j in range(12): @@ -210,7 +219,10 @@ cdef class ElementMesh(TriangleMesh): triangles[12*i+j].v1 = quad_indices[i][triangulate_hex[j][1]] triangles[12*i+j].v2 = quad_indices[i][triangulate_hex[j][2]] - rtcg.rtcUnmapBuffer(scene.scene_i, mesh, rtcg.RTC_INDEX_BUFFER) + rtcg.rtcCommitGeometry(geom) + cdef unsigned int mesh = rtcg.rtcAttachGeometry(scene.scene_i, geom) + rtcg.rtcReleaseGeometry(geom) + self.vertices = vertices self.indices = triangles self.mesh = mesh @@ -226,29 +238,33 @@ cdef class ElementMesh(TriangleMesh): # There are four triangle faces for each tetrahedron. cdef int nt = 4*ne - cdef unsigned int mesh = rtcg.rtcNewTriangleMesh(scene.scene_i, - rtcg.RTC_GEOMETRY_STATIC, nt, nv, 1) + cdef rtcg.RTCGeometry geom = rtcg.rtcNewGeometry(scene.device.device, + rtcg.RTC_GEOMETRY_TYPE_TRIANGLE) # Just copy over the vertices - cdef Vertex* vertices = rtcg.rtcMapBuffer(scene.scene_i, mesh, - rtcg.RTC_VERTEX_BUFFER) + cdef Vertex* vertices = rtcg.rtcSetNewGeometryBuffer(geom, + rtcg.RTC_BUFFER_TYPE_VERTEX, 0, rtcg.RTC_FORMAT_FLOAT3, + 4 * sizeof(float), nv) for i in range(nv): vertices[i].x = tetra_vertices[i, 0] vertices[i].y = tetra_vertices[i, 1] vertices[i].z = tetra_vertices[i, 2] - rtcg.rtcUnmapBuffer(scene.scene_i, mesh, rtcg.RTC_VERTEX_BUFFER) # Now build up the triangles - cdef Triangle* triangles = rtcg.rtcMapBuffer(scene.scene_i, - mesh, rtcg.RTC_INDEX_BUFFER) + cdef Triangle* triangles = rtcg.rtcSetNewGeometryBuffer(geom, + rtcg.RTC_BUFFER_TYPE_INDEX, 0, rtcg.RTC_FORMAT_UINT3, + 3 * sizeof(unsigned int), nt) for i in range(ne): for j in range(4): triangles[4*i+j].v0 = tetra_indices[i][triangulate_tetra[j][0]] triangles[4*i+j].v1 = tetra_indices[i][triangulate_tetra[j][1]] triangles[4*i+j].v2 = tetra_indices[i][triangulate_tetra[j][2]] - rtcg.rtcUnmapBuffer(scene.scene_i, mesh, rtcg.RTC_INDEX_BUFFER) + rtcg.rtcCommitGeometry(geom) + cdef unsigned int mesh = rtcg.rtcAttachGeometry(scene.scene_i, geom) + rtcg.rtcReleaseGeometry(geom) + self.vertices = vertices self.indices = triangles self.mesh = mesh diff --git a/embreex/rtcore.pxd b/embreex/rtcore.pxd index 989e47b..157af11 100644 --- a/embreex/rtcore.pxd +++ b/embreex/rtcore.pxd @@ -4,44 +4,37 @@ cimport cython cimport numpy as np -cdef extern from "embree2/rtcore.h": - cdef int RTCORE_VERSION_MAJOR - cdef int RTCORE_VERSION_MINOR - cdef int RTCORE_VERSION_PATCH +cdef extern from "embree4/rtcore.h": + cdef int RTC_VERSION_MAJOR + cdef int RTC_VERSION_MINOR + cdef int RTC_VERSION_PATCH void rtcInit(const char* cfg) void rtcExit() cdef enum RTCError: - RTC_NO_ERROR - RTC_UNKNOWN_ERROR - RTC_INVALID_ARGUMENT - RTC_INVALID_OPERATION - RTC_OUT_OF_MEMORY - RTC_UNSUPPORTED_CPU - RTC_CANCELLED + RTC_ERROR_NONE + RTC_ERROR_UNKNOWN + RTC_ERROR_INVALID_ARGUMENT + RTC_ERROR_INVALID_OPERATION + RTC_ERROR_OUT_OF_MEMORY + RTC_ERROR_UNSUPPORTED_CPU + RTC_ERROR_CANCELLED # typedef struct __RTCDevice {}* RTCDevice; ctypedef void* RTCDevice RTCDevice rtcNewDevice(const char* cfg) - void rtcDeleteDevice(RTCDevice device) + void rtcReleaseDevice(RTCDevice device) - RTCError rtcGetError() - ctypedef void (*RTCErrorFunc)(const RTCError code, const char* _str) - void rtcSetErrorFunction(RTCErrorFunc func) - - # Embree 2.14.0-0 - void rtcDeviceSetErrorFunction(RTCDevice device, RTCErrorFunc func) - - # Embree 2.15.1 - ctypedef void (*RTCErrorFunc2)(void* userPtr, const RTCError code, const char* str) - void rtcDeviceSetErrorFunction2(RTCDevice device, RTCErrorFunc2 func, void* userPtr) + RTCError rtcGetDeviceError(RTCDevice device) + ctypedef void (*RTCErrorFunc)(void* userPtr, RTCError code, const char* str) + void rtcSetDeviceErrorFunction(RTCDevice device, RTCErrorFunc func, void* userPtr) ctypedef bint RTCMemoryMonitorFunc(const ssize_t _bytes, const bint post) void rtcSetMemoryMonitorFunction(RTCMemoryMonitorFunc func) -cdef extern from "embree2/rtcore_ray.h": +cdef extern from "embree4/rtcore_ray.h": pass cdef struct Vertex: diff --git a/embreex/rtcore.pyx b/embreex/rtcore.pyx index c186e77..691664c 100644 --- a/embreex/rtcore.pyx +++ b/embreex/rtcore.pyx @@ -6,19 +6,19 @@ import logging log = logging.getLogger('embreex') cdef void print_error(RTCError code): - if code == RTC_NO_ERROR: + if code == RTC_ERROR_NONE: log.error("ERROR: No error") - elif code == RTC_UNKNOWN_ERROR: + elif code == RTC_ERROR_UNKNOWN: log.error("ERROR: Unknown error") - elif code == RTC_INVALID_ARGUMENT: + elif code == RTC_ERROR_INVALID_ARGUMENT: log.error("ERROR: Invalid argument") - elif code == RTC_INVALID_OPERATION: + elif code == RTC_ERROR_INVALID_OPERATION: log.error("ERROR: Invalid operation") - elif code == RTC_OUT_OF_MEMORY: + elif code == RTC_ERROR_OUT_OF_MEMORY: log.error("ERROR: Out of memory") - elif code == RTC_UNSUPPORTED_CPU: + elif code == RTC_ERROR_UNSUPPORTED_CPU: log.error("ERROR: Unsupported CPU") - elif code == RTC_CANCELLED: + elif code == RTC_ERROR_CANCELLED: log.error("ERROR: Cancelled") else: raise RuntimeError @@ -29,9 +29,9 @@ cdef class EmbreeDevice: self.device = rtcNewDevice(NULL) def __dealloc__(self): - rtcDeleteDevice(self.device) + rtcReleaseDevice(self.device) def __repr__(self): - return 'Embree version: {0}.{1}.{2}'.format(RTCORE_VERSION_MAJOR, - RTCORE_VERSION_MINOR, - RTCORE_VERSION_PATCH) + return 'Embree version: {0}.{1}.{2}'.format(RTC_VERSION_MAJOR, + RTC_VERSION_MINOR, + RTC_VERSION_PATCH) diff --git a/embreex/rtcore_geometry.pxd b/embreex/rtcore_geometry.pxd index 3c002a1..1a3e797 100644 --- a/embreex/rtcore_geometry.pxd +++ b/embreex/rtcore_geometry.pxd @@ -2,38 +2,69 @@ from .rtcore_ray cimport RTCRay, RTCRay4, RTCRay8, RTCRay16 from .rtcore_scene cimport RTCScene +from . cimport rtcore as rtc cimport cython cimport numpy as np -cdef extern from "embree2/rtcore_geometry.h": +cdef extern from "embree4/rtcore_geometry.h": cdef unsigned int RTC_INVALID_GEOMETRY_ID cdef enum RTCBufferType: - RTC_INDEX_BUFFER - RTC_VERTEX_BUFFER - RTC_VERTEX_BUFFER0 - RTC_VERTEX_BUFFER1 + RTC_BUFFER_TYPE_INDEX + RTC_BUFFER_TYPE_VERTEX + RTC_BUFFER_TYPE_VERTEX_ATTRIBUTE + RTC_BUFFER_TYPE_NORMAL + RTC_BUFFER_TYPE_TANGENT + RTC_BUFFER_TYPE_NORMAL_DERIVATIVE + RTC_BUFFER_TYPE_GRID + RTC_BUFFER_TYPE_FACE + RTC_BUFFER_TYPE_LEVEL + RTC_BUFFER_TYPE_EDGE_CREASE_INDEX + RTC_BUFFER_TYPE_EDGE_CREASE_WEIGHT + RTC_BUFFER_TYPE_VERTEX_CREASE_INDEX + RTC_BUFFER_TYPE_VERTEX_CREASE_WEIGHT + RTC_BUFFER_TYPE_HOLE + RTC_BUFFER_TYPE_TRANSFORM + RTC_BUFFER_TYPE_FLAGS - RTC_FACE_BUFFER - RTC_LEVEL_BUFFER + cdef enum RTCFormat: + RTC_FORMAT_UNDEFINED + RTC_FORMAT_UCHAR + RTC_FORMAT_UCHAR2 + RTC_FORMAT_UCHAR3 + RTC_FORMAT_UCHAR4 + RTC_FORMAT_CHAR + RTC_FORMAT_CHAR2 + RTC_FORMAT_CHAR3 + RTC_FORMAT_CHAR4 + RTC_FORMAT_USHORT + RTC_FORMAT_USHORT2 + RTC_FORMAT_USHORT3 + RTC_FORMAT_USHORT4 + RTC_FORMAT_SHORT + RTC_FORMAT_SHORT2 + RTC_FORMAT_SHORT3 + RTC_FORMAT_SHORT4 + RTC_FORMAT_UINT + RTC_FORMAT_UINT2 + RTC_FORMAT_UINT3 + RTC_FORMAT_UINT4 + RTC_FORMAT_INT + RTC_FORMAT_INT2 + RTC_FORMAT_INT3 + RTC_FORMAT_INT4 + RTC_FORMAT_FLOAT + RTC_FORMAT_FLOAT2 + RTC_FORMAT_FLOAT3 + RTC_FORMAT_FLOAT4 - RTC_EDGE_CREASE_INDEX_BUFFER - RTC_EDGE_CREASE_WEIGHT_BUFFER - - RTC_VERTEX_CREASE_INDEX_BUFFER - RTC_VERTEX_CREASE_WEIGHT_BUFFER - - RTC_HOLE_BUFFER - - cdef enum RTCMatrixType: - RTC_MATRIX_ROW_MAJOR - RTC_MATRIX_COLUMN_MAJOR - RTC_MATRIX_COLUMN_MAJOR_ALIGNED16 - - cdef enum RTCGeometryFlags: - RTC_GEOMETRY_STATIC - RTC_GEOMETRY_DEFORMABLE - RTC_GEOMETRY_DYNAMIC + cdef enum RTCGeometryType: + RTC_GEOMETRY_TYPE_TRIANGLE + RTC_GEOMETRY_TYPE_QUAD + RTC_GEOMETRY_TYPE_GRID + RTC_GEOMETRY_TYPE_SUBDIVISION + RTC_GEOMETRY_TYPE_USER + RTC_GEOMETRY_TYPE_INSTANCE cdef struct RTCBounds: float lower_x, lower_y, lower_z, align0 @@ -49,40 +80,23 @@ cdef extern from "embree2/rtcore_geometry.h": const float* nx, const float* ny, const float* nz, float* px, float* py, float* pz, size_t N) - unsigned rtcNewInstance(RTCScene target, RTCScene source) - void rtcSetTransform(RTCScene scene, unsigned geomID, - RTCMatrixType layout, const float *xfm) - unsigned rtcNewTriangleMesh(RTCScene scene, RTCGeometryFlags flags, - size_t numTriangles, size_t numVertices, - size_t numTimeSteps) + ctypedef void* RTCGeometry - unsigned rtcNewSubdivisionMesh (RTCScene scene, RTCGeometryFlags flags, - size_t numFaces, size_t numEdges, - size_t numVertices, size_t numEdgeCreases, - size_t numVertexCreases, size_t numHoles, - size_t numTimeSteps) - unsigned rtcNewHairGeometry (RTCScene scene, RTCGeometryFlags flags, - size_t numCurves, size_t numVertices, - size_t numTimeSteps) - void rtcSetMask(RTCScene scene, unsigned geomID, int mask) - void *rtcMapBuffer(RTCScene scene, unsigned geomID, RTCBufferType type) - void rtcUnmapBuffer(RTCScene scene, unsigned geomID, RTCBufferType type) - void rtcSetBuffer(RTCScene scene, unsigned geomID, RTCBufferType type, - void *ptr, size_t offset, size_t stride) - void rtcEnable(RTCScene scene, unsigned geomID) - void rtcUpdate(RTCScene scene, unsigned geomID) - void rtcUpdateBuffer(RTCScene scene, unsigned geomID, RTCBufferType type) - void rtcDisable(RTCScene scene, unsigned geomID) - void rtcSetDisplacementFunction (RTCScene scene, unsigned geomID, RTCDisplacementFunc func, RTCBounds* bounds) - void rtcSetIntersectionFilterFunction (RTCScene scene, unsigned geomID, RTCFilterFunc func) - void rtcSetIntersectionFilterFunction4 (RTCScene scene, unsigned geomID, RTCFilterFunc4 func) - void rtcSetIntersectionFilterFunction8 (RTCScene scene, unsigned geomID, RTCFilterFunc8 func) - void rtcSetIntersectionFilterFunction16 (RTCScene scene, unsigned geomID, RTCFilterFunc16 func) - void rtcSetOcclusionFilterFunction (RTCScene scene, unsigned geomID, RTCFilterFunc func) - void rtcSetOcclusionFilterFunction4 (RTCScene scene, unsigned geomID, RTCFilterFunc4 func) - void rtcSetOcclusionFilterFunction8 (RTCScene scene, unsigned geomID, RTCFilterFunc8 func) - void rtcSetOcclusionFilterFunction16 (RTCScene scene, unsigned geomID, RTCFilterFunc16 func) - void rtcSetUserData (RTCScene scene, unsigned geomID, void* ptr) - void* rtcGetUserData (RTCScene scene, unsigned geomID) - void rtcDeleteGeometry (RTCScene scene, unsigned geomID) + RTCGeometry rtcNewGeometry(rtc.RTCDevice device, RTCGeometryType type) + void rtcCommitGeometry(RTCGeometry geometry) + void rtcReleaseGeometry(RTCGeometry geometry) + void* rtcSetNewGeometryBuffer(RTCGeometry geometry, + RTCBufferType type, + unsigned int slot, + RTCFormat format, + size_t byteStride, + size_t itemCount) + void rtcSetGeometryUserData(RTCGeometry geometry, void* ptr) + void* rtcGetGeometryUserData(RTCGeometry geometry) + void rtcSetGeometryMask(RTCGeometry geometry, unsigned int mask) + unsigned int rtcAttachGeometry(RTCScene scene, RTCGeometry geometry) + void rtcAttachGeometryByID(RTCScene scene, RTCGeometry geometry, unsigned int geomID) + void rtcDetachGeometry(RTCScene scene, unsigned int geomID) + RTCGeometry rtcGetGeometry(RTCScene scene, unsigned int geomID) + void rtcSetGeometryInstancedScene(RTCGeometry geometry, RTCScene scene) diff --git a/embreex/rtcore_geometry_user.pxd b/embreex/rtcore_geometry_user.pxd deleted file mode 100644 index f1ad5fb..0000000 --- a/embreex/rtcore_geometry_user.pxd +++ /dev/null @@ -1,35 +0,0 @@ -# rtcore_geometry_user wrapper - -#from libc.stdint cimport ssize_t, size_t -from .rtcore_ray cimport RTCRay, RTCRay4, RTCRay8, RTCRay16 -from .rtcore_geometry cimport RTCBounds -from .rtcore_scene cimport RTCScene -cimport cython -cimport numpy as np - -cdef extern from "embree2/rtcore_geometry_user.h": - ctypedef void (*RTCBoundsFunc)(void* ptr, size_t item, RTCBounds& bounds_o) - ctypedef void (*RTCIntersectFunc)(void* ptr, RTCRay& ray, size_t item) - ctypedef void (*RTCIntersectFunc4)(const void* valid, void* ptr, - RTCRay4& ray, size_t item) - ctypedef void (*RTCIntersectFunc8)(const void* valid, void* ptr, - RTCRay8& ray, size_t item) - ctypedef void (*RTCIntersectFunc16)(const void* valid, void* ptr, - RTCRay16& ray, size_t item) - ctypedef void (*RTCOccludedFunc)(void* ptr, RTCRay& ray, size_t item) - ctypedef void (*RTCOccludedFunc4)(const void* valid, void* ptr, - RTCRay4& ray, size_t item) - ctypedef void (*RTCOccludedFunc8)(const void* valid, void* ptr, - RTCRay8& ray, size_t item) - ctypedef void (*RTCOccludedFunc16)(const void* valid, void* ptr, - RTCRay16& ray, size_t item) - unsigned rtcNewUserGeometry(RTCScene scene, size_t numGeometries) - void rtcSetBoundsFunction(RTCScene scene, unsigned geomID, RTCBoundsFunc bounds) - void rtcSetIntersectFunction(RTCScene scene, unsigned geomID, RTCIntersectFunc intersect) - void rtcSetIntersectFunction4(RTCScene scene, unsigned geomID, RTCIntersectFunc4 intersect4) - void rtcSetIntersectFunction8(RTCScene scene, unsigned geomID, RTCIntersectFunc8 intersect8) - void rtcSetIntersectFunction16(RTCScene scene, unsigned geomID, RTCIntersectFunc16 intersect16) - void rtcSetOccludedFunction(RTCScene scene, unsigned geomID, RTCOccludedFunc occluded) - void rtcSetOccludedFunction4(RTCScene scene, unsigned geomID, RTCOccludedFunc4 occluded4) - void rtcSetOccludedFunction8(RTCScene scene, unsigned geomID, RTCOccludedFunc8 occluded8) - void rtcSetOccludedFunction16(RTCScene scene, unsigned geomID, RTCOccludedFunc16 occluded16) diff --git a/embreex/rtcore_ray.pxd b/embreex/rtcore_ray.pxd index 3f248b0..a8a637d 100644 --- a/embreex/rtcore_ray.pxd +++ b/embreex/rtcore_ray.pxd @@ -3,33 +3,43 @@ cimport cython cimport numpy as np -cdef extern from "embree2/rtcore_ray.h": +cdef extern from "embree4/rtcore_ray.h": # RTCORE_ALIGN(16) # This is for a *single* ray cdef struct RTCRay: # Ray data - float org[3] - float align0 - - float dir[3] - float align1 - + float org_x + float org_y + float org_z float tnear - float tfar + float dir_x + float dir_y + float dir_z float time - int mask - # Hit data - float Ng[3] - float align2 + float tfar + unsigned int mask + unsigned int id + unsigned int flags + + # Hit data structure + cdef struct RTCHit: + float Ng_x + float Ng_y + float Ng_z float u float v - int geomID - int primID - int instID + unsigned int primID + unsigned int geomID + unsigned int instID[1] # RTC_MAX_INSTANCE_LEVEL_COUNT + + # Combined ray/hit structure + cdef struct RTCRayHit: + RTCRay ray + RTCHit hit # This is for a packet of 4 rays cdef struct RTCRay4: diff --git a/embreex/rtcore_scene.pxd b/embreex/rtcore_scene.pxd index 006357a..09ea6ac 100644 --- a/embreex/rtcore_scene.pxd +++ b/embreex/rtcore_scene.pxd @@ -5,60 +5,56 @@ cimport numpy as np cimport rtcore as rtc cimport rtcore_ray as rtcr -cdef extern from "embree2/rtcore_scene.h": +cdef extern from "embree4/rtcore_scene.h": ctypedef struct RTCRay ctypedef struct RTCRay4 ctypedef struct RTCRay8 ctypedef struct RTCRay16 + ctypedef struct RTCRayHit + ctypedef struct RTCRayHit4 + ctypedef struct RTCRayHit8 + ctypedef struct RTCRayHit16 cdef enum RTCSceneFlags: - RTC_SCENE_STATIC - RTC_SCENE_DYNAMIC - RTC_SCENE_COMPACT - RTC_SCENE_COHERENT - RTC_SCENE_INCOHERENT - RTC_SCENE_HIGH_QUALITY - RTC_SCENE_ROBUST - - cdef enum RTCAlgorithmFlags: - RTC_INTERSECT1 - RTC_INTERSECT4 - RTC_INTERSECT8 - RTC_INTERSECT16 + RTC_SCENE_FLAG_NONE + RTC_SCENE_FLAG_DYNAMIC + RTC_SCENE_FLAG_COMPACT + RTC_SCENE_FLAG_ROBUST + RTC_SCENE_FLAG_FILTER_FUNCTION_IN_ARGUMENTS # ctypedef void* RTCDevice ctypedef void* RTCScene - RTCScene rtcNewScene(RTCSceneFlags flags, RTCAlgorithmFlags aflags) - - RTCScene rtcDeviceNewScene(rtc.RTCDevice device, RTCSceneFlags flags, RTCAlgorithmFlags aflags) + RTCScene rtcNewScene(rtc.RTCDevice device) ctypedef bint (*RTCProgressMonitorFunc)(void* ptr, const double n) - void rtcSetProgressMonitorFunction(RTCScene scene, RTCProgressMonitorFunc func, void* ptr) + void rtcSetSceneProgressMonitorFunction(RTCScene scene, RTCProgressMonitorFunc func, void* ptr) - void rtcCommit(RTCScene scene) + void rtcCommitScene(RTCScene scene) - void rtcCommitThread(RTCScene scene, unsigned int threadID, unsigned int numThreads) + void rtcJoinCommitScene(RTCScene scene) - void rtcIntersect(RTCScene scene, RTCRay& ray) + void rtcIntersect1(RTCScene scene, RTCRayHit* rayhit, void* args) - void rtcIntersect4(const void* valid, RTCScene scene, RTCRay4& ray) + void rtcIntersect4(const void* valid, RTCScene scene, RTCRayHit4* rayhit, void* args) - void rtcIntersect8(const void* valid, RTCScene scene, RTCRay8& ray) + void rtcIntersect8(const void* valid, RTCScene scene, RTCRayHit8* rayhit, void* args) - void rtcIntersect16(const void* valid, RTCScene scene, RTCRay16& ray) + void rtcIntersect16(const void* valid, RTCScene scene, RTCRayHit16* rayhit, void* args) - void rtcOccluded(RTCScene scene, RTCRay& ray) + void rtcOccluded1(RTCScene scene, RTCRay* ray, void* args) - void rtcOccluded4(const void* valid, RTCScene scene, RTCRay4& ray) + void rtcOccluded4(const void* valid, RTCScene scene, RTCRay4* ray, void* args) - void rtcOccluded8(const void* valid, RTCScene scene, RTCRay8& ray) + void rtcOccluded8(const void* valid, RTCScene scene, RTCRay8* ray, void* args) - void rtcOccluded16(const void* valid, RTCScene scene, RTCRay16& ray) + void rtcOccluded16(const void* valid, RTCScene scene, RTCRay16* ray, void* args) - void rtcDeleteScene(RTCScene scene) + void rtcReleaseScene(RTCScene scene) + + void rtcSetSceneFlags(RTCScene scene, RTCSceneFlags flags) cdef class EmbreeScene: cdef RTCScene scene_i diff --git a/embreex/rtcore_scene.pyx b/embreex/rtcore_scene.pyx index 8929f07..85350d7 100644 --- a/embreex/rtcore_scene.pyx +++ b/embreex/rtcore_scene.pyx @@ -12,13 +12,9 @@ cimport rtcore_geometry as rtcg log = logging.getLogger('embreex') -cdef void error_printer(const rtc.RTCError code, const char *_str) noexcept: +cdef void error_printer(void* userPtr, const rtc.RTCError code, const char *_str) noexcept: """ - error_printer function depends on embree version - Embree 2.14.1 - -> cdef void error_printer(const rtc.RTCError code, const char *_str): - Embree 2.17.1 - -> cdef void error_printer(void* userPtr, const rtc.RTCError code, const char *_str): + error_printer function for Embree 4.x """ log.error("ERROR CAUGHT IN EMBREE") rtc.print_error(code) @@ -26,17 +22,18 @@ cdef void error_printer(const rtc.RTCError code, const char *_str) noexcept: cdef class EmbreeScene: - def __init__(self, rtc.EmbreeDevice device=None, robust=False): + def __init__(self, rtc.EmbreeDevice device=None, robust=True): if device is None: device = rtc.EmbreeDevice() # We store the embree device inside EmbreeScene to avoid premature deletion self.device = device - flags = RTC_SCENE_STATIC + rtc.rtcSetDeviceErrorFunction(device.device, error_printer, NULL) + self.scene_i = rtcNewScene(device.device) + flags = RTC_SCENE_FLAG_NONE if robust: # bitwise-or the robust flag - flags |= RTC_SCENE_ROBUST - rtc.rtcDeviceSetErrorFunction(device.device, error_printer) - self.scene_i = rtcDeviceNewScene(device.device, flags, RTC_INTERSECT1) + flags |= RTC_SCENE_FLAG_ROBUST + rtcSetSceneFlags(self.scene_i, flags) self.is_committed = 0 def run(self, np.ndarray[np.float32_t, ndim=2] vec_origins, @@ -44,11 +41,11 @@ cdef class EmbreeScene: dists=None,query='INTERSECT',output=None): if self.is_committed == 0: - rtcCommit(self.scene_i) + rtcCommitScene(self.scene_i) self.is_committed = 1 cdef int nv = vec_origins.shape[0] - cdef int vo_i, vd_i, vd_step + cdef int vd_i, vd_step cdef np.ndarray[np.int32_t, ndim=1] intersect_ids cdef np.ndarray[np.float32_t, ndim=1] tfars cdef rayQueryType query_type @@ -82,7 +79,8 @@ cdef class EmbreeScene: else: intersect_ids = np.empty(nv, dtype="int32") - cdef rtcr.RTCRay ray + cdef rtcr.RTCRayHit rayhit + cdef unsigned int INVALID_GEOMETRY_ID = 0xFFFFFFFF vd_i = 0 vd_step = 1 # If vec_directions is 1 long, we won't be updating it. @@ -90,35 +88,44 @@ cdef class EmbreeScene: for i in range(nv): for j in range(3): - ray.org[j] = vec_origins[i, j] - ray.dir[j] = vec_directions[vd_i, j] - ray.tnear = 0.0 - ray.tfar = tfars[i] - ray.geomID = rtcg.RTC_INVALID_GEOMETRY_ID - ray.primID = rtcg.RTC_INVALID_GEOMETRY_ID - ray.instID = rtcg.RTC_INVALID_GEOMETRY_ID - ray.mask = -1 - ray.time = 0 + rayhit.ray.org_x = vec_origins[i, 0] if j == 0 else rayhit.ray.org_x + rayhit.ray.org_y = vec_origins[i, 1] if j == 1 else rayhit.ray.org_y + rayhit.ray.org_z = vec_origins[i, 2] if j == 2 else rayhit.ray.org_z + rayhit.ray.dir_x = vec_directions[vd_i, 0] if j == 0 else rayhit.ray.dir_x + rayhit.ray.dir_y = vec_directions[vd_i, 1] if j == 1 else rayhit.ray.dir_y + rayhit.ray.dir_z = vec_directions[vd_i, 2] if j == 2 else rayhit.ray.dir_z + rayhit.ray.tnear = 0.0 + rayhit.ray.tfar = tfars[i] + rayhit.hit.geomID = INVALID_GEOMETRY_ID + rayhit.hit.primID = INVALID_GEOMETRY_ID + rayhit.hit.instID[0] = INVALID_GEOMETRY_ID + rayhit.ray.mask = 0xFFFFFFFF + rayhit.ray.time = 0.0 + rayhit.ray.flags = 0 vd_i += vd_step if query_type == intersect or query_type == distance: - rtcIntersect(self.scene_i, ray) + rtcIntersect1(self.scene_i, &rayhit, NULL) if not output: if query_type == intersect: - intersect_ids[i] = ray.primID + # Convert unsigned INVALID_GEOMETRY_ID to signed -1 for compatibility + intersect_ids[i] = -1 if rayhit.hit.primID == INVALID_GEOMETRY_ID else rayhit.hit.primID else: - tfars[i] = ray.tfar + tfars[i] = rayhit.ray.tfar else: - primID[i] = ray.primID - geomID[i] = ray.geomID - u[i] = ray.u - v[i] = ray.v - tfars[i] = ray.tfar - for j in range(3): - Ng[i, j] = ray.Ng[j] + # Convert unsigned INVALID_GEOMETRY_ID to signed -1 for compatibility + primID[i] = -1 if rayhit.hit.primID == INVALID_GEOMETRY_ID else rayhit.hit.primID + geomID[i] = -1 if rayhit.hit.geomID == INVALID_GEOMETRY_ID else rayhit.hit.geomID + u[i] = rayhit.hit.u + v[i] = rayhit.hit.v + tfars[i] = rayhit.ray.tfar + Ng[i, 0] = rayhit.hit.Ng_x + Ng[i, 1] = rayhit.hit.Ng_y + Ng[i, 2] = rayhit.hit.Ng_z else: - rtcOccluded(self.scene_i, ray) - intersect_ids[i] = ray.geomID + rtcOccluded1(self.scene_i, &rayhit.ray, NULL) + # In Embree 4, occlusion is signaled by setting ray.tfar to -inf + intersect_ids[i] = 0 if rayhit.ray.tfar < 0 else -1 if output: return {'u':u, 'v':v, 'Ng': Ng, 'tfar': tfars, 'primID': primID, 'geomID': geomID} @@ -129,4 +136,4 @@ cdef class EmbreeScene: return intersect_ids def __dealloc__(self): - rtcDeleteScene(self.scene_i) + rtcReleaseScene(self.scene_i) diff --git a/embreex/triangles.pyx b/embreex/triangles.pyx index d47bda5..f991106 100644 --- a/embreex/triangles.pyx +++ b/embreex/triangles.pyx @@ -5,7 +5,6 @@ cimport rtcore as rtc cimport rtcore_ray as rtcr cimport rtcore_scene as rtcs cimport rtcore_geometry as rtcg -cimport rtcore_geometry_user as rtcgu from rtcore cimport Vertex, Triangle, Vec3f from libc.stdlib cimport malloc, free @@ -16,10 +15,12 @@ ctypedef Vec3f (*renderPixelFunc)(float x, float y, def run_triangles(): pass -cdef unsigned int addCube(rtcs.RTCScene scene_i): - cdef unsigned int mesh = rtcg.rtcNewTriangleMesh(scene_i, - rtcg.RTC_GEOMETRY_STATIC, 12, 8, 1) - cdef Vertex* vertices = rtcg.rtcMapBuffer(scene_i, mesh, rtcg.RTC_VERTEX_BUFFER) +cdef unsigned int addCube(rtcs.RTCScene scene_i, rtc.RTCDevice device): + cdef rtcg.RTCGeometry geom = rtcg.rtcNewGeometry(device, + rtcg.RTC_GEOMETRY_TYPE_TRIANGLE) + cdef Vertex* vertices = rtcg.rtcSetNewGeometryBuffer(geom, + rtcg.RTC_BUFFER_TYPE_VERTEX, 0, rtcg.RTC_FORMAT_FLOAT3, + 4 * sizeof(float), 8) vertices[0].x = -1 vertices[0].y = -1 vertices[0].z = -1 @@ -52,13 +53,13 @@ cdef unsigned int addCube(rtcs.RTCScene scene_i): vertices[7].y = +1 vertices[7].z = +1 - rtcg.rtcUnmapBuffer(scene_i, mesh, rtcg.RTC_VERTEX_BUFFER) cdef Vec3f *colors = malloc(12*sizeof(Vec3f)) cdef int tri = 0 - cdef Triangle* triangles = rtcg.rtcMapBuffer(scene_i, mesh, - rtcg.RTC_INDEX_BUFFER) + cdef Triangle* triangles = rtcg.rtcSetNewGeometryBuffer(geom, + rtcg.RTC_BUFFER_TYPE_INDEX, 0, rtcg.RTC_FORMAT_UINT3, + 3 * sizeof(unsigned int), 12) # left side colors[tri].x = 1.0 @@ -156,15 +157,19 @@ cdef unsigned int addCube(rtcs.RTCScene scene_i): triangles[tri].v2 = 5 tri += 1 - rtcg.rtcUnmapBuffer(scene_i, mesh, rtcg.RTC_INDEX_BUFFER) + rtcg.rtcCommitGeometry(geom) + cdef unsigned int mesh = rtcg.rtcAttachGeometry(scene_i, geom) + rtcg.rtcReleaseGeometry(geom) return mesh -cdef unsigned int addGroundPlane (rtcs.RTCScene scene_i): - cdef unsigned int mesh = rtcg.rtcNewTriangleMesh (scene_i, - rtcg.RTC_GEOMETRY_STATIC, 2, 4, 1) +cdef unsigned int addGroundPlane (rtcs.RTCScene scene_i, rtc.RTCDevice device): + cdef rtcg.RTCGeometry geom = rtcg.rtcNewGeometry(device, + rtcg.RTC_GEOMETRY_TYPE_TRIANGLE) - cdef Vertex* vertices = rtcg.rtcMapBuffer(scene_i, mesh, rtcg.RTC_VERTEX_BUFFER) + cdef Vertex* vertices = rtcg.rtcSetNewGeometryBuffer(geom, + rtcg.RTC_BUFFER_TYPE_VERTEX, 0, rtcg.RTC_FORMAT_FLOAT3, + 4 * sizeof(float), 4) vertices[0].x = -10 vertices[0].y = -2 vertices[0].z = -10 @@ -180,15 +185,19 @@ cdef unsigned int addGroundPlane (rtcs.RTCScene scene_i): vertices[3].x = +10 vertices[3].y = -2 vertices[3].z = +10 - rtcg.rtcUnmapBuffer(scene_i, mesh, rtcg.RTC_VERTEX_BUFFER) - cdef Triangle* triangles = rtcg.rtcMapBuffer(scene_i, mesh, rtcg.RTC_INDEX_BUFFER) + cdef Triangle* triangles = rtcg.rtcSetNewGeometryBuffer(geom, + rtcg.RTC_BUFFER_TYPE_INDEX, 0, rtcg.RTC_FORMAT_UINT3, + 3 * sizeof(unsigned int), 2) triangles[0].v0 = 0 triangles[0].v1 = 2 triangles[0].v2 = 1 triangles[1].v0 = 1 triangles[1].v1 = 2 triangles[1].v2 = 3 - rtcg.rtcUnmapBuffer(scene_i, mesh, rtcg.RTC_INDEX_BUFFER) + + rtcg.rtcCommitGeometry(geom) + cdef unsigned int mesh = rtcg.rtcAttachGeometry(scene_i, geom) + rtcg.rtcReleaseGeometry(geom) return mesh diff --git a/package/embree.json b/package/embree.json new file mode 100644 index 0000000..1637bc9 --- /dev/null +++ b/package/embree.json @@ -0,0 +1,48 @@ +[ + { + "extract_skip": ["bin/*", "doc/*"], + "name": "embree4", + "platform": "linux", + "architecture": "x86_64", + "sha256": "cb3d4402537fc9165c76c3316b8953dcfea523cd1eaf588e2de7639864ee3c57", + "target": "../embree4", + "url": "https://github.com/RenderKit/embree/releases/download/v4.4.0/embree-4.4.0.x86_64.linux.tar.gz" + }, + { + "extract_skip": ["bin/*", "doc/*"], + "name": "embree4", + "platform": "macos", + "architecture": "x86_64", + "sha256": "435b4ef3421eb75cf41a41438d44b760f4d86c24124e0b9a0ed9d85adff54b4d", + "target": "../embree4", + "url": "https://github.com/RenderKit/embree/releases/download/v4.4.0/embree-4.4.0.x86_64.macosx.zip", + "symlink": { + "lib/libembree4.dylib": "lib/libembree4.4.dylib", + "lib/libtbb.dylib": "lib/libtbb.12.11.dylib", + "lib/libtbb.12.dylib": "lib/libtbb.12.11.dylib" + } + }, + { + "extract_skip": ["bin/*", "doc/*"], + "name": "embree4", + "platform": "macos", + "architecture": "arm64", + "sha256": "288a5bb1adc3d4cd0f86646008f4ac442b958b1635bf60cecff8f1b232e999a1", + "target": "../embree4", + "url": "https://github.com/RenderKit/embree/releases/download/v4.4.0/embree-4.4.0.arm64.macosx.zip", + "symlink": { + "lib/libembree4.dylib": "lib/libembree4.4.dylib", + "lib/libtbb.dylib": "lib/libtbb.12.11.dylib", + "lib/libtbb.12.dylib": "lib/libtbb.12.11.dylib" + } + }, + { + "extract_skip": ["doc/*"], + "name": "embree4", + "platform": "windows", + "architecture": "amd64", + "sha256": "d951e5e6bd295c54cdd66be9cdb44a4e8c42fb38a99f94f79305e48765fc3454", + "target": "../embree4", + "url": "https://github.com/RenderKit/embree/releases/download/v4.4.0/embree-4.4.0.x64.windows.zip" + } +] diff --git a/ci/fetch-embree.py b/package/fetch-embree.py similarity index 80% rename from ci/fetch-embree.py rename to package/fetch-embree.py index 8548467..12494c6 100755 --- a/ci/fetch-embree.py +++ b/package/fetch-embree.py @@ -3,15 +3,16 @@ and copy them into the home directory for every plaform. """ +import argparse +import json +import logging import os import sys -import json import tarfile -import logging -import argparse -from io import BytesIO from fnmatch import fnmatch -from platform import system +from io import BytesIO +from platform import system, uname +from subprocess import check_call from typing import Optional from zipfile import ZipFile @@ -84,6 +85,7 @@ def handle_fetch( extract_skip: Optional[bool] = None, extract_only: Optional[bool] = None, strip_components: int = 0, + symlink: Optional[dict] = None, ): """A macro to fetch a remote resource (usually an executable) and move it somewhere on the file system. @@ -130,7 +132,7 @@ def handle_fetch( members = tar.infolist() else: # mode needs to know what type of compression - mode = f'r:{url.split(".")[-1]}' + mode = f"r:{url.split('.')[-1]}" # get the archive tar = tarfile.open(fileobj=BytesIO(raw), mode=mode) members = tar.getmembers() @@ -175,19 +177,52 @@ def handle_fetch( # python os.chmod takes an octal value os.chmod(path, int(str(chmod), base=8)) + if symlink is not None: + for k, v in symlink.items(): + # todo : doesn't work on windows obviously + check_call(["ln", "-sf", os.path.join(target, v), os.path.join(target, k)]) + def load_config(path: Optional[str] = None) -> list: """Load a config file for embree download locations.""" if path is None or len(path) == 0: # use a default config file path = os.path.join(_cwd, "embree.json") - with open(path, "r") as f: + with open(path) as f: return json.load(f) -def is_current_platform(platform: str) -> bool: - """Check to see if a string platform identifier matches the current platform.""" +def is_current_platform(platform: str, architecture: Optional[str]) -> bool: + """Check to see if a string platform identifier matches the current platform. + + Parameters + ---------- + platform + Checked against `platform.system` + architecture + Checked against `platform.uname.machine` + + Returns + ------- + matched + If the current platform matches the request. + + """ # 'linux', 'darwin', 'windows' + + if architecture is not None: + # Check for cibuildwheel target architecture first + target_arch = os.environ.get('ARCHFLAGS', '') + if 'arm64' in target_arch: + machine = 'arm64' + elif 'x86_64' in target_arch: + machine = 'x86_64' + else: + machine = uname().machine.lower() + + if architecture.lower() not in machine.lower(): + return False + current = system().lower().strip() if current.startswith("dar"): return platform.startswith("dar") or platform.startswith("mac") @@ -217,9 +252,19 @@ def is_current_platform(platform: str) -> bool: else: select = set(" ".join(args.install).replace(",", " ").split()) + print(system(), uname()) + for option in config: - if option["name"] in select and is_current_platform(option["platform"]): + print( + option["platform"], + option.get("architecture", None), + is_current_platform(option["platform"], option.get("architecture", None)), + ) + if option["name"] in select and is_current_platform( + option["platform"], option.get("architecture", None) + ): subset = option.copy() subset.pop("name") subset.pop("platform") + subset.pop("architecture") handle_fetch(**subset) diff --git a/pyproject.toml b/pyproject.toml index 3292cc2..860b2c9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,17 +1,15 @@ [build-system] requires = [ "setuptools>=59.0", - "tomli>=0.10; python_version<'3.7'", "numpy", "cython>=3.0" ] - build-backend = "setuptools.build_meta" [project] name = "embreex" -version = "2.17.7.post7" requires-python = ">=3.8" +version = "4.4.0rc0" dependencies = ["numpy"] description = "Python binding for Intel's Embree ray engine" @@ -36,20 +34,20 @@ skip = "*i686 *-win32 *musllinux*" manylinux-x86_64-image = "manylinux_2_28" before-test = "pip install pytest" test-command = "pytest -v {project}/tests" -before-build = "python {project}/ci/fetch-embree.py --install embree2" +before-build = "python {project}/package/fetch-embree.py --install embree4" [tool.cibuildwheel.windows] -before-build = "pip install delvewheel && python {project}\\ci\\fetch-embree.py --install embree2" -repair-wheel-command = "delvewheel repair --add-path embree2\\bin --no-mangle tbb12.dll;embree2.dll -w {dest_dir} {wheel}" +before-build = "pip install delvewheel && python {project}\\package\\fetch-embree.py --install embree4" +repair-wheel-command = "delvewheel repair --add-path embree4\\bin --no-mangle tbb12.dll;embree4.dll -w {dest_dir} {wheel}" [tool.cibuildwheel.macos] -repair-wheel-command = "DYLD_LIBRARY_PATH=embree2/lib delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}" +environment = { MACOSX_DEPLOYMENT_TARGET = "13.0" } +repair-wheel-command = "DYLD_LIBRARY_PATH=embree4/lib delocate-listdeps -a {wheel} && DYLD_LIBRARY_PATH=embree4/lib delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}" [tool.cibuildwheel.linux] -repair-wheel-command = "LD_LIBRARY_PATH=`realpath embree2/lib`; auditwheel repair -w {dest_dir} {wheel}" +repair-wheel-command = "LD_LIBRARY_PATH=`realpath embree4/lib`; auditwheel repair -w {dest_dir} {wheel}" -[tool.ruff] +[tool.ruff.lint] select = ["E", "F", # the default rules - "T201", # disallow print statements - "B", # pass bugbear - "D"] + "B" # pass bugbear + ] diff --git a/setup.py b/setup.py index 92e3053..24d6e85 100755 --- a/setup.py +++ b/setup.py @@ -15,29 +15,34 @@ def ext_modules(): """Generate a list of extension modules for embreex.""" if os.name == "nt": # embree search locations on windows - includes = [ - get_include(), - "c:/Program Files/Intel/Embree2/include", - os.path.join(_cwd, "embree2", "include"), - ] + includes = [get_include(), + 'c:/Program Files/Intel/Embree4/include', + os.path.join(_cwd, 'embree4', 'include')] libraries = [ - "c:/Program Files/Intel/Embree2/lib", - os.path.join(_cwd, "embree2", "lib"), - ] + 'c:/Program Files/Intel/Embree4/lib', + os.path.join(_cwd, 'embree4', 'lib')] else: # embree search locations on posix - includes = [ - get_include(), - "/opt/local/include", - os.path.join(_cwd, "embree2", "include"), - ] - libraries = ["/opt/local/lib", os.path.join(_cwd, "embree2", "lib")] + includes = [get_include(), + '/opt/local/include', + os.path.join(_cwd, 'embree4', 'include')] + libraries = ['/opt/local/lib', + os.path.join(_cwd, 'embree4', 'lib')] ext_modules = cythonize("embreex/*.pyx", include_path=includes, language_level=2) for ext in ext_modules: ext.include_dirs = includes ext.library_dirs = libraries - ext.libraries = ["embree"] + # on macOS with Embree 4.x, link against the versioned library directly + if sys.platform == "darwin": + ext.libraries = ["embree4.4"] + # Add rpath to find libembree4 during build and set loader_path for runtime + ext.extra_link_args = [ + "-Wl,-rpath,@loader_path", + "-Wl,-rpath," + os.path.join(_cwd, 'embree4', 'lib') + ] + else: + ext.libraries = ["embree4"] return ext_modules diff --git a/tests/test_intersection.py b/tests/test_intersection.py index 0525968..a10fc8c 100644 --- a/tests/test_intersection.py +++ b/tests/test_intersection.py @@ -205,6 +205,19 @@ def test_intersect(self): self.assertTrue(np.allclose([0.8, 0.6], v)) +class TestOccludedQuery(TestCase): + def test_occluded(self): + """Occluded query: hits return >= 0, misses return -1.""" + triangles = np.array(xplane(7.0), "float32") + scene = rtcs.EmbreeScene() + TriangleMesh(scene, triangles) + origins, dirs = define_rays_origins_and_directions() + res = scene.run(origins, dirs, query="OCCLUDED") + # First 3 rays hit the plane, 4th misses (y=-8.2 outside) + self.assertTrue(np.all(res[:3] >= 0)) + self.assertEqual(res[3], -1) + + if __name__ == "__main__": from unittest import main