├── pyembree ├── __init__.pxd ├── __init__.py ├── mesh_construction.h ├── rtcore.pyx ├── rtcore.pxd ├── rtcore_scene.pxd ├── rtcore_geometry_user.pxd ├── rtcore_ray.pxd ├── rtcore_geometry.pxd ├── rtcore_scene.pyx ├── triangles.pyx └── mesh_construction.pyx ├── news └── TEMPLATE.rst ├── .gitignore ├── AUTHORS ├── .travis-install.sh ├── recipes └── pyembree │ ├── build.sh │ └── meta.yaml ├── setup.py ├── README.rst ├── .mailmap ├── .authors.yml ├── rever.xsh ├── CHANGELOG.rst ├── LICENSE ├── examples ├── intersection.py └── attenuate.py ├── .travis.yml └── tests └── test_intersection.py /pyembree/__init__.pxd: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyembree/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.1.6' 2 | -------------------------------------------------------------------------------- /news/TEMPLATE.rst: -------------------------------------------------------------------------------- 1 | **Added:** None 2 | 3 | **Changed:** None 4 | 5 | **Deprecated:** None 6 | 7 | **Removed:** None 8 | 9 | **Fixed:** None 10 | 11 | **Security:** None 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | __pycache__/ 3 | pyembree/*.c 4 | pyembree/*.cpp 5 | pyembree/*.cxx 6 | pyembree/*.so 7 | pyembree/.*.swp 8 | pyembree/*.pyc 9 | .*.swp 10 | pyembree.egg-info/ 11 | dist/ 12 | *.png 13 | 14 | .idea 15 | .vscode 16 | # Rever 17 | rever/ 18 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | All of the people who have made at least one contribution to pyembree. 2 | Authors are sorted by number of commits. 3 | 4 | * Anthony Scopatz 5 | * Guillaume Jacquenot 6 | * Dag Wästberg 7 | * Matthew Turk 8 | * atmyers 9 | * Sam Mellor 10 | * Kacper Kowalik 11 | 12 | 13 | -------------------------------------------------------------------------------- /.travis-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x # print cmds 3 | set -e # exit as soon as an error occurs 4 | 5 | # log 6 | msg=$(git log --pretty=oneline -1) 7 | echo "Building commit: $msg" 8 | 9 | # build 10 | conda build --no-test recipes/pyembree 11 | 12 | # install 13 | conda install --use-local pyembree 14 | -------------------------------------------------------------------------------- /recipes/pyembree/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export LD_LIBRARY_PATH=$PREFIX/lib 3 | export PATH=$PREFIX/bin:$PATH 4 | export DYLD_FALLBACK_LIBRARY_PATH=$PREFIX/lib/cyclus:$PREFIX/lib 5 | export C_INCLUDE_PATH=$PREFIX/include:$C_INCLUDE_PATH 6 | export CPLUS_INCLUDE_PATH=$PREFIX/include:$CPLUS_INCLUDE_PATH 7 | 8 | python setup.py install --prefix=$PREFIX 9 | -------------------------------------------------------------------------------- /recipes/pyembree/meta.yaml: -------------------------------------------------------------------------------- 1 | package: 2 | name: pyembree 3 | version: 0.1.6 4 | 5 | source: 6 | #fn: cymetric-src.tar.gz # ["TRAVIS" not in environ] 7 | #url: https://github.com/scopatz/pyembree/archive/master.tar.gz # ["TRAVIS" not in environ] 8 | #path: ../.. # ["TRAVIS" in environ] 9 | path: ../.. 10 | 11 | requirements: 12 | build: 13 | - embree 14 | - python 15 | - cython 16 | - numpy 17 | - setuptools 18 | run: 19 | - embree 20 | - python 21 | - numpy 22 | 23 | 24 | about: 25 | home: 26 | license: BSD 2 Clause 27 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup, find_packages 4 | 5 | import numpy as np 6 | from Cython.Build import cythonize 7 | 8 | include_path = [np.get_include()] 9 | 10 | ext_modules = cythonize('pyembree/*.pyx', language='c++', 11 | include_path=include_path) 12 | for ext in ext_modules: 13 | ext.include_dirs = include_path 14 | ext.libraries = ["embree"] 15 | 16 | setup( 17 | name="pyembree", 18 | version='0.1.6', 19 | ext_modules=ext_modules, 20 | zip_safe=False, 21 | packages=find_packages(), 22 | package_data = {'pyembree': ['*.pxd']} 23 | ) 24 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ======== 2 | pyembree 3 | ======== 4 | Python Wrapper for Embree 5 | 6 | Installation 7 | ------------ 8 | You can install pyembree (and embree) via the conda-forge package. 9 | 10 | .. code-block:: bash 11 | 12 | $ conda install -c conda-forge pyembree 13 | 14 | 15 | 16 | Suppressing errors 17 | ------------------ 18 | 19 | Creating multiple scenes produces some harmless error messages: 20 | :: 21 | ERROR CAUGHT IN EMBREE 22 | ERROR: Invalid operation 23 | ERROR MESSAGE: b'already initialized' 24 | 25 | These can be suppressed with: 26 | 27 | .. code-block:: python 28 | 29 | import logging 30 | logging.getLogger('pyembree').disabled = True 31 | -------------------------------------------------------------------------------- /pyembree/mesh_construction.h: -------------------------------------------------------------------------------- 1 | // This array is used to triangulate the hexahedral mesh elements 2 | // Each element has six faces with two triangles each. 3 | // The vertex ordering convention is assumed to follow that used 4 | // here: http://homepages.cae.wisc.edu/~tautges/papers/cnmev3.pdf 5 | // Note that this is the case for Exodus II data. 6 | int triangulate_hex[12][3] = { 7 | {0, 2, 1}, {0, 3, 2}, // Face is 0 3 2 1 8 | {4, 5, 6}, {4, 6, 7}, // Face is 4 5 6 7 9 | {0, 1, 5}, {0, 5, 4}, // Face is 0 1 5 4 10 | {1, 2, 6}, {1, 6, 5}, // Face is 1 2 6 5 11 | {0, 7, 3}, {0, 4, 7}, // Face is 0 4 7 3 12 | {3, 6, 2}, {3, 7, 6} // Face is 3 7 6 2 13 | }; 14 | 15 | // Similarly, this is used to triangulate the tetrahedral cells 16 | int triangulate_tetra[4][3] = { 17 | {0, 2, 1}, 18 | {0, 1, 3}, 19 | {0, 3, 2}, 20 | {1, 2, 3} 21 | }; 22 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | # This file was autogenerated by rever: https://regro.github.io/rever-docs/ 2 | # This prevent git from showing duplicates with various logging commands. 3 | # See the git documentation for more details. The syntax is: 4 | # 5 | # good-name bad-name 6 | # 7 | # You can skip bad-name if it is the same as good-name and is unique in the repo. 8 | # 9 | # This file is up-to-date if the command git log --format="%aN <%aE>" | sort -u 10 | # gives no duplicates. 11 | 12 | Anthony Scopatz 13 | Guillaume Jacquenot Guillaume Jacquenot 14 | Dag Wästberg 15 | Matthew Turk 16 | atmyers 17 | Sam Mellor Sam Mellor 18 | Kacper Kowalik 19 | -------------------------------------------------------------------------------- /.authors.yml: -------------------------------------------------------------------------------- 1 | - name: Guillaume Jacquenot 2 | email: guillaume.jacquenot@gmail.com 3 | alternate_emails: 4 | - Gjacquenot@users.noreply.github.com 5 | num_commits: 37 6 | first_commit: 2018-01-03 06:01:35 7 | github: Gjacquenot 8 | - name: Anthony Scopatz 9 | email: scopatz@gmail.com 10 | num_commits: 51 11 | first_commit: 2015-05-13 16:20:53 12 | - name: Sam Mellor 13 | email: github@u2ft.me 14 | alternate_emails: 15 | - U2Ft@users.noreply.github.com 16 | num_commits: 3 17 | first_commit: 2018-01-04 10:19:12 18 | github: U2Ft 19 | - name: Kacper Kowalik 20 | email: xarthisius.kk@gmail.com 21 | num_commits: 1 22 | first_commit: 2015-08-20 15:36:49 23 | github: Xarthisius 24 | - name: atmyers 25 | email: atmyers2@gmail.com 26 | num_commits: 6 27 | first_commit: 2015-05-15 21:28:00 28 | github: atmyers 29 | - name: Dag Wästberg 30 | email: dwastberg@gmail.com 31 | num_commits: 11 32 | first_commit: 2017-04-05 17:02:02 33 | github: dwastberg 34 | - name: Matthew Turk 35 | email: matthewturk@gmail.com 36 | num_commits: 10 37 | first_commit: 2015-05-13 16:42:25 38 | -------------------------------------------------------------------------------- /rever.xsh: -------------------------------------------------------------------------------- 1 | from rever.activity import dockeractivity 2 | 3 | $PROJECT = $GITHUB_REPO = 'pyembree' 4 | $GITHUB_ORG = 'scopatz' 5 | $WEBSITE_URL = 'https://github.com/scopatz/pyembree' 6 | 7 | with! dockeractivity(name='pyembree-examples', lang='sh'): 8 | python examples/attenuate.py --no-plots 9 | 10 | $ACTIVITIES = ['authors', 'pyembree-examples', 'version_bump', 'changelog', 11 | 'tag', 'push_tag', 'conda_forge','ghrelease' 12 | ] 13 | 14 | $VERSION_BUMP_PATTERNS = [ 15 | ('recipes/pyembree/meta.yaml', 'version:.*', 'version: $VERSION'), 16 | ('pyembree/__init__.py', r'__version__\s*=.*', "__version__ = '$VERSION'"), 17 | ('setup.py', r'version\s*=.*', "version='$VERSION',"), 18 | ] 19 | $CHANGELOG_FILENAME = 'CHANGELOG.rst' 20 | $CHANGELOG_TEMPLATE = 'TEMPLATE.rst' 21 | 22 | $DOCKER_APT_DEPS = ['gcc'] 23 | $DOCKER_CONDA_DEPS = ['numpy', 'embree', 'gcc', 'setuptools', 'cython'] 24 | $DOCKER_INSTALL_COMMAND = ('git clean -fdx && ' 25 | './setup.py install') 26 | $DOCKER_GIT_NAME = 'Anthony Scopatz' 27 | $DOCKER_GIT_EMAIL = 'scopatz@gmail.com' 28 | -------------------------------------------------------------------------------- /pyembree/rtcore.pyx: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | 4 | log = logging.getLogger('pyembree') 5 | 6 | cdef void print_error(RTCError code): 7 | if code == RTC_NO_ERROR: 8 | log.error("ERROR: No error") 9 | elif code == RTC_UNKNOWN_ERROR: 10 | log.error("ERROR: Unknown error") 11 | elif code == RTC_INVALID_ARGUMENT: 12 | log.error("ERROR: Invalid argument") 13 | elif code == RTC_INVALID_OPERATION: 14 | log.error("ERROR: Invalid operation") 15 | elif code == RTC_OUT_OF_MEMORY: 16 | log.error("ERROR: Out of memory") 17 | elif code == RTC_UNSUPPORTED_CPU: 18 | log.error("ERROR: Unsupported CPU") 19 | elif code == RTC_CANCELLED: 20 | log.error("ERROR: Cancelled") 21 | else: 22 | raise RuntimeError 23 | 24 | 25 | cdef class EmbreeDevice: 26 | def __init__(self): 27 | self.device = rtcNewDevice(NULL) 28 | 29 | def __dealloc__(self): 30 | rtcDeleteDevice(self.device) 31 | 32 | def __repr__(self): 33 | return 'Embree version: {0}.{1}.{2}'.format(RTCORE_VERSION_MAJOR, 34 | RTCORE_VERSION_MINOR, 35 | RTCORE_VERSION_PATCH) 36 | -------------------------------------------------------------------------------- /CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | =================== 2 | pyembree Change Log 3 | =================== 4 | 5 | .. current developments 6 | 7 | v0.1.6 8 | ==================== 9 | 10 | **Changed:** 11 | 12 | * Multiple ``TriangleMesh`` objects can now be added to a scene, and the scene is committed only as-needed. 13 | 14 | **Authors:** 15 | 16 | * Anthony Scopatz 17 | * Matthew Turk 18 | 19 | 20 | 21 | v0.1.5 22 | ==================== 23 | 24 | **Added:** 25 | 26 | * Calling EmbreeScene.run with query = 'DISTANCE' returns an array with the 27 | distance to the nearest hit, or tfar (default of 1e37) if there is no hit. 28 | * Set tfar to the same value for all points by passing a number to the dists argument in EmbreeScene.run 29 | 30 | 31 | 32 | v0.1.4 33 | ==================== 34 | 35 | 36 | 37 | v0.1.3 38 | ==================== 39 | 40 | **Changed:** 41 | 42 | * Error logging now uses the `logging` module 43 | 44 | 45 | 46 | 47 | v0.1.2 48 | ==================== 49 | 50 | **Fixed:** 51 | 52 | * setup.py and rever.xsh fix 53 | 54 | 55 | 56 | 57 | v0.1.1 58 | ==================== 59 | 60 | **Changed:** 61 | 62 | * Moved ``trianges`` module to ``triangles``. 63 | 64 | 65 | **Fixed:** 66 | 67 | * Fixed issue where ``RTC_GEOMETRY_STATIC`` was called ``RTCGEOMETRY_STATIC``. 68 | * Fixed attenuate example. 69 | * Fixed build for recent versions of Cython. 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Anthony Scopatz 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /examples/intersection.py: -------------------------------------------------------------------------------- 1 | import time 2 | import numpy as np 3 | 4 | from pyembree import rtcore_scene as rtcs 5 | from pyembree.mesh_construction import TriangleMesh 6 | 7 | N = 4 8 | 9 | def xplane(x): 10 | return [[[x, -1.0, -1.0], 11 | [x, +1.0, -1.0], 12 | [x, -1.0, +1.0]], 13 | [[x, +1.0, -1.0], 14 | [x, +1.0, +1.0], 15 | [x, -1.0, +1.0]]] 16 | 17 | 18 | triangles = xplane(7.0) 19 | triangles = np.array(triangles, 'float32') 20 | 21 | scene = rtcs.EmbreeScene() 22 | mesh = TriangleMesh(scene, triangles) 23 | 24 | origins = np.zeros((N, 3), dtype='float32') 25 | origins[:,0] = 0.1 26 | origins[0,1] = -0.2 27 | origins[1,1] = +0.2 28 | origins[2,1] = +0.3 29 | origins[3,1] = -8.2 30 | 31 | dirs = np.zeros((N, 3), dtype='float32') 32 | dirs[:, 0] = 1.0 33 | 34 | t1 = time.time() 35 | res = scene.run(origins, dirs, output=1) 36 | t2 = time.time() 37 | print("Ran in {0:.3f} s".format(t2-t1)) 38 | 39 | print('Output is a dict containing Embree results with id of intersected dimensionless coordinates') 40 | print(res) 41 | 42 | ray_inter = res['geomID'] >= 0 43 | print('{0} rays intersect geometry (over {1})'.format(sum(ray_inter), N)) 44 | print('Intersection coordinates') 45 | primID = res['primID'][ray_inter] 46 | u = res['u'][ray_inter] 47 | v = res['v'][ray_inter] 48 | w = 1 - u - v 49 | inters = np.vstack(w) * triangles[primID][:, 0, :] + \ 50 | np.vstack(u) * triangles[primID][:, 1, :] + \ 51 | np.vstack(v) * triangles[primID][:, 2, :] 52 | print(inters) 53 | -------------------------------------------------------------------------------- /pyembree/rtcore.pxd: -------------------------------------------------------------------------------- 1 | # rtcore.pxd wrapper 2 | 3 | cimport cython 4 | cimport numpy as np 5 | 6 | 7 | cdef extern from "embree2/rtcore.h": 8 | cdef int RTCORE_VERSION_MAJOR 9 | cdef int RTCORE_VERSION_MINOR 10 | cdef int RTCORE_VERSION_PATCH 11 | 12 | void rtcInit(const char* cfg) 13 | void rtcExit() 14 | 15 | cdef enum RTCError: 16 | RTC_NO_ERROR 17 | RTC_UNKNOWN_ERROR 18 | RTC_INVALID_ARGUMENT 19 | RTC_INVALID_OPERATION 20 | RTC_OUT_OF_MEMORY 21 | RTC_UNSUPPORTED_CPU 22 | RTC_CANCELLED 23 | 24 | # typedef struct __RTCDevice {}* RTCDevice; 25 | ctypedef void* RTCDevice 26 | 27 | RTCDevice rtcNewDevice(const char* cfg) 28 | void rtcDeleteDevice(RTCDevice device) 29 | 30 | RTCError rtcGetError() 31 | ctypedef void (*RTCErrorFunc)(const RTCError code, const char* _str) 32 | void rtcSetErrorFunction(RTCErrorFunc func) 33 | 34 | # Embree 2.14.0-0 35 | void rtcDeviceSetErrorFunction(RTCDevice device, RTCErrorFunc func) 36 | 37 | # Embree 2.15.1 38 | ctypedef void (*RTCErrorFunc2)(void* userPtr, const RTCError code, const char* str) 39 | void rtcDeviceSetErrorFunction2(RTCDevice device, RTCErrorFunc2 func, void* userPtr) 40 | 41 | ctypedef bint RTCMemoryMonitorFunc(const ssize_t _bytes, const bint post) 42 | void rtcSetMemoryMonitorFunction(RTCMemoryMonitorFunc func) 43 | 44 | cdef extern from "embree2/rtcore_ray.h": 45 | pass 46 | 47 | cdef struct Vertex: 48 | float x, y, z, r 49 | 50 | cdef struct Triangle: 51 | int v0, v1, v2 52 | 53 | cdef struct Vec3f: 54 | float x, y, z 55 | 56 | cdef void print_error(RTCError code) 57 | 58 | cdef class EmbreeDevice: 59 | cdef RTCDevice device 60 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | # We don't actually use the Travis Python, but this keeps it organized. 4 | - "2.7" 5 | - "3.7" 6 | install: 7 | # You may want to periodically update this, although the conda update 8 | # conda line below will keep everything up-to-date. We do this 9 | # conditionally because it saves us some downloading if the version is 10 | # the same. 11 | # First two lines install g++4.8 and enable compilation using it for c++11 capability: 12 | - sudo apt-get install -qq gcc-4.8 g++-4.8 13 | - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 90 14 | - if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then 15 | wget http://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh; 16 | else 17 | wget http://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh; 18 | fi 19 | - bash miniconda.sh -b -p $HOME/miniconda 20 | - export PATH="$HOME/miniconda/bin:$PATH" 21 | - hash -r 22 | - conda config --set always_yes yes --set changeps1 no 23 | - conda config --add channels conda-forge 24 | - if [[ "$TRAVIS_PYTHON_VERSION" == "3.7" ]]; then 25 | conda config --set channel_priority strict; 26 | fi 27 | - conda update -q conda 28 | - conda install conda-build jinja2 setuptools patchelf nose 29 | # Useful for debugging any issues with conda 30 | - conda info -a 31 | 32 | # install pyembree 33 | - ./.travis-install.sh 34 | 35 | script: 36 | - echo "Running tests with nosetests" 37 | - export PATH="$HOME/miniconda/bin:$PATH" 38 | - export LD_LIBRARY_PATH="$HOME/miniconda/lib:$LD_LIBRARY_PATH" 39 | - nosetests -w tests 40 | - echo "Running all examples" 41 | - cd examples && for i in `find . -type f -name "*.py"`; do echo $i; python $i --no-plots; done && cd .. 42 | -------------------------------------------------------------------------------- /examples/attenuate.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | 4 | import numpy as np 5 | PLOT = '--no-plots' not in sys.argv 6 | if PLOT: 7 | import matplotlib.pyplot as plt 8 | 9 | from pyembree import rtcore_scene as rtcs 10 | from pyembree.mesh_construction import TriangleMesh 11 | 12 | N = (4*256)**2 13 | R = 3 14 | sigmas = [0.5, 2.0, 4.0] 15 | 16 | def xplane(x): 17 | return [[[x, -1.0, -1.0], 18 | [x, -1.0, 1.0], 19 | [x, 1.0, -1.0]], 20 | [[x, -1.0, 1.0], 21 | [x, 1.0, -1.0], 22 | [x, 1.0, 1.0]]] 23 | 24 | 25 | triangles = xplane(0.0) + xplane(1.0) + xplane(2.0) + xplane(3.0) 26 | triangles = np.array(triangles, 'float32') 27 | 28 | scene = rtcs.EmbreeScene() 29 | mesh = TriangleMesh(scene, triangles) 30 | xgrid = np.linspace(0.0, 3.0, 100) 31 | tally = np.zeros(len(xgrid), dtype=int) 32 | 33 | origins = np.zeros((N, 3), dtype='float32') 34 | origins[:, 0] += 1e-8 35 | dirs = np.zeros((N, 3), dtype='float32') 36 | dirs[:, 0] = 1.0 37 | 38 | maxdist = np.empty(N, dtype='float32') 39 | exists = np.arange(N) 40 | 41 | def transport_region(r, origins, maxdist, exist): 42 | n = len(origins) 43 | u = np.random.random(n) 44 | dists = np.log(u) / (-sigmas[r]) 45 | dists = np.asarray(dists, dtype='float32') 46 | 47 | t1 = time.time() 48 | intersects = scene.run(origins, dirs[:n], dists) 49 | t2 = time.time() 50 | print("Ran region {0} in {1:.3f} s".format(r+1, t2-t1)) 51 | 52 | bi = intersects == -1 53 | maxdist[exist[bi]] = origins[bi, 0] + dists[bi] 54 | neworigins = np.asarray(triangles[intersects[~bi],0,:], dtype='float32') 55 | neworigins[:,1:] = 0.0 56 | exist = exist[~bi] 57 | return intersects, neworigins, exist 58 | 59 | for r in range(R): 60 | intersects, origins, exists = transport_region(r, origins, maxdist, exists) 61 | 62 | gi = intersects > -1 63 | maxdist[exists] = triangles[intersects[gi],0,0] # get x coord 64 | 65 | for i in range(len(xgrid)): 66 | tally[i] += (maxdist >= xgrid[i]).sum() 67 | 68 | if PLOT: 69 | plt.plot(xgrid, tally) 70 | plt.xlabel('x [cm]') 71 | plt.ylabel('flux') 72 | plt.savefig('attenuate.png') 73 | 74 | -------------------------------------------------------------------------------- /pyembree/rtcore_scene.pxd: -------------------------------------------------------------------------------- 1 | # rtcore_scene.pxd wrapper 2 | 3 | cimport cython 4 | cimport numpy as np 5 | cimport rtcore as rtc 6 | cimport rtcore_ray as rtcr 7 | 8 | cdef extern from "embree2/rtcore_scene.h": 9 | 10 | ctypedef struct RTCRay 11 | ctypedef struct RTCRay4 12 | ctypedef struct RTCRay8 13 | ctypedef struct RTCRay16 14 | 15 | cdef enum RTCSceneFlags: 16 | RTC_SCENE_STATIC 17 | RTC_SCENE_DYNAMIC 18 | RTC_SCENE_COMPACT 19 | RTC_SCENE_COHERENT 20 | RTC_SCENE_INCOHERENT 21 | RTC_SCENE_HIGH_QUALITY 22 | RTC_SCENE_ROBUST 23 | 24 | cdef enum RTCAlgorithmFlags: 25 | RTC_INTERSECT1 26 | RTC_INTERSECT4 27 | RTC_INTERSECT8 28 | RTC_INTERSECT16 29 | 30 | # ctypedef void* RTCDevice 31 | ctypedef void* RTCScene 32 | 33 | RTCScene rtcNewScene(RTCSceneFlags flags, RTCAlgorithmFlags aflags) 34 | 35 | RTCScene rtcDeviceNewScene(rtc.RTCDevice device, RTCSceneFlags flags, RTCAlgorithmFlags aflags) 36 | 37 | ctypedef bint (*RTCProgressMonitorFunc)(void* ptr, const double n) 38 | 39 | void rtcSetProgressMonitorFunction(RTCScene scene, RTCProgressMonitorFunc func, void* ptr) 40 | 41 | void rtcCommit(RTCScene scene) 42 | 43 | void rtcCommitThread(RTCScene scene, unsigned int threadID, unsigned int numThreads) 44 | 45 | void rtcIntersect(RTCScene scene, RTCRay& ray) 46 | 47 | void rtcIntersect4(const void* valid, RTCScene scene, RTCRay4& ray) 48 | 49 | void rtcIntersect8(const void* valid, RTCScene scene, RTCRay8& ray) 50 | 51 | void rtcIntersect16(const void* valid, RTCScene scene, RTCRay16& ray) 52 | 53 | void rtcOccluded(RTCScene scene, RTCRay& ray) 54 | 55 | void rtcOccluded4(const void* valid, RTCScene scene, RTCRay4& ray) 56 | 57 | void rtcOccluded8(const void* valid, RTCScene scene, RTCRay8& ray) 58 | 59 | void rtcOccluded16(const void* valid, RTCScene scene, RTCRay16& ray) 60 | 61 | void rtcDeleteScene(RTCScene scene) 62 | 63 | cdef class EmbreeScene: 64 | cdef RTCScene scene_i 65 | # Optional device used if not given, it should be as input of EmbreeScene 66 | cdef public int is_committed 67 | cdef rtc.EmbreeDevice device 68 | 69 | cdef enum rayQueryType: 70 | intersect, 71 | occluded, 72 | distance 73 | -------------------------------------------------------------------------------- /pyembree/rtcore_geometry_user.pxd: -------------------------------------------------------------------------------- 1 | # rtcore_geometry_user wrapper 2 | 3 | #from libc.stdint cimport ssize_t, size_t 4 | from .rtcore_ray cimport RTCRay, RTCRay4, RTCRay8, RTCRay16 5 | from .rtcore_geometry cimport RTCBounds 6 | from .rtcore_scene cimport RTCScene 7 | cimport cython 8 | cimport numpy as np 9 | 10 | cdef extern from "embree2/rtcore_geometry_user.h": 11 | ctypedef void (*RTCBoundsFunc)(void* ptr, size_t item, RTCBounds& bounds_o) 12 | ctypedef void (*RTCIntersectFunc)(void* ptr, RTCRay& ray, size_t item) 13 | ctypedef void (*RTCIntersectFunc4)(const void* valid, void* ptr, 14 | RTCRay4& ray, size_t item) 15 | ctypedef void (*RTCIntersectFunc8)(const void* valid, void* ptr, 16 | RTCRay8& ray, size_t item) 17 | ctypedef void (*RTCIntersectFunc16)(const void* valid, void* ptr, 18 | RTCRay16& ray, size_t item) 19 | ctypedef void (*RTCOccludedFunc)(void* ptr, RTCRay& ray, size_t item) 20 | ctypedef void (*RTCOccludedFunc4)(const void* valid, void* ptr, 21 | RTCRay4& ray, size_t item) 22 | ctypedef void (*RTCOccludedFunc8)(const void* valid, void* ptr, 23 | RTCRay8& ray, size_t item) 24 | ctypedef void (*RTCOccludedFunc16)(const void* valid, void* ptr, 25 | RTCRay16& ray, size_t item) 26 | unsigned rtcNewUserGeometry(RTCScene scene, size_t numGeometries) 27 | void rtcSetBoundsFunction(RTCScene scene, unsigned geomID, RTCBoundsFunc bounds) 28 | void rtcSetIntersectFunction(RTCScene scene, unsigned geomID, RTCIntersectFunc intersect) 29 | void rtcSetIntersectFunction4(RTCScene scene, unsigned geomID, RTCIntersectFunc4 intersect4) 30 | void rtcSetIntersectFunction8(RTCScene scene, unsigned geomID, RTCIntersectFunc8 intersect8) 31 | void rtcSetIntersectFunction16(RTCScene scene, unsigned geomID, RTCIntersectFunc16 intersect16) 32 | void rtcSetOccludedFunction(RTCScene scene, unsigned geomID, RTCOccludedFunc occluded) 33 | void rtcSetOccludedFunction4(RTCScene scene, unsigned geomID, RTCOccludedFunc4 occluded4) 34 | void rtcSetOccludedFunction8(RTCScene scene, unsigned geomID, RTCOccludedFunc8 occluded8) 35 | void rtcSetOccludedFunction16(RTCScene scene, unsigned geomID, RTCOccludedFunc16 occluded16) 36 | -------------------------------------------------------------------------------- /pyembree/rtcore_ray.pxd: -------------------------------------------------------------------------------- 1 | # rtcore_ray.pxd wrapper 2 | 3 | cimport cython 4 | cimport numpy as np 5 | 6 | cdef extern from "embree2/rtcore_ray.h": 7 | # RTCORE_ALIGN(16) 8 | # This is for a *single* ray 9 | cdef struct RTCRay: 10 | # Ray data 11 | float org[3] 12 | float align0 13 | 14 | float dir[3] 15 | float align1 16 | 17 | float tnear 18 | float tfar 19 | 20 | float time 21 | int mask 22 | 23 | # Hit data 24 | float Ng[3] 25 | float align2 26 | 27 | float u 28 | float v 29 | 30 | int geomID 31 | int primID 32 | int instID 33 | 34 | # This is for a packet of 4 rays 35 | cdef struct RTCRay4: 36 | # Ray data 37 | float orgx[4] 38 | float orgy[4] 39 | float orgz[4] 40 | float align0 41 | 42 | float dirx[4] 43 | float diry[4] 44 | float dirz[4] 45 | 46 | float tnear[4] 47 | float tfar[4] 48 | 49 | float time[4] 50 | int mask[4] 51 | 52 | # Hit data 53 | float Ngx[4] 54 | float Ngy[4] 55 | float Ngz[4] 56 | 57 | float u[4] 58 | float v[4] 59 | 60 | int geomID[4] 61 | int primID[4] 62 | int instID[4] 63 | 64 | # This is for a packet of 8 rays 65 | cdef struct RTCRay8: 66 | # Ray data 67 | float orgx[8] 68 | float orgy[8] 69 | float orgz[8] 70 | float align0 71 | 72 | float dirx[8] 73 | float diry[8] 74 | float dirz[8] 75 | 76 | float tnear[8] 77 | float tfar[8] 78 | 79 | float time[8] 80 | int mask[8] 81 | 82 | # Hit data 83 | float Ngx[8] 84 | float Ngy[8] 85 | float Ngz[8] 86 | 87 | float u[8] 88 | float v[8] 89 | 90 | int geomID[8] 91 | int primID[8] 92 | int instID[8] 93 | 94 | # This is for a packet of 16 rays 95 | cdef struct RTCRay16: 96 | # Ray data 97 | float orgx[16] 98 | float orgy[16] 99 | float orgz[16] 100 | float align0 101 | 102 | float dirx[16] 103 | float diry[16] 104 | float dirz[16] 105 | 106 | float tnear[16] 107 | float tfar[16] 108 | 109 | float time[16] 110 | int mask[16] 111 | 112 | # Hit data 113 | float Ngx[16] 114 | float Ngy[16] 115 | float Ngz[16] 116 | 117 | float u[16] 118 | float v[16] 119 | 120 | int geomID[16] 121 | int primID[16] 122 | int instID[16] 123 | -------------------------------------------------------------------------------- /pyembree/rtcore_geometry.pxd: -------------------------------------------------------------------------------- 1 | # rtcore_geometry wrapper 2 | 3 | from .rtcore_ray cimport RTCRay, RTCRay4, RTCRay8, RTCRay16 4 | from .rtcore_scene cimport RTCScene 5 | cimport cython 6 | cimport numpy as np 7 | 8 | cdef extern from "embree2/rtcore_geometry.h": 9 | cdef unsigned int RTC_INVALID_GEOMETRY_ID 10 | 11 | cdef enum RTCBufferType: 12 | RTC_INDEX_BUFFER 13 | RTC_VERTEX_BUFFER 14 | RTC_VERTEX_BUFFER0 15 | RTC_VERTEX_BUFFER1 16 | 17 | RTC_FACE_BUFFER 18 | RTC_LEVEL_BUFFER 19 | 20 | RTC_EDGE_CREASE_INDEX_BUFFER 21 | RTC_EDGE_CREASE_WEIGHT_BUFFER 22 | 23 | RTC_VERTEX_CREASE_INDEX_BUFFER 24 | RTC_VERTEX_CREASE_WEIGHT_BUFFER 25 | 26 | RTC_HOLE_BUFFER 27 | 28 | cdef enum RTCMatrixType: 29 | RTC_MATRIX_ROW_MAJOR 30 | RTC_MATRIX_COLUMN_MAJOR 31 | RTC_MATRIX_COLUMN_MAJOR_ALIGNED16 32 | 33 | cdef enum RTCGeometryFlags: 34 | RTC_GEOMETRY_STATIC 35 | RTC_GEOMETRY_DEFORMABLE 36 | RTC_GEOMETRY_DYNAMIC 37 | 38 | cdef struct RTCBounds: 39 | float lower_x, lower_y, lower_z, align0 40 | float upper_x, upper_y, upper_z, align1 41 | 42 | ctypedef void (*RTCFilterFunc)(void* ptr, RTCRay& ray) 43 | ctypedef void (*RTCFilterFunc4)(void* ptr, RTCRay4& ray) 44 | ctypedef void (*RTCFilterFunc8)(void* ptr, RTCRay8& ray) 45 | ctypedef void (*RTCFilterFunc16)(void* ptr, RTCRay16& ray) 46 | 47 | ctypedef void (*RTCDisplacementFunc)(void* ptr, unsigned geomID, unsigned primID, 48 | const float* u, const float* v, 49 | const float* nx, const float* ny, const float* nz, 50 | float* px, float* py, float* pz, size_t N) 51 | 52 | unsigned rtcNewInstance(RTCScene target, RTCScene source) 53 | void rtcSetTransform(RTCScene scene, unsigned geomID, 54 | RTCMatrixType layout, const float *xfm) 55 | unsigned rtcNewTriangleMesh(RTCScene scene, RTCGeometryFlags flags, 56 | size_t numTriangles, size_t numVertices, 57 | size_t numTimeSteps) 58 | 59 | unsigned rtcNewSubdivisionMesh (RTCScene scene, RTCGeometryFlags flags, 60 | size_t numFaces, size_t numEdges, 61 | size_t numVertices, size_t numEdgeCreases, 62 | size_t numVertexCreases, size_t numHoles, 63 | size_t numTimeSteps) 64 | unsigned rtcNewHairGeometry (RTCScene scene, RTCGeometryFlags flags, 65 | size_t numCurves, size_t numVertices, 66 | size_t numTimeSteps) 67 | void rtcSetMask(RTCScene scene, unsigned geomID, int mask) 68 | void *rtcMapBuffer(RTCScene scene, unsigned geomID, RTCBufferType type) 69 | void rtcUnmapBuffer(RTCScene scene, unsigned geomID, RTCBufferType type) 70 | void rtcSetBuffer(RTCScene scene, unsigned geomID, RTCBufferType type, 71 | void *ptr, size_t offset, size_t stride) 72 | void rtcEnable(RTCScene scene, unsigned geomID) 73 | void rtcUpdate(RTCScene scene, unsigned geomID) 74 | void rtcUpdateBuffer(RTCScene scene, unsigned geomID, RTCBufferType type) 75 | void rtcDisable(RTCScene scene, unsigned geomID) 76 | void rtcSetDisplacementFunction (RTCScene scene, unsigned geomID, RTCDisplacementFunc func, RTCBounds* bounds) 77 | void rtcSetIntersectionFilterFunction (RTCScene scene, unsigned geomID, RTCFilterFunc func) 78 | void rtcSetIntersectionFilterFunction4 (RTCScene scene, unsigned geomID, RTCFilterFunc4 func) 79 | void rtcSetIntersectionFilterFunction8 (RTCScene scene, unsigned geomID, RTCFilterFunc8 func) 80 | void rtcSetIntersectionFilterFunction16 (RTCScene scene, unsigned geomID, RTCFilterFunc16 func) 81 | void rtcSetOcclusionFilterFunction (RTCScene scene, unsigned geomID, RTCFilterFunc func) 82 | void rtcSetOcclusionFilterFunction4 (RTCScene scene, unsigned geomID, RTCFilterFunc4 func) 83 | void rtcSetOcclusionFilterFunction8 (RTCScene scene, unsigned geomID, RTCFilterFunc8 func) 84 | void rtcSetOcclusionFilterFunction16 (RTCScene scene, unsigned geomID, RTCFilterFunc16 func) 85 | void rtcSetUserData (RTCScene scene, unsigned geomID, void* ptr) 86 | void* rtcGetUserData (RTCScene scene, unsigned geomID) 87 | void rtcDeleteGeometry (RTCScene scene, unsigned geomID) 88 | 89 | -------------------------------------------------------------------------------- /pyembree/rtcore_scene.pyx: -------------------------------------------------------------------------------- 1 | cimport cython 2 | cimport numpy as np 3 | import numpy as np 4 | import logging 5 | import numbers 6 | cimport rtcore as rtc 7 | cimport rtcore_ray as rtcr 8 | cimport rtcore_geometry as rtcg 9 | 10 | 11 | log = logging.getLogger('pyembree') 12 | 13 | cdef void error_printer(const rtc.RTCError code, const char *_str): 14 | """ 15 | error_printer function depends on embree version 16 | Embree 2.14.1 17 | -> cdef void error_printer(const rtc.RTCError code, const char *_str): 18 | Embree 2.17.1 19 | -> cdef void error_printer(void* userPtr, const rtc.RTCError code, const char *_str): 20 | """ 21 | log.error("ERROR CAUGHT IN EMBREE") 22 | rtc.print_error(code) 23 | log.error("ERROR MESSAGE: %s" % _str) 24 | 25 | 26 | cdef class EmbreeScene: 27 | def __init__(self, rtc.EmbreeDevice device=None): 28 | if device is None: 29 | # We store the embree device inside EmbreeScene to avoid premature deletion 30 | self.device = rtc.EmbreeDevice() 31 | device = self.device 32 | rtc.rtcDeviceSetErrorFunction(device.device, error_printer) 33 | self.scene_i = rtcDeviceNewScene(device.device, RTC_SCENE_STATIC, RTC_INTERSECT1) 34 | self.is_committed = 0 35 | 36 | def run(self, np.ndarray[np.float32_t, ndim=2] vec_origins, 37 | np.ndarray[np.float32_t, ndim=2] vec_directions, 38 | dists=None,query='INTERSECT',output=None): 39 | 40 | if self.is_committed == 0: 41 | rtcCommit(self.scene_i) 42 | self.is_committed = 1 43 | 44 | cdef int nv = vec_origins.shape[0] 45 | cdef int vo_i, vd_i, vd_step 46 | cdef np.ndarray[np.int32_t, ndim=1] intersect_ids 47 | cdef np.ndarray[np.float32_t, ndim=1] tfars 48 | cdef rayQueryType query_type 49 | 50 | if query == 'INTERSECT': 51 | query_type = intersect 52 | elif query == 'OCCLUDED': 53 | query_type = occluded 54 | elif query == 'DISTANCE': 55 | query_type = distance 56 | 57 | else: 58 | raise ValueError("Embree ray query type %s not recognized." 59 | "\nAccepted types are (INTERSECT,OCCLUDED,DISTANCE)" % (query)) 60 | 61 | if dists is None: 62 | tfars = np.empty(nv, 'float32') 63 | tfars.fill(1e37) 64 | elif isinstance(dists, numbers.Number): 65 | tfars = np.empty(nv, 'float32') 66 | tfars.fill(dists) 67 | else: 68 | tfars = dists 69 | 70 | if output: 71 | u = np.empty(nv, dtype="float32") 72 | v = np.empty(nv, dtype="float32") 73 | Ng = np.empty((nv, 3), dtype="float32") 74 | primID = np.empty(nv, dtype="int32") 75 | geomID = np.empty(nv, dtype="int32") 76 | else: 77 | intersect_ids = np.empty(nv, dtype="int32") 78 | 79 | cdef rtcr.RTCRay ray 80 | vd_i = 0 81 | vd_step = 1 82 | # If vec_directions is 1 long, we won't be updating it. 83 | if vec_directions.shape[0] == 1: vd_step = 0 84 | 85 | for i in range(nv): 86 | for j in range(3): 87 | ray.org[j] = vec_origins[i, j] 88 | ray.dir[j] = vec_directions[vd_i, j] 89 | ray.tnear = 0.0 90 | ray.tfar = tfars[i] 91 | ray.geomID = rtcg.RTC_INVALID_GEOMETRY_ID 92 | ray.primID = rtcg.RTC_INVALID_GEOMETRY_ID 93 | ray.instID = rtcg.RTC_INVALID_GEOMETRY_ID 94 | ray.mask = -1 95 | ray.time = 0 96 | vd_i += vd_step 97 | 98 | if query_type == intersect or query_type == distance: 99 | rtcIntersect(self.scene_i, ray) 100 | if not output: 101 | if query_type == intersect: 102 | intersect_ids[i] = ray.primID 103 | else: 104 | tfars[i] = ray.tfar 105 | else: 106 | primID[i] = ray.primID 107 | geomID[i] = ray.geomID 108 | u[i] = ray.u 109 | v[i] = ray.v 110 | tfars[i] = ray.tfar 111 | for j in range(3): 112 | Ng[i, j] = ray.Ng[j] 113 | else: 114 | rtcOccluded(self.scene_i, ray) 115 | intersect_ids[i] = ray.geomID 116 | 117 | if output: 118 | return {'u':u, 'v':v, 'Ng': Ng, 'tfar': tfars, 'primID': primID, 'geomID': geomID} 119 | else: 120 | if query_type == distance: 121 | return tfars 122 | else: 123 | return intersect_ids 124 | 125 | def __dealloc__(self): 126 | rtcDeleteScene(self.scene_i) 127 | -------------------------------------------------------------------------------- /pyembree/triangles.pyx: -------------------------------------------------------------------------------- 1 | cimport numpy as np 2 | cimport rtcore as rtc 3 | cimport rtcore_ray as rtcr 4 | cimport rtcore_scene as rtcs 5 | cimport rtcore_geometry as rtcg 6 | cimport rtcore_geometry_user as rtcgu 7 | from rtcore cimport Vertex, Triangle, Vec3f 8 | from libc.stdlib cimport malloc, free 9 | 10 | ctypedef Vec3f (*renderPixelFunc)(float x, float y, 11 | const Vec3f &vx, const Vec3f &vy, const Vec3f &vz, 12 | const Vec3f &p) 13 | 14 | def run_triangles(): 15 | pass 16 | 17 | cdef unsigned int addCube(rtcs.RTCScene scene_i): 18 | cdef unsigned int mesh = rtcg.rtcNewTriangleMesh(scene_i, 19 | rtcg.RTC_GEOMETRY_STATIC, 12, 8, 1) 20 | cdef Vertex* vertices = rtcg.rtcMapBuffer(scene_i, mesh, rtcg.RTC_VERTEX_BUFFER) 21 | vertices[0].x = -1 22 | vertices[0].y = -1 23 | vertices[0].z = -1 24 | 25 | vertices[1].x = -1 26 | vertices[1].y = -1 27 | vertices[1].z = +1 28 | 29 | vertices[2].x = -1 30 | vertices[2].y = +1 31 | vertices[2].z = -1 32 | 33 | vertices[3].x = -1 34 | vertices[3].y = +1 35 | vertices[3].z = +1 36 | 37 | vertices[4].x = +1 38 | vertices[4].y = -1 39 | vertices[4].z = -1 40 | 41 | vertices[5].x = +1 42 | vertices[5].y = -1 43 | vertices[5].z = +1 44 | 45 | vertices[6].x = +1 46 | vertices[6].y = +1 47 | vertices[6].z = -1 48 | 49 | vertices[7].x = +1 50 | vertices[7].y = +1 51 | vertices[7].z = +1 52 | 53 | rtcg.rtcUnmapBuffer(scene_i, mesh, rtcg.RTC_VERTEX_BUFFER) 54 | 55 | cdef Vec3f *colors = malloc(12*sizeof(Vec3f)) 56 | 57 | cdef int tri = 0 58 | cdef Triangle* triangles = rtcg.rtcMapBuffer(scene_i, mesh, 59 | rtcg.RTC_INDEX_BUFFER) 60 | 61 | # left side 62 | colors[tri].x = 1.0 63 | colors[tri].y = 0.0 64 | colors[tri].z = 0.0 65 | triangles[tri].v0 = 0 66 | triangles[tri].v1 = 2 67 | triangles[tri].v2 = 1 68 | tri += 1 69 | colors[tri].x = 1.0 70 | colors[tri].y = 0.0 71 | colors[tri].z = 0.0 72 | triangles[tri].v0 = 1 73 | triangles[tri].v1 = 2 74 | triangles[tri].v2 = 3 75 | tri += 1 76 | 77 | # right side 78 | colors[tri].x = 0.0 79 | colors[tri].y = 1.0 80 | colors[tri].z = 0.0 81 | triangles[tri].v0 = 4 82 | triangles[tri].v1 = 5 83 | triangles[tri].v2 = 6 84 | tri += 1 85 | colors[tri].x = 0.0 86 | colors[tri].y = 1.0 87 | colors[tri].z = 0.0 88 | triangles[tri].v0 = 5 89 | triangles[tri].v1 = 7 90 | triangles[tri].v2 = 6 91 | tri += 1 92 | 93 | # bottom side 94 | colors[tri].x = 0.5 95 | colors[tri].y = 0.5 96 | colors[tri].z = 0.5 97 | triangles[tri].v0 = 0 98 | triangles[tri].v1 = 1 99 | triangles[tri].v2 = 4 100 | tri += 1 101 | colors[tri].x = 0.5 102 | colors[tri].y = 0.5 103 | colors[tri].z = 0.5 104 | triangles[tri].v0 = 1 105 | triangles[tri].v1 = 5 106 | triangles[tri].v2 = 4 107 | tri += 1 108 | 109 | # top side 110 | colors[tri].x = 1.0 111 | colors[tri].y = 1.0 112 | colors[tri].z = 1.0 113 | triangles[tri].v0 = 2 114 | triangles[tri].v1 = 6 115 | triangles[tri].v2 = 3 116 | tri += 1 117 | colors[tri].x = 1.0 118 | colors[tri].y = 1.0 119 | colors[tri].z = 1.0 120 | triangles[tri].v0 = 3 121 | triangles[tri].v1 = 6 122 | triangles[tri].v2 = 7 123 | tri += 1 124 | 125 | # front side 126 | colors[tri].x = 0.0 127 | colors[tri].y = 0.0 128 | colors[tri].z = 1.0 129 | triangles[tri].v0 = 0 130 | triangles[tri].v1 = 4 131 | triangles[tri].v2 = 2 132 | tri += 1 133 | colors[tri].x = 0.0 134 | colors[tri].y = 0.0 135 | colors[tri].z = 1.0 136 | triangles[tri].v0 = 2 137 | triangles[tri].v1 = 4 138 | triangles[tri].v2 = 6 139 | tri += 1 140 | 141 | # back side 142 | colors[tri].x = 1.0 143 | colors[tri].y = 1.0 144 | colors[tri].z = 0.0 145 | triangles[tri].v0 = 1 146 | triangles[tri].v1 = 3 147 | triangles[tri].v2 = 5 148 | tri += 1 149 | colors[tri].x = 1.0 150 | colors[tri].y = 1.0 151 | colors[tri].z = 0.0 152 | triangles[tri].v0 = 3 153 | triangles[tri].v1 = 7 154 | triangles[tri].v2 = 5 155 | tri += 1 156 | 157 | rtcg.rtcUnmapBuffer(scene_i, mesh, rtcg.RTC_INDEX_BUFFER) 158 | 159 | return mesh 160 | 161 | cdef unsigned int addGroundPlane (rtcs.RTCScene scene_i): 162 | cdef unsigned int mesh = rtcg.rtcNewTriangleMesh (scene_i, 163 | rtcg.RTC_GEOMETRY_STATIC, 2, 4, 1) 164 | 165 | cdef Vertex* vertices = rtcg.rtcMapBuffer(scene_i, mesh, rtcg.RTC_VERTEX_BUFFER) 166 | vertices[0].x = -10 167 | vertices[0].y = -2 168 | vertices[0].z = -10 169 | 170 | vertices[1].x = -10 171 | vertices[1].y = -2 172 | vertices[1].z = +10 173 | 174 | vertices[2].x = +10 175 | vertices[2].y = -2 176 | vertices[2].z = -10 177 | 178 | vertices[3].x = +10 179 | vertices[3].y = -2 180 | vertices[3].z = +10 181 | rtcg.rtcUnmapBuffer(scene_i, mesh, rtcg.RTC_VERTEX_BUFFER) 182 | 183 | cdef Triangle* triangles = rtcg.rtcMapBuffer(scene_i, mesh, rtcg.RTC_INDEX_BUFFER) 184 | triangles[0].v0 = 0 185 | triangles[0].v1 = 2 186 | triangles[0].v2 = 1 187 | triangles[1].v0 = 1 188 | triangles[1].v1 = 2 189 | triangles[1].v2 = 3 190 | rtcg.rtcUnmapBuffer(scene_i, mesh, rtcg.RTC_INDEX_BUFFER) 191 | 192 | return mesh 193 | -------------------------------------------------------------------------------- /tests/test_intersection.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | import numpy as np 3 | from pyembree import rtcore as rtc 4 | from pyembree import rtcore_scene as rtcs 5 | from pyembree.mesh_construction import TriangleMesh 6 | from pyembree.mesh_construction import ElementMesh 7 | 8 | 9 | def xplane(x): 10 | return [[[x, -1.0, -1.0], 11 | [x, +1.0, -1.0], 12 | [x, -1.0, +1.0]], 13 | [[x, +1.0, -1.0], 14 | [x, +1.0, +1.0], 15 | [x, -1.0, +1.0]]] 16 | 17 | 18 | def xplane_only_points(x): 19 | # Indices are [[0, 1, 2], [1, 3, 2]] 20 | return [[x, -1.0, -1.0], 21 | [x, +1.0, -1.0], 22 | [x, -1.0, +1.0], 23 | [x, +1.0, +1.0]] 24 | 25 | 26 | def define_rays_origins_and_directions(): 27 | N = 4 28 | origins = np.zeros((N, 3), dtype='float32') 29 | origins[:,0] = 0.1 30 | origins[0,1] = -0.2 31 | origins[1,1] = +0.2 32 | origins[2,1] = +0.3 33 | origins[3,1] = -8.2 34 | 35 | dirs = np.zeros((N, 3), dtype='float32') 36 | dirs[:, 0] = 1.0 37 | return origins, dirs 38 | 39 | 40 | class TestPyEmbree(TestCase): 41 | def test_pyembree_should_be_able_to_display_embree_version(self): 42 | embreeDevice = rtc.EmbreeDevice() 43 | print(embreeDevice) 44 | 45 | def test_pyembree_should_be_able_to_create_a_scene(self): 46 | embreeDevice = rtc.EmbreeDevice() 47 | scene = rtcs.EmbreeScene(embreeDevice) 48 | 49 | def test_pyembree_should_be_able_to_create_several_scenes(self): 50 | embreeDevice = rtc.EmbreeDevice() 51 | scene1 = rtcs.EmbreeScene(embreeDevice) 52 | scene2 = rtcs.EmbreeScene(embreeDevice) 53 | 54 | def test_pyembree_should_be_able_to_create_a_device_if_not_provided(self): 55 | scene = rtcs.EmbreeScene() 56 | 57 | class TestIntersectionTriangles(TestCase): 58 | 59 | def setUp(self): 60 | """Initialisation""" 61 | triangles = xplane(7.0) 62 | triangles = np.array(triangles, 'float32') 63 | 64 | self.embreeDevice = rtc.EmbreeDevice() 65 | self.scene = rtcs.EmbreeScene(self.embreeDevice) 66 | mesh = TriangleMesh(self.scene, triangles) 67 | 68 | origins, dirs = define_rays_origins_and_directions() 69 | self.origins = origins 70 | self.dirs = dirs 71 | 72 | def test_intersect_simple(self): 73 | res = self.scene.run(self.origins, self.dirs) 74 | self.assertTrue([0, 1, 1, -1], res) 75 | 76 | def test_intersect_distance(self): 77 | res = self.scene.run(self.origins, self.dirs,query='DISTANCE') 78 | self.assertTrue(np.allclose([6.9, 6.9, 6.9,1e37], res)) 79 | 80 | 81 | def test_intersect(self): 82 | res = self.scene.run(self.origins, self.dirs, output=1, dists = 100) 83 | 84 | self.assertTrue([0, 0, 0, -1], res['geomID']) 85 | ray_inter = res['geomID'] >= 0 86 | primID = res['primID'][ray_inter] 87 | u = res['u'][ray_inter] 88 | v = res['v'][ray_inter] 89 | tfar = res['tfar'] 90 | self.assertTrue([ 0, 1, 1], primID) 91 | self.assertTrue(np.allclose([6.9, 6.9, 6.9,100], tfar)) 92 | self.assertTrue(np.allclose([0.4, 0.1, 0.15], u)) 93 | self.assertTrue(np.allclose([0.5, 0.4, 0.35], v)) 94 | 95 | 96 | class TestIntersectionTrianglesFromIndices(TestCase): 97 | 98 | def setUp(self): 99 | """Initialisation""" 100 | points = xplane_only_points(7.0) 101 | points = np.array(points, 'float32') 102 | indices = np.array([[0, 1, 2], [1, 3, 2]], 'uint32') 103 | 104 | self.embreeDevice = rtc.EmbreeDevice() 105 | self.scene = rtcs.EmbreeScene(self.embreeDevice) 106 | mesh = TriangleMesh(self.scene, points, indices) 107 | 108 | origins, dirs = define_rays_origins_and_directions() 109 | self.origins = origins 110 | self.dirs = dirs 111 | 112 | def test_intersect_simple(self): 113 | res = self.scene.run(self.origins, self.dirs) 114 | self.assertTrue([0, 1, 1, -1], res) 115 | 116 | def test_intersect(self): 117 | res = self.scene.run(self.origins, self.dirs, output=1) 118 | 119 | self.assertTrue([0, 0, 0, -1], res['geomID']) 120 | 121 | ray_inter = res['geomID'] >= 0 122 | primID = res['primID'][ray_inter] 123 | u = res['u'][ray_inter] 124 | v = res['v'][ray_inter] 125 | tfar = res['tfar'][ray_inter] 126 | self.assertTrue([ 0, 1, 1], primID) 127 | self.assertTrue(np.allclose([6.9, 6.9, 6.9], tfar)) 128 | self.assertTrue(np.allclose([0.4, 0.1, 0.15], u)) 129 | self.assertTrue(np.allclose([0.5, 0.4, 0.35], v)) 130 | 131 | 132 | class TestIntersectionTetrahedron(TestCase): 133 | 134 | def setUp(self): 135 | """Initialisation""" 136 | vertices = [(0.0, 0.0, 0.0), (1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)] 137 | vertices = np.array(vertices, 'float32') 138 | indices = np.array([[0, 1, 2, 3]], 'uint32') 139 | self.embreeDevice = rtc.EmbreeDevice() 140 | self.scene = rtcs.EmbreeScene(self.embreeDevice) 141 | mesh = ElementMesh(self.scene, vertices, indices) 142 | 143 | N = 2 144 | self.origins = np.zeros((N, 3), dtype='float32') 145 | self.origins[0, :] = (-0.1, +0.1, +0.1) 146 | self.origins[1, :] = (-0.1, +0.2, +0.2) 147 | self.dirs = np.zeros((N, 3), dtype='float32') 148 | self.dirs[:, 0] = 1.0 149 | 150 | def test_intersect_simple(self): 151 | res = self.scene.run(self.origins, self.dirs) 152 | self.assertTrue([1, 1], res) 153 | 154 | def test_intersect(self): 155 | res = self.scene.run(self.origins, self.dirs, output=1) 156 | 157 | self.assertTrue([0, 0], res['geomID']) 158 | 159 | ray_inter = res['geomID'] >= 0 160 | primID = res['primID'][ray_inter] 161 | u = res['u'][ray_inter] 162 | v = res['v'][ray_inter] 163 | tfar = res['tfar'][ray_inter] 164 | self.assertTrue([0, 1], primID) 165 | self.assertTrue(np.allclose([0.1, 0.1], tfar)) 166 | self.assertTrue(np.allclose([0.1, 0.2], u)) 167 | self.assertTrue(np.allclose([0.1, 0.2], v)) 168 | 169 | 170 | class TestIntersectionHexahedron(TestCase): 171 | 172 | def setUp(self): 173 | """Initialisation""" 174 | vertices = [(1.0, 0.0, 0.0), (1.0, 1.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 0.0), 175 | (1.0, 0.0, 1.0), (1.0, 1.0, 1.0), (0.0, 1.0, 1.0), (0.0, 0.0, 1.0)] 176 | vertices = np.array(vertices, 'float32') 177 | indices = np.array([[0, 1, 2, 3, 4, 5, 6, 7]], 'uint32') 178 | self.embreeDevice = rtc.EmbreeDevice() 179 | self.scene = rtcs.EmbreeScene(self.embreeDevice) 180 | mesh = ElementMesh(self.scene, vertices, indices) 181 | 182 | N = 2 183 | self.origins = np.zeros((N, 3), dtype='float32') 184 | self.origins[0, :] = (-0.1, +0.9, +0.1) 185 | self.origins[1, :] = (-0.1, +0.8, +0.2) 186 | self.dirs = np.zeros((N, 3), dtype='float32') 187 | self.dirs[:, 0] = 1.0 188 | 189 | def test_intersect_simple(self): 190 | res = self.scene.run(self.origins, self.dirs) 191 | self.assertTrue([1, 1], res) 192 | 193 | def test_intersect(self): 194 | res = self.scene.run(self.origins, self.dirs, output=1) 195 | 196 | self.assertTrue([0, 0], res['geomID']) 197 | 198 | ray_inter = res['geomID'] >= 0 199 | primID = res['primID'][ray_inter] 200 | u = res['u'][ray_inter] 201 | v = res['v'][ray_inter] 202 | tfar = res['tfar'][ray_inter] 203 | self.assertTrue([0, 1], primID) 204 | self.assertTrue(np.allclose([0.1, 0.1], tfar)) 205 | self.assertTrue(np.allclose([0.1, 0.2], u)) 206 | self.assertTrue(np.allclose([0.8, 0.6], v)) 207 | 208 | if __name__ == '__main__': 209 | from unittest import main 210 | main() -------------------------------------------------------------------------------- /pyembree/mesh_construction.pyx: -------------------------------------------------------------------------------- 1 | cimport numpy as np 2 | cimport rtcore as rtc 3 | cimport rtcore_ray as rtcr 4 | cimport rtcore_scene as rtcs 5 | cimport rtcore_geometry as rtcg 6 | cimport rtcore_geometry_user as rtcgu 7 | from rtcore cimport Vertex, Triangle 8 | 9 | 10 | cdef extern from "mesh_construction.h": 11 | int triangulate_hex[12][3] 12 | int triangulate_tetra[4][3] 13 | 14 | cdef class TriangleMesh: 15 | r''' 16 | 17 | This class constructs a polygon mesh with triangular elements and 18 | adds it to the scene. 19 | 20 | Parameters 21 | ---------- 22 | 23 | scene : EmbreeScene 24 | This is the scene to which the constructed polygons will be 25 | added. 26 | vertices : a np.ndarray of floats. 27 | This specifies the x, y, and z coordinates of the vertices in 28 | the polygon mesh. This should either have the shape 29 | (num_triangles, 3, 3), or the shape (num_vertices, 3), depending 30 | on the value of the `indices` parameter. 31 | indices : either None, or a np.ndarray of ints 32 | If None, then vertices must have the shape (num_triangles, 3, 3). 33 | In this case, `vertices` specifices the coordinates of each 34 | vertex of each triangle in the mesh, with vertices being 35 | duplicated if they are shared between triangles. For example, 36 | if indices is None, then vertices[2][1][0] should give you 37 | the x-coordinate of the 2nd vertex of the 3rd triangle. 38 | If indices is a np.ndarray, then it must have the shape 39 | (num_triangles, 3), and `vertices` must have the shape 40 | (num_vertices, 3). In this case, indices[2][1] tells you 41 | the index of the 2nd vertex of the 3rd triangle in `indices`, 42 | while vertices[5][2] tells you the z-coordinate of the 6th 43 | vertex in the mesh. Note that the indexing is assumed to be 44 | zero-based. In this setup, vertices can be shared between 45 | triangles, and the number of vertices can be less than 3 times 46 | the number of triangles. 47 | 48 | ''' 49 | 50 | cdef Vertex* vertices 51 | cdef Triangle* indices 52 | cdef unsigned int mesh 53 | 54 | def __init__(self, rtcs.EmbreeScene scene, 55 | np.ndarray vertices, 56 | np.ndarray indices = None): 57 | 58 | if indices is None: 59 | self._build_from_flat(scene, vertices) 60 | else: 61 | self._build_from_indices(scene, vertices, indices) 62 | 63 | cdef void _build_from_flat(self, rtcs.EmbreeScene scene, 64 | np.ndarray tri_vertices): 65 | cdef int i, j 66 | cdef int nt = tri_vertices.shape[0] 67 | # In this scheme, we don't share any vertices. This leads to cracks, 68 | # but also means we have exactly three times as many vertices as 69 | # triangles. 70 | cdef unsigned int mesh = rtcg.rtcNewTriangleMesh(scene.scene_i, 71 | rtcg.RTC_GEOMETRY_STATIC, nt, nt*3, 1) 72 | 73 | cdef Vertex* vertices = rtcg.rtcMapBuffer(scene.scene_i, mesh, 74 | rtcg.RTC_VERTEX_BUFFER) 75 | 76 | for i in range(nt): 77 | for j in range(3): 78 | vertices[i*3 + j].x = tri_vertices[i,j,0] 79 | vertices[i*3 + j].y = tri_vertices[i,j,1] 80 | vertices[i*3 + j].z = tri_vertices[i,j,2] 81 | rtcg.rtcUnmapBuffer(scene.scene_i, mesh, rtcg.RTC_VERTEX_BUFFER) 82 | 83 | cdef Triangle* triangles = rtcg.rtcMapBuffer(scene.scene_i, 84 | mesh, rtcg.RTC_INDEX_BUFFER) 85 | for i in range(nt): 86 | triangles[i].v0 = i*3 + 0 87 | triangles[i].v1 = i*3 + 1 88 | triangles[i].v2 = i*3 + 2 89 | 90 | rtcg.rtcUnmapBuffer(scene.scene_i, mesh, rtcg.RTC_INDEX_BUFFER) 91 | self.vertices = vertices 92 | self.indices = triangles 93 | self.mesh = mesh 94 | 95 | cdef void _build_from_indices(self, rtcs.EmbreeScene scene, 96 | np.ndarray tri_vertices, 97 | np.ndarray tri_indices): 98 | cdef int i 99 | cdef int nv = tri_vertices.shape[0] 100 | cdef int nt = tri_indices.shape[0] 101 | 102 | cdef unsigned int mesh = rtcg.rtcNewTriangleMesh(scene.scene_i, 103 | rtcg.RTC_GEOMETRY_STATIC, nt, nv, 1) 104 | 105 | # set up vertex and triangle arrays. In this case, we just read 106 | # them directly from the inputs 107 | cdef Vertex* vertices = rtcg.rtcMapBuffer(scene.scene_i, mesh, 108 | rtcg.RTC_VERTEX_BUFFER) 109 | 110 | for i in range(nv): 111 | vertices[i].x = tri_vertices[i, 0] 112 | vertices[i].y = tri_vertices[i, 1] 113 | vertices[i].z = tri_vertices[i, 2] 114 | 115 | rtcg.rtcUnmapBuffer(scene.scene_i, mesh, rtcg.RTC_VERTEX_BUFFER) 116 | 117 | cdef Triangle* triangles = rtcg.rtcMapBuffer(scene.scene_i, 118 | mesh, rtcg.RTC_INDEX_BUFFER) 119 | 120 | for i in range(nt): 121 | triangles[i].v0 = tri_indices[i][0] 122 | triangles[i].v1 = tri_indices[i][1] 123 | triangles[i].v2 = tri_indices[i][2] 124 | 125 | rtcg.rtcUnmapBuffer(scene.scene_i, mesh, rtcg.RTC_INDEX_BUFFER) 126 | 127 | self.vertices = vertices 128 | self.indices = triangles 129 | self.mesh = mesh 130 | 131 | cdef class ElementMesh(TriangleMesh): 132 | r''' 133 | 134 | Currently, we handle non-triangular mesh types by converting them 135 | to triangular meshes. This class performs this transformation. 136 | Currently, this is implemented for hexahedral and tetrahedral 137 | meshes. 138 | 139 | Parameters 140 | ---------- 141 | 142 | scene : EmbreeScene 143 | This is the scene to which the constructed polygons will be 144 | added. 145 | vertices : a np.ndarray of floats. 146 | This specifies the x, y, and z coordinates of the vertices in 147 | the polygon mesh. This should either have the shape 148 | (num_vertices, 3). For example, vertices[2][1] should give the 149 | y-coordinate of the 3rd vertex in the mesh. 150 | indices : a np.ndarray of ints 151 | This should either have the shape (num_elements, 4) or 152 | (num_elements, 8) for tetrahedral and hexahedral meshes, 153 | respectively. For tetrahedral meshes, each element will 154 | be represented by four triangles in the scene. For hex meshes, 155 | each element will be represented by 12 triangles, 2 for each 156 | face. For hex meshes, we assume that the node ordering is as 157 | defined here: 158 | http://homepages.cae.wisc.edu/~tautges/papers/cnmev3.pdf 159 | 160 | ''' 161 | 162 | def __init__(self, rtcs.EmbreeScene scene, 163 | np.ndarray vertices, 164 | np.ndarray indices): 165 | # We need now to figure out if we've been handed quads or tetrahedra. 166 | # If it's quads, we can build the mesh slightly differently. 167 | # http://stackoverflow.com/questions/23723993/converting-quadriladerals-in-an-obj-file-into-triangles 168 | if indices.shape[1] == 8: 169 | self._build_from_hexahedra(scene, vertices, indices) 170 | elif indices.shape[1] == 4: 171 | self._build_from_tetrahedra(scene, vertices, indices) 172 | else: 173 | raise NotImplementedError 174 | 175 | cdef void _build_from_hexahedra(self, rtcs.EmbreeScene scene, 176 | np.ndarray quad_vertices, 177 | np.ndarray quad_indices): 178 | 179 | cdef int i, j 180 | cdef int nv = quad_vertices.shape[0] 181 | cdef int ne = quad_indices.shape[0] 182 | 183 | # There are six faces for every quad. Each of those will be divided 184 | # into two triangles. 185 | cdef int nt = 6*2*ne 186 | 187 | cdef unsigned int mesh = rtcg.rtcNewTriangleMesh(scene.scene_i, 188 | rtcg.RTC_GEOMETRY_STATIC, nt, nv, 1) 189 | 190 | # first just copy over the vertices 191 | cdef Vertex* vertices = rtcg.rtcMapBuffer(scene.scene_i, mesh, 192 | rtcg.RTC_VERTEX_BUFFER) 193 | 194 | for i in range(nv): 195 | vertices[i].x = quad_vertices[i, 0] 196 | vertices[i].y = quad_vertices[i, 1] 197 | vertices[i].z = quad_vertices[i, 2] 198 | rtcg.rtcUnmapBuffer(scene.scene_i, mesh, rtcg.RTC_VERTEX_BUFFER) 199 | 200 | # now build up the triangles 201 | cdef Triangle* triangles = rtcg.rtcMapBuffer(scene.scene_i, 202 | mesh, rtcg.RTC_INDEX_BUFFER) 203 | 204 | for i in range(ne): 205 | for j in range(12): 206 | triangles[12*i+j].v0 = quad_indices[i][triangulate_hex[j][0]] 207 | triangles[12*i+j].v1 = quad_indices[i][triangulate_hex[j][1]] 208 | triangles[12*i+j].v2 = quad_indices[i][triangulate_hex[j][2]] 209 | 210 | rtcg.rtcUnmapBuffer(scene.scene_i, mesh, rtcg.RTC_INDEX_BUFFER) 211 | self.vertices = vertices 212 | self.indices = triangles 213 | self.mesh = mesh 214 | 215 | cdef void _build_from_tetrahedra(self, rtcs.EmbreeScene scene, 216 | np.ndarray tetra_vertices, 217 | np.ndarray tetra_indices): 218 | 219 | cdef int i, j 220 | cdef int nv = tetra_vertices.shape[0] 221 | cdef int ne = tetra_indices.shape[0] 222 | 223 | # There are four triangle faces for each tetrahedron. 224 | cdef int nt = 4*ne 225 | 226 | cdef unsigned int mesh = rtcg.rtcNewTriangleMesh(scene.scene_i, 227 | rtcg.RTC_GEOMETRY_STATIC, nt, nv, 1) 228 | 229 | # Just copy over the vertices 230 | cdef Vertex* vertices = rtcg.rtcMapBuffer(scene.scene_i, mesh, 231 | rtcg.RTC_VERTEX_BUFFER) 232 | 233 | for i in range(nv): 234 | vertices[i].x = tetra_vertices[i, 0] 235 | vertices[i].y = tetra_vertices[i, 1] 236 | vertices[i].z = tetra_vertices[i, 2] 237 | rtcg.rtcUnmapBuffer(scene.scene_i, mesh, rtcg.RTC_VERTEX_BUFFER) 238 | 239 | # Now build up the triangles 240 | cdef Triangle* triangles = rtcg.rtcMapBuffer(scene.scene_i, 241 | mesh, rtcg.RTC_INDEX_BUFFER) 242 | for i in range(ne): 243 | for j in range(4): 244 | triangles[4*i+j].v0 = tetra_indices[i][triangulate_tetra[j][0]] 245 | triangles[4*i+j].v1 = tetra_indices[i][triangulate_tetra[j][1]] 246 | triangles[4*i+j].v2 = tetra_indices[i][triangulate_tetra[j][2]] 247 | 248 | rtcg.rtcUnmapBuffer(scene.scene_i, mesh, rtcg.RTC_INDEX_BUFFER) 249 | self.vertices = vertices 250 | self.indices = triangles 251 | self.mesh = mesh 252 | --------------------------------------------------------------------------------