diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 64545698e6..0892a2dab5 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -13,15 +13,34 @@ FetchContent_MakeAvailable(pybind11) # Declare our python embree module pybind11_add_module(pyembree - src/bindings.cpp - #pyembree.cpp - #intersect_callback_guard.cpp - #rtcore_common.cpp - #rtcore_device.cpp - #rtcore_buffer.cpp - #rtcore_ray.cpp - #rtcore_geometry.cpp - #rtcore_scene.cpp - #rtcore_builder.cpp - #rtcore_quaternion.cpp -) \ No newline at end of file + #src/bindings.cpp + src/pyembree.cpp + src/intersect_callback_guard.cpp + src/rtcore_common.cpp + src/rtcore_device.cpp + src/rtcore_buffer.cpp + src/rtcore_ray.cpp + src/rtcore_geometry.cpp + src/rtcore_scene.cpp + src/rtcore_builder.cpp + src/rtcore_quaternion.cpp +) + + +# Make sure we have embree +if (NOT embree_DIR) + message("-- building embree from source") + option(EMBREE_INSTALL_DEPENDENCIES "always on for pyembree" ON) + option(EMBREE_TUTORIALS "alway off for pyembree" OFF) + set(embree_BINARY_DIR ${CMAKE_BINARY_DIR}/_deps/embree-build) + message("bin dir ${embree_BINARY_DIR}") + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/.. ${embree_BINARY_DIR}) + + message("EMBREE LIBS: ${EMBREE_LIBRARIES}") +else() + message("-- using embree from ${embree_DIR}") + set(embree_BINARY_DIR "${embree_DIR}/../../../bin") +endif() + +target_include_directories(pyembree PRIVATE ${CMAKE_INSTALL_PREFIX}/include) +target_link_libraries(pyembree PRIVATE embree) \ No newline at end of file diff --git a/python/CMakePresets.json b/python/CMakePresets.json deleted file mode 100644 index 1e4f7863b3..0000000000 --- a/python/CMakePresets.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "version": 4, - "cmakeMinimumRequired": { - "major": 3, - "minor": 11, - "patch": 0 - }, - "include": [ - "../scripts/cmake-presets/os.json", - "../scripts/cmake-presets/package.json", - "../scripts/cmake-presets/compiler.json", - "../scripts/cmake-presets/tbb.json", - "../scripts/cmake-presets/continuous.json", - "../scripts/cmake-presets/nightly.json", - "../scripts/cmake-presets/release.json", - "../scripts/cmake-presets/integrate.json", - "../scripts/cmake-presets/performance.json" - ], - "configurePresets": [ - { - "name": "windows-pyembree-x", - "binaryDir": "${sourceDir}/build", - "inherits": ["package-windows", "env", "dpcpp-windows", "ispc_NEWEST", "tbb_NEWEST"], - "cacheVariables": { - "CMAKE_INSTALL_PREFIX": "./build/install", - "CMAKE_BUILD_TYPE": "Release", - "EMBREE_SYCL_SUPPORT": "ON", - "EMBREE_SYCL_L0_RTAS_BUILDER" : "ON", - "EMBREE_SYCL_AOT_DEVICES": "none", - "EMBREE_MAX_ISA": "SSE2" - } - } - ] -} diff --git a/python/README.md b/python/README.md index c08c461dea..83bc875b41 100644 --- a/python/README.md +++ b/python/README.md @@ -1,33 +1,62 @@ -# pyembree - Python bindings for embree +# PyEmbree - Python bindings for embree -## Building pyembree +These Python bindings for Embree are available under the same license as IntelĀ® Embree. +By utilizing these bindings, you can access the full capabilities of the Embree API through a Python interface, making it easier to integrate Embree's powerful ray tracing functionalities into your Python projects. +For a comprehensive understanding of the available API functions and their usage, please refer to the Embree C-API documentation. +This documentation provides detailed descriptions and examples to help you effectively get you started with Embree in python applications. -``` -cmake -B build -D embree_DIR=path/to/installed/embree/cmake/files -cmake --build build -``` +This is a pre-release version, and PyEmbree is still under active development. +In its current state, it provides a one-to-one mapping of the C-style Embree API, allowing users to utilize Embree's functionalities directly through Python. +This is however expected to change in the future, when Embree's SYCL support is integrated in a more unified and streamlined manner. +As development progresses, users can anticipate enhancements that will simplify and enrich the interaction with Embree's powerful ray tracing capabilities. -## Running test -Make sure that build artefacts of pyembree can be sourced by python, i.e. setting your PYTHONPATH. -Make sure that embree binaries are located in you PATH. +## Building, Installing, and Testing PyEmbree -### Linux +PyEmbree provides a `setup.py` to create a python wheel and install it locally to the system. +To do so follow a few simple steps inside the `python` subfolder inside the Embree repository: ``` -export PYTHONPATH=\build\src:$PYTHONPATH -python - ->>> import pyembree ->>> print(pyembree.RTC_INVALID_GEOMETRY_ID) +pip install . ``` -### Windows +You may then run the tutorials, e.g.: ``` -$env:PYTHONPATH += "\build\src\Debug" -python +python tutorials/minimal.py +``` + +### Troubleshooting + +Dependencies are built and installed using the pip command. +If you encounter issues loading the module, please ensure that your PATH environment variable includes the location where the dependencies are installed. + + +## API Extensions + +Embree functions that handle arrays of data, such as the rtcIntersect* class of functions, accept both Python lists and NumPy arrays. +When using NumPy arrays, it is crucial to ensure that the data is properly aligned to meet Embree's requirements. +To facilitate the use of these functions with NumPy, several helper functions are provided. +These helpers assist with generating and accessing ray hit data, making it easier to integrate Embree's capabilities with NumPy arrays. + +### rtcCreateRayHits + +This function creates a ray hits structure array from several NumPy arrays, which provide the ray origins, directions, and ray IDs component-wise. +The resulting Python array is properly aligned for use with rtcIntersect*. + +### rtcTransformRayHits + +Applies a transformation to a list of ray hit structures. + +### rtcRayHits_get_org(x|y|z), rtcRayHits_get_dir(x|y/|), rtcRayHits_get_tfar, rtcRayHits_get_rayids + +These functions provide convenient access to components within an array of ray hit structures. +They enable straightforward conversion between a structure of arrays and an array of structures for ray hits. + +### rtcIntersectN ->>> import pyembree ->>> print(pyembree.RTC_INVALID_GEOMETRY_ID) -``` \ No newline at end of file +This function extends the functionality of the rtcIntersect(1|4|8|16) functions found in the Embree C-API. +While it is possible to perform ray packet intersections from Python, doing so typically involves complex steps to ensure that arrays of ray hits are correctly aligned. +Constructing these aligned arrays can be challenging and error-prone from within Python. +The rtcIntersectN function simplifies this process by providing an interface that allows for the intersection of arrays of ray hit structures of any length. +It manages the complexities of ray packets and parallelization automatically, making the process more straightforward and efficient from within Python. \ No newline at end of file diff --git a/python/__init__.py b/python/__init__.py index 7185a4f73c..82c5f72894 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -1,9 +1 @@ -print("AOEUAOEUAOEUAOEUAOEU") -print("AOEUAOEUAOEUAOEUAOEU") -print("AOEUAOEUAOEUAOEUAOEU") -print("AOEUAOEUAOEUAOEUAOEU") -print("AOEUAOEUAOEUAOEUAOEU") -print("AOEUAOEUAOEUAOEUAOEU") -print("AOEUAOEUAOEUAOEUAOEU") - from .pyembree import * \ No newline at end of file diff --git a/python/setup.py b/python/setup.py index a4545bfab4..cbe8cef15b 100644 --- a/python/setup.py +++ b/python/setup.py @@ -9,12 +9,34 @@ from setuptools import setup, Extension from setuptools.command.build_ext import build_ext + + +def tabulate(data): + if not data: + return "" + + col_widths = [max(len(str(item)) for item in col) for col in zip(*data)] + format_str = ' | '.join(f'{{:<{width}}}' for width in col_widths) + table = [format_str.format(*row) for row in data] + + return '\n'.join(table) + + class CMakeExtension(Extension): def __init__(self, name): Extension.__init__(self, name, sources=[]) class CMakeBuild(build_ext): + + user_options = build_ext.user_options + [ + ('embree-config=', None, 'Comma separated list of embree configs') + ] + + def initialize_options(self): + build_ext.initialize_options(self) + self.embree_config = "" + def run(self): try: out = subprocess.check_output(['cmake', '--version']) @@ -29,17 +51,22 @@ def run(self): def build_extension(self, ext): - extdir = os.path.join(os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name))), ext.name) - print("AOEUAOEUAOEUAOEU") - print("AOEUAOEUAOEUAOEU") - print("AOEUAOEUAOEUAOEU") - print("extdir") - print(extdir) + + extension_dir = os.path.join(os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name))), ext.name) + setuppy_dir = os.path.abspath('') + build_dir = setuppy_dir + os.path.sep + 'build' + embree_install_dir = build_dir + os.path.sep + 'embree_install' + print(tabulate([ + ["extension_dir", extension_dir], + ["setuppy_dir", setuppy_dir], + ["build_dir", build_dir], + ["embree_install_dir", embree_install_dir], + ])) cmake_args = [ - '-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir, - '-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE=' + extdir, - '-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG=' + extdir, + '-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extension_dir, + '-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE=' + extension_dir, + '-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG=' + extension_dir, #'-DPYTHON_EXECUTABLE=' + sys.executable, #'-G Ninja', ] @@ -50,27 +77,28 @@ def build_extension(self, ext): # '-DCMAKE_C_COMPILER=clang', #] - - print("debug") - print(self.debug) - cfg = 'Debug' if self.debug else 'Release' - cmake_args += ['DCMAKE_BUILD_TYPE=' + cfg] + cmake_args += ['-DCMAKE_BUILD_TYPE=' + cfg] + cmake_args += ['-DEMBREE_MAX_ISA=SSE2'] + cmake_args += ['-DCMAKE_INSTALL_PREFIX=' + embree_install_dir] print("cmake_args") - print(cmake_args) + print(tabulate([s.split('=') for s in cmake_args])) env = os.environ.copy() if not os.path.exists(self.build_temp): os.makedirs(self.build_temp) - subprocess.check_call(['cmake', os.path.abspath('')] + cmake_args, cwd=self.build_temp, env=env) - subprocess.check_call(['cmake', '--build', '.', '--config', cfg, '--verbose'], cwd=self.build_temp) + subprocess.check_call(['cmake', setuppy_dir] + cmake_args, cwd=self.build_temp, env=env) + subprocess.check_call(['cmake', '--build', '.', '--config', cfg, '-j', '--verbose'], cwd=self.build_temp) subprocess.check_call(['cmake', '--install', '.', '--verbose'], cwd=self.build_temp) - shutil.copy("__init__.py", extdir) - + # copy additional dependencies + shutil.copy("__init__.py", extension_dir) + embree_bin_dir = embree_install_dir + os.path.sep + 'bin' + for f in os.listdir(embree_bin_dir): + shutil.copy(os.path.join(embree_bin_dir, f), extension_dir) setup( diff --git a/python/src/CMakeLists.txt b/python/src/CMakeLists.txt deleted file mode 100644 index b18dbfb6e2..0000000000 --- a/python/src/CMakeLists.txt +++ /dev/null @@ -1,64 +0,0 @@ -set(CMAKE_CXX_STANDARD 17) - -include(ExternalProject) - -# Make sure we have embree -if (NOT embree_DIR) - message("-- building embree from source") - option(EMBREE_INSTALL_DEPENDENCIES "always on for pyembree" ON) - option(EMBREE_TUTORIALS "alway off for pyembree" OFF) - set(embree_BINARY_DIR ${CMAKE_BINARY_DIR}/_deps/embree-build) - message("bin dir ${embree_BINARY_DIR}") - add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../.. ${embree_BINARY_DIR}) - - message("EMBREE LIBS: ${EMBREE_LIBRARIES}") -else() - message("-- using embree from ${embree_DIR}") - set(embree_BINARY_DIR "${embree_DIR}/../../../bin") -endif() - - -# Use pybind11 from github source -include(FetchContent) -message("-- download and build pybind11 from source") -FetchContent_Declare( - pybind11 - GIT_REPOSITORY "https://github.com/pybind/pybind11.git" - SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/pybind11" -) -FetchContent_MakeAvailable(pybind11) - - -# Declare our python embree module -pybind11_add_module(pyembree - pyembree.cpp - intersect_callback_guard.cpp - rtcore_common.cpp - rtcore_device.cpp - rtcore_buffer.cpp - rtcore_ray.cpp - rtcore_geometry.cpp - rtcore_scene.cpp - rtcore_builder.cpp - rtcore_quaternion.cpp -) - -target_include_directories(pyembree PRIVATE ${CMAKE_INSTALL_PREFIX}/include) -target_link_libraries(pyembree PRIVATE embree) - -if(EMBREE_SYCL_SUPPORT) - add_compile_definitions(EMBREE_SYCL_SUPPORT=1) - message("${CMAKE_MODULE_PATH}") - message("${CMAKE_CURRENT_SOURCE_DIR}/../../common/cmake") - set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../common/cmake" ${CMAKE_MODULE_PATH}) - message("${CMAKE_MODULE_PATH}") - include(dpcpp) -endif() - -install(TARGETS pyembree - COMPONENT python - RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" - LIBRARY DESTINATION "${CMAKE_INSTALL_BINDIR}" - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") - -message("install prefix ${CMAKE_INSTALL_PREFIX}") \ No newline at end of file diff --git a/python/src/bindings.cpp b/python/src/bindings.cpp deleted file mode 100644 index cec14de931..0000000000 --- a/python/src/bindings.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include -#include - - -int add(int i, int j) { - std::cout << "AOUEAOEUAOEUAOUE" << std::endl; - return i + j; -} - -PYBIND11_MODULE(pyembree, m) { - m.def("add", &add); -} \ No newline at end of file diff --git a/python/tutorials/intersection_filter.ipynb b/python/tutorials/intersection_filter.ipynb deleted file mode 100644 index 57e14b448b..0000000000 --- a/python/tutorials/intersection_filter.ipynb +++ /dev/null @@ -1,72 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "ename": "ModuleNotFoundError", - "evalue": "No module named 'common'", - "output_type": "error", - "traceback": [ - "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[1;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[1;32mIn[3], line 3\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[38;5;66;03m# We setup an embree device and scene as we did in the minimal tutorial\u001b[39;00m\n\u001b[0;32m 2\u001b[0m \u001b[38;5;66;03m# We also declare some helper functions to create geometry, rays and output intersection results\u001b[39;00m\n\u001b[1;32m----> 3\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mcommon\u001b[39;00m\n\u001b[0;32m 4\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mpyembree\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01mpe\u001b[39;00m\n\u001b[0;32m 5\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mctypes\u001b[39;00m\n", - "\u001b[1;31mModuleNotFoundError\u001b[0m: No module named 'common'" - ] - } - ], - "source": [ - "# We setup an embree device and scene as we did in the minimal tutorial\n", - "# We also declare some helper functions to create geometry, rays and output intersection results, see the minimal tutorial for more information on this\n", - "from common import create_triangle, create_ray, print_hit\n", - "import pyembree as pe\n", - "import ctypes\n", - "import numpy as np\n", - "\n", - "d = pe.rtcNewDevice(None)\n", - "s = pe.rtcNewScene(d)\n", - "\n", - "# We crate two triangles\n", - "create_triangle(s)\n", - "create_triangle(s, 3)\n", - "pe.rtcCommitScene(s)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def intersect_filter(args):\n", - " print(\"Hello World from intersect!\")\n", - "\n", - "will_hit = create_ray(0.33, 0.33, -1, 0, 0, 1)\n", - "wont_hit = create_ray(1.00, 1.00, -1, 0, 0, 1)\n", - "\n", - "intersect_args = pe.RTCIntersectArguments()\n", - "intersect_args.flags = pe.RTC_RAY_QUERY_FLAG_INVOKE_ARGUMENT_FILTER\n", - "intersect_args.filter = intersect_filter\n", - "\n", - "pe.rtcIntersect1(s, will_hit, intersect_args)\n", - "pe.rtcIntersect1(s, wont_hit, intersect_args)\n", - "print_hit(will_hit)\n", - "print_hit(wont_hit)\n", - "\n", - "pe.rtcReleaseScene(s)\n", - "pe.rtcReleaseDevice(d)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/python/tutorials/intersectn.ipynb b/python/tutorials/intersectn.ipynb deleted file mode 100644 index 974d087399..0000000000 --- a/python/tutorials/intersectn.ipynb +++ /dev/null @@ -1,95 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "ename": "NameError", - "evalue": "name 'cm' is not defined", - "output_type": "error", - "traceback": [ - "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[1;32mIn[1], line 19\u001b[0m\n\u001b[0;32m 16\u001b[0m s \u001b[38;5;241m=\u001b[39m pe\u001b[38;5;241m.\u001b[39mrtcNewScene(d)\n\u001b[0;32m 18\u001b[0m \u001b[38;5;66;03m# We crate two triangles\u001b[39;00m\n\u001b[1;32m---> 19\u001b[0m \u001b[43mcm\u001b[49m\u001b[38;5;241m.\u001b[39mcreate_triangle(d, s)\n\u001b[0;32m 20\u001b[0m cm\u001b[38;5;241m.\u001b[39mcreate_triangle(d, s, \u001b[38;5;241m3\u001b[39m)\n\u001b[0;32m 21\u001b[0m pe\u001b[38;5;241m.\u001b[39mrtcCommitScene(s)\n", - "\u001b[1;31mNameError\u001b[0m: name 'cm' is not defined" - ] - } - ], - "source": [ - "# We setup an embree device and scene as we did in the minimal tutorial\n", - "# We also declare some helper functions to create geometry, rays and output intersection results, see the minimal tutorial for more information on this\n", - "#from common import create_triangle, create_ray, print_hit\n", - "\n", - "import pyrootutils\n", - "import os\n", - "pyrootutils.setup_root(os.getcwd(), indicator=\"CMakeLists.txt\", pythonpath=True)\n", - "\n", - "from tutorials.common import create_triangle, create_ray, print_hit\n", - "\n", - "import pyembree as pe\n", - "import ctypes\n", - "import numpy as np\n", - "\n", - "d = pe.rtcNewDevice(None)\n", - "s = pe.rtcNewScene(d)\n", - "\n", - "# We crate two triangles\n", - "create_triangle(d, s)\n", - "create_triangle(d, s, 3)\n", - "pe.rtcCommitScene(s)" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Ray (0.33, 0.33, -1.00) -> (0.00, 0.00, 1.00): Found intersection on geometry 0, primitive 0 at tfar=1.0\n", - "Ray (1.00, 1.00, -1.00) -> (0.00, 0.00, 1.00): Did not find any intersection\n", - "Ray (3.33, 0.33, -1.00) -> (0.00, 0.00, 1.00): Found intersection on geometry 1, primitive 0 at tfar=1.0\n", - "Ray (4.00, 1.00, -1.00) -> (0.00, 0.00, 1.00): Did not find any intersection\n" - ] - } - ], - "source": [ - "# Collect rays in a python list\n", - "rayhits = [create_ray(0.33, 0.33, -1, 0, 0, 1), create_ray(1.00, 1.00, -1, 0, 0, 1), create_ray(3.33, 0.33, -1, 0, 0, 1), create_ray(4, 1, -1, 0, 0, 1)]\n", - "# Intersect all rays in the list.\n", - "# Intersection calls are done in parallel, we can define the number of threads used in the last function argument. 0 will use the maximum of the hardware capabilities.\n", - "pe.rtcIntersectN(s, rayhits, None, 0)\n", - "for rh in rayhits:\n", - " print_hit(rh)\n", - "\n", - "pe.rtcReleaseScene(s)\n", - "pe.rtcReleaseDevice(d)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.8" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/python/tutorials/minimal.ipynb b/python/tutorials/minimal.ipynb deleted file mode 100644 index 239fc6de6f..0000000000 --- a/python/tutorials/minimal.ipynb +++ /dev/null @@ -1,179 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# import pyembree\n", - "# make sure that the path with pyembree binaries is added to the $PYTHONPATH environment variable\n", - "import pyembree as pe\n", - "import ctypes\n", - "\n", - "# We first create several Embree objects:\n", - "# * an embree device with a default configuration (see sec. 5.1 and 7.1 in the documention for details)\n", - "# * an embree scene to hold our geometry\n", - "d = pe.rtcNewDevice(None)\n", - "s = pe.rtcNewScene(d)" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "# A scene is a collection of geometry objects. Scenes are what the intersect / occluded API functions work on. \n", - "# You can think of a scene as an acceleration structure, e.g. a bounding-volume hierarchy.\n", - "#\n", - "# Next, we instruct Embree to create vertex and index buffers into which we copy the geomety data. \n", - "# For complex scenes, shared buffers are often better choice but special care must be taken to ensure proper alignment and padding. \n", - "# This is described in more detail in the API documentation.\n", - "g = pe.rtcNewGeometry(d, pe.RTC_GEOMETRY_TYPE_TRIANGLE)\n", - "vertices = pe.rtcSetNewGeometryBuffer(g, pe.RTC_BUFFER_TYPE_VERTEX, 0, pe.RTC_FORMAT_FLOAT3, 3*ctypes.sizeof(ctypes.c_float), 3).as_float()\n", - "indices = pe.rtcSetNewGeometryBuffer(g, pe.RTC_BUFFER_TYPE_INDEX, 0, pe.RTC_FORMAT_UINT3, 3*ctypes.sizeof(ctypes.c_uint), 1).as_uint()\n", - "vertices[0] = 0.0\n", - "vertices[1] = 0.0\n", - "vertices[2] = 0.0\n", - "vertices[3] = 1.0\n", - "vertices[4] = 0.0\n", - "vertices[5] = 0.0\n", - "vertices[6] = 0.0\n", - "vertices[7] = 1.0\n", - "vertices[8] = 0.0\n", - "indices[0] = 0\n", - "indices[1] = 1\n", - "indices[2] = 2" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "# After setting the geometry data the geometry can be committed and attached to the scene. \n", - "# Embree objects such as RTCDevice, RTCScene and RTCGeometry are reference-counted. \n", - "# This means, that the scene takes ownership of the geometry when we attach it and we can release the geometry handle. \n", - "# The API function rtcAttachGeometry returns a geometry ID which can be used identify intersected objects when the scene contains multiple geometry objects. \n", - "# We finish the setup by committing the scene, after which the scene can be intersected.\n", - "pe.rtcCommitGeometry(g)\n", - "pe.rtcAttachGeometry(s, g)\n", - "pe.rtcReleaseGeometry(g)\n", - " \n", - "pe.rtcCommitScene(s)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "# To perform a ray intersect, we obviously have to create a ray first. \n", - "# Embree also expects an intersection context which can be used for advanced features such as intersection filters and instancing.\n", - "def create_ray(ox, oy, oz, dx, dy, dz):\n", - " rayhit = pe.RTCRayHit()\n", - " rayhit.ray.org_x = ox\n", - " rayhit.ray.org_y = oy\n", - " rayhit.ray.org_z = oz\n", - " rayhit.ray.dir_x = dx\n", - " rayhit.ray.dir_y = dy\n", - " rayhit.ray.dir_z = dz\n", - " # We set the ray values tnear to 0 and tfar to infinity to indicate that the ray starts at origin (org_x, org_y, org_z) and is unbounded. \n", - " rayhit.ray.tnear = 0\n", - " rayhit.ray.tfar = float('inf')\n", - " rayhit.ray.mask = 0xffffffff\n", - " rayhit.ray.flags = 0\n", - " # We also set geomID to RTC_INVALID_GEOMETRY_ID. \n", - " rayhit.hit.geomID = pe.RTC_INVALID_GEOMETRY_ID\n", - " rayhit.hit.instID[0] = pe.RTC_INVALID_GEOMETRY_ID\n", - " return rayhit\n", - "\n", - "will_hit = create_ray(0.33, 0.33, -1, 0, 0, 1)\n", - "wont_hit = create_ray(1, 1, -1, 0, 0, 1)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "# Now we can call Embree's intersect function and check if the ray intersects the triangle.\n", - "pe.rtcIntersect1(s, will_hit, None)\n", - "pe.rtcIntersect1(s, wont_hit, None)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Ray (0.33000001311302185, 0.33000001311302185, -1.0) -> (0.0, 0.0, 1.0): Found intersection on geometry 0, primitive 0 at tfar=1.0\n", - "Ray (1.0, 1.0, -1.0) -> (0.0, 0.0, 1.0): Did not find any intersection\n" - ] - } - ], - "source": [ - "# We can read out the results now:\n", - "# If an intersection occured\n", - "# * geomID will contain the ID of the geometry that has been intersected. \n", - "# * the value tfar will contain the ray parameter which we can compute the point at which the ray and triangle intersect as (org_x, org_y, org_z) + t * (dir_x, dir_y, dir_z).\n", - "def print_hit(rayhit):\n", - " if rayhit.hit.geomID != pe.RTC_INVALID_GEOMETRY_ID:\n", - " print(f\"Ray ({rayhit.ray.org_x}, {rayhit.ray.org_y}, {rayhit.ray.org_z}) -> ({rayhit.ray.dir_x}, {rayhit.ray.dir_y}, {rayhit.ray.dir_z}): Found intersection on geometry {rayhit.hit.geomID}, primitive {rayhit.hit.primID} at tfar={rayhit.ray.tfar}\")\n", - " else:\n", - " print(f\"Ray ({rayhit.ray.org_x}, {rayhit.ray.org_y}, {rayhit.ray.org_z}) -> ({rayhit.ray.dir_x}, {rayhit.ray.dir_y}, {rayhit.ray.dir_z}): Did not find any intersection\")\n", - "\n", - "print_hit(will_hit)\n", - "print_hit(wont_hit)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "# At the end of an application all resources that where allocated through Embree should be released. \n", - "# Note that the geometry will be released automatically, when the (last) scene the geometry is attached to is released.\n", - "pe.rtcReleaseScene(s)\n", - "pe.rtcReleaseDevice(d)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.8" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -}