From 7b09710392d35fb55b52031d447a542d99fc6b4b Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 19 Aug 2014 17:10:03 -0400 Subject: Modularize the libdimension codebase. --- .gitignore | 3 +- HACKING | 13 +- configure.ac | 2 +- dimension/client.py.in | 2 +- libdimension-python/dimension.pxd | 4 +- libdimension-python/dimension.pyx | 8 +- libdimension-python/tests/demo.py | 5 +- libdimension/Makefile.am | 211 ++++++----- libdimension/array.c | 33 -- libdimension/base/array.c | 33 ++ libdimension/base/dictionary.c | 228 +++++++++++ libdimension/base/error.c | 144 +++++++ libdimension/base/inline.c | 43 +++ libdimension/base/malloc.c | 94 +++++ libdimension/base/pool.c | 177 +++++++++ libdimension/base/profile.c | 168 ++++++++ libdimension/bench/future.c | 6 +- libdimension/bench/geometry.c | 37 +- libdimension/bench/polynomial.c | 3 +- libdimension/bench/prtree.c | 20 +- libdimension/bench/triangle.c | 10 +- libdimension/bvh.c | 404 -------------------- libdimension/bvh.h | 74 ---- libdimension/bvh/bvh.c | 402 ++++++++++++++++++++ libdimension/bvh/prtree.c | 372 ++++++++++++++++++ libdimension/camera.c | 51 --- libdimension/canvas.c | 92 ----- libdimension/canvas/canvas.c | 92 +++++ libdimension/canvas/gl-stubs.c | 48 +++ libdimension/canvas/gl.c | 113 ++++++ libdimension/canvas/png-stubs.c | 62 +++ libdimension/canvas/png.c | 393 +++++++++++++++++++ libdimension/canvas/rgba.c | 95 +++++ libdimension/canvas_pigment.c | 57 --- libdimension/checker.c | 63 --- libdimension/compiler-internal.h | 92 ----- libdimension/concurrency/future.c | 281 ++++++++++++++ libdimension/concurrency/threads.c | 326 ++++++++++++++++ libdimension/cone.c | 206 ---------- libdimension/csg.c | 332 ---------------- libdimension/cube.c | 154 -------- libdimension/dictionary.c | 228 ----------- libdimension/dimension-internal.h | 40 -- libdimension/dimension.h | 58 +-- libdimension/dimension/array.h | 357 ----------------- libdimension/dimension/base.h | 46 +++ libdimension/dimension/base/array.h | 361 ++++++++++++++++++ libdimension/dimension/base/common.h | 34 ++ libdimension/dimension/base/compiler.h | 139 +++++++ libdimension/dimension/base/dictionary.h | 87 +++++ libdimension/dimension/base/error.h | 118 ++++++ libdimension/dimension/base/malloc.h | 70 ++++ libdimension/dimension/base/pool.h | 81 ++++ libdimension/dimension/camera.h | 67 ---- libdimension/dimension/cameras.h | 34 -- libdimension/dimension/canvas.h | 114 +----- libdimension/dimension/canvas/canvas.h | 131 +++++++ libdimension/dimension/canvas/gl.h | 52 +++ libdimension/dimension/canvas/png.h | 75 ++++ libdimension/dimension/color.h | 177 +-------- libdimension/dimension/color/color.h | 198 ++++++++++ libdimension/dimension/color/tcolor.h | 116 ++++++ libdimension/dimension/common.h | 30 -- libdimension/dimension/compiler.h | 135 ------- libdimension/dimension/concurrency.h | 39 ++ libdimension/dimension/concurrency/future.h | 85 +++++ libdimension/dimension/csg.h | 59 --- libdimension/dimension/dictionary.h | 83 ---- libdimension/dimension/error.h | 117 ------ libdimension/dimension/finish.h | 141 ------- libdimension/dimension/finishes.h | 51 --- libdimension/dimension/future.h | 79 ---- libdimension/dimension/geometry.h | 500 ------------------------ libdimension/dimension/gl.h | 48 --- libdimension/dimension/interior.h | 44 --- libdimension/dimension/light.h | 76 ---- libdimension/dimension/lights.h | 33 -- libdimension/dimension/malloc.h | 66 ---- libdimension/dimension/map.h | 68 ---- libdimension/dimension/math.h | 82 +--- libdimension/dimension/math/aabb.h | 129 +++++++ libdimension/dimension/math/matrix.h | 193 ++++++++++ libdimension/dimension/math/ray.h | 83 ++++ libdimension/dimension/math/scalar.h | 103 +++++ libdimension/dimension/math/vector.h | 183 +++++++++ libdimension/dimension/model.h | 59 +++ libdimension/dimension/model/camera.h | 66 ++++ libdimension/dimension/model/cameras.h | 34 ++ libdimension/dimension/model/csg.h | 59 +++ libdimension/dimension/model/finish.h | 141 +++++++ libdimension/dimension/model/finishes.h | 51 +++ libdimension/dimension/model/interior.h | 44 +++ libdimension/dimension/model/light.h | 76 ++++ libdimension/dimension/model/lights.h | 33 ++ libdimension/dimension/model/object.h | 165 ++++++++ libdimension/dimension/model/objects.h | 103 +++++ libdimension/dimension/model/pigment.h | 86 +++++ libdimension/dimension/model/pigments.h | 66 ++++ libdimension/dimension/model/scene.h | 95 +++++ libdimension/dimension/model/texture.h | 57 +++ libdimension/dimension/object.h | 164 -------- libdimension/dimension/objects.h | 103 ----- libdimension/dimension/pattern.h | 50 +-- libdimension/dimension/pattern/map.h | 68 ++++ libdimension/dimension/pattern/pattern.h | 62 +++ libdimension/dimension/pattern/patterns.h | 48 +++ libdimension/dimension/patterns.h | 48 --- libdimension/dimension/pigment.h | 86 ----- libdimension/dimension/pigments.h | 66 ---- libdimension/dimension/platform.h | 39 ++ libdimension/dimension/platform/timer.h | 58 +++ libdimension/dimension/png.h | 71 ---- libdimension/dimension/polynomial.h | 85 ----- libdimension/dimension/pool.h | 77 ---- libdimension/dimension/ray_trace.h | 37 -- libdimension/dimension/render.h | 41 ++ libdimension/dimension/render/render.h | 41 ++ libdimension/dimension/scene.h | 95 ----- libdimension/dimension/tcolor.h | 112 ------ libdimension/dimension/texture.h | 57 --- libdimension/dimension/timer.h | 54 --- libdimension/error.c | 143 ------- libdimension/finish.c | 102 ----- libdimension/future-internal.h | 71 ---- libdimension/future.c | 279 -------------- libdimension/geometry.c | 387 ------------------- libdimension/gl-stubs.c | 48 --- libdimension/gl.c | 111 ------ libdimension/gradient.c | 56 --- libdimension/inline.c | 43 --- libdimension/interior.c | 46 --- libdimension/internal.h | 35 ++ libdimension/internal/bvh.h | 79 ++++ libdimension/internal/compiler.h | 71 ++++ libdimension/internal/concurrency.h | 236 ++++++++++++ libdimension/internal/future.h | 61 +++ libdimension/internal/platform.h | 67 ++++ libdimension/internal/polynomial.h | 85 +++++ libdimension/internal/profile.h | 68 ++++ libdimension/internal/prtree.h | 38 ++ libdimension/internal/rgba.h | 55 +++ libdimension/lambertian.c | 57 --- libdimension/leopard.c | 46 --- libdimension/light.c | 45 --- libdimension/malloc.c | 94 ----- libdimension/map.c | 125 ------ libdimension/math/matrix.c | 388 +++++++++++++++++++ libdimension/math/polynomial.c | 443 ++++++++++++++++++++++ libdimension/model/camera.c | 52 +++ libdimension/model/cameras/perspective.c | 47 +++ libdimension/model/finish.c | 103 +++++ libdimension/model/finishes/lambertian.c | 57 +++ libdimension/model/finishes/phong.c | 69 ++++ libdimension/model/finishes/reflection.c | 64 ++++ libdimension/model/interior.c | 47 +++ libdimension/model/light.c | 46 +++ libdimension/model/lights/point_light.c | 72 ++++ libdimension/model/object.c | 110 ++++++ libdimension/model/objects/cone.c | 207 ++++++++++ libdimension/model/objects/csg.c | 334 ++++++++++++++++ libdimension/model/objects/cube.c | 154 ++++++++ libdimension/model/objects/plane.c | 88 +++++ libdimension/model/objects/sphere.c | 116 ++++++ libdimension/model/objects/torus.c | 173 +++++++++ libdimension/model/objects/triangle.c | 163 ++++++++ libdimension/model/objects/triangle_fan.c | 347 +++++++++++++++++ libdimension/model/pigment.c | 72 ++++ libdimension/model/pigments/canvas_pigment.c | 57 +++ libdimension/model/pigments/pigment_map.c | 96 +++++ libdimension/model/pigments/solid_pigment.c | 35 ++ libdimension/model/scene.c | 79 ++++ libdimension/model/texture.c | 68 ++++ libdimension/object.c | 109 ------ libdimension/pattern.c | 45 --- libdimension/pattern/checker.c | 63 +++ libdimension/pattern/gradient.c | 56 +++ libdimension/pattern/leopard.c | 46 +++ libdimension/pattern/map.c | 126 ++++++ libdimension/pattern/pattern.c | 45 +++ libdimension/perspective.c | 47 --- libdimension/phong.c | 69 ---- libdimension/pigment.c | 72 ---- libdimension/pigment_map.c | 96 ----- libdimension/plane.c | 88 ----- libdimension/platform.c | 185 --------- libdimension/platform.h | 60 --- libdimension/platform/platform.c | 187 +++++++++ libdimension/platform/timer.c | 43 +++ libdimension/png-stubs.c | 62 --- libdimension/png.c | 390 ------------------- libdimension/point_light.c | 72 ---- libdimension/polynomial.c | 441 --------------------- libdimension/pool.c | 176 --------- libdimension/profile.c | 167 -------- libdimension/profile.h | 39 -- libdimension/prtree.c | 370 ------------------ libdimension/prtree.h | 30 -- libdimension/ray_trace.c | 547 -------------------------- libdimension/reflection.c | 64 ---- libdimension/render/render.c | 548 +++++++++++++++++++++++++++ libdimension/rgba.c | 95 ----- libdimension/rgba.h | 48 --- libdimension/scene.c | 77 ---- libdimension/solid_pigment.c | 36 -- libdimension/sphere.c | 114 ------ libdimension/tests/future.c | 6 +- libdimension/tests/polynomial.c | 2 +- libdimension/tests/pool.c | 6 +- libdimension/tests/prtree.c | 24 +- libdimension/tests/render.c | 2 +- libdimension/texture.c | 68 ---- libdimension/threads.c | 324 ---------------- libdimension/threads.h | 216 ----------- libdimension/timer.c | 42 -- libdimension/torus.c | 172 --------- libdimension/triangle.c | 162 -------- libdimension/triangle_fan.c | 346 ----------------- 217 files changed, 12544 insertions(+), 11934 deletions(-) delete mode 100644 libdimension/array.c create mode 100644 libdimension/base/array.c create mode 100644 libdimension/base/dictionary.c create mode 100644 libdimension/base/error.c create mode 100644 libdimension/base/inline.c create mode 100644 libdimension/base/malloc.c create mode 100644 libdimension/base/pool.c create mode 100644 libdimension/base/profile.c delete mode 100644 libdimension/bvh.c delete mode 100644 libdimension/bvh.h create mode 100644 libdimension/bvh/bvh.c create mode 100644 libdimension/bvh/prtree.c delete mode 100644 libdimension/camera.c delete mode 100644 libdimension/canvas.c create mode 100644 libdimension/canvas/canvas.c create mode 100644 libdimension/canvas/gl-stubs.c create mode 100644 libdimension/canvas/gl.c create mode 100644 libdimension/canvas/png-stubs.c create mode 100644 libdimension/canvas/png.c create mode 100644 libdimension/canvas/rgba.c delete mode 100644 libdimension/canvas_pigment.c delete mode 100644 libdimension/checker.c delete mode 100644 libdimension/compiler-internal.h create mode 100644 libdimension/concurrency/future.c create mode 100644 libdimension/concurrency/threads.c delete mode 100644 libdimension/cone.c delete mode 100644 libdimension/csg.c delete mode 100644 libdimension/cube.c delete mode 100644 libdimension/dictionary.c delete mode 100644 libdimension/dimension-internal.h delete mode 100644 libdimension/dimension/array.h create mode 100644 libdimension/dimension/base.h create mode 100644 libdimension/dimension/base/array.h create mode 100644 libdimension/dimension/base/common.h create mode 100644 libdimension/dimension/base/compiler.h create mode 100644 libdimension/dimension/base/dictionary.h create mode 100644 libdimension/dimension/base/error.h create mode 100644 libdimension/dimension/base/malloc.h create mode 100644 libdimension/dimension/base/pool.h delete mode 100644 libdimension/dimension/camera.h delete mode 100644 libdimension/dimension/cameras.h create mode 100644 libdimension/dimension/canvas/canvas.h create mode 100644 libdimension/dimension/canvas/gl.h create mode 100644 libdimension/dimension/canvas/png.h create mode 100644 libdimension/dimension/color/color.h create mode 100644 libdimension/dimension/color/tcolor.h delete mode 100644 libdimension/dimension/common.h delete mode 100644 libdimension/dimension/compiler.h create mode 100644 libdimension/dimension/concurrency.h create mode 100644 libdimension/dimension/concurrency/future.h delete mode 100644 libdimension/dimension/csg.h delete mode 100644 libdimension/dimension/dictionary.h delete mode 100644 libdimension/dimension/error.h delete mode 100644 libdimension/dimension/finish.h delete mode 100644 libdimension/dimension/finishes.h delete mode 100644 libdimension/dimension/future.h delete mode 100644 libdimension/dimension/geometry.h delete mode 100644 libdimension/dimension/gl.h delete mode 100644 libdimension/dimension/interior.h delete mode 100644 libdimension/dimension/light.h delete mode 100644 libdimension/dimension/lights.h delete mode 100644 libdimension/dimension/malloc.h delete mode 100644 libdimension/dimension/map.h create mode 100644 libdimension/dimension/math/aabb.h create mode 100644 libdimension/dimension/math/matrix.h create mode 100644 libdimension/dimension/math/ray.h create mode 100644 libdimension/dimension/math/scalar.h create mode 100644 libdimension/dimension/math/vector.h create mode 100644 libdimension/dimension/model.h create mode 100644 libdimension/dimension/model/camera.h create mode 100644 libdimension/dimension/model/cameras.h create mode 100644 libdimension/dimension/model/csg.h create mode 100644 libdimension/dimension/model/finish.h create mode 100644 libdimension/dimension/model/finishes.h create mode 100644 libdimension/dimension/model/interior.h create mode 100644 libdimension/dimension/model/light.h create mode 100644 libdimension/dimension/model/lights.h create mode 100644 libdimension/dimension/model/object.h create mode 100644 libdimension/dimension/model/objects.h create mode 100644 libdimension/dimension/model/pigment.h create mode 100644 libdimension/dimension/model/pigments.h create mode 100644 libdimension/dimension/model/scene.h create mode 100644 libdimension/dimension/model/texture.h delete mode 100644 libdimension/dimension/object.h delete mode 100644 libdimension/dimension/objects.h create mode 100644 libdimension/dimension/pattern/map.h create mode 100644 libdimension/dimension/pattern/pattern.h create mode 100644 libdimension/dimension/pattern/patterns.h delete mode 100644 libdimension/dimension/patterns.h delete mode 100644 libdimension/dimension/pigment.h delete mode 100644 libdimension/dimension/pigments.h create mode 100644 libdimension/dimension/platform.h create mode 100644 libdimension/dimension/platform/timer.h delete mode 100644 libdimension/dimension/png.h delete mode 100644 libdimension/dimension/polynomial.h delete mode 100644 libdimension/dimension/pool.h delete mode 100644 libdimension/dimension/ray_trace.h create mode 100644 libdimension/dimension/render.h create mode 100644 libdimension/dimension/render/render.h delete mode 100644 libdimension/dimension/scene.h delete mode 100644 libdimension/dimension/tcolor.h delete mode 100644 libdimension/dimension/texture.h delete mode 100644 libdimension/dimension/timer.h delete mode 100644 libdimension/error.c delete mode 100644 libdimension/finish.c delete mode 100644 libdimension/future-internal.h delete mode 100644 libdimension/future.c delete mode 100644 libdimension/geometry.c delete mode 100644 libdimension/gl-stubs.c delete mode 100644 libdimension/gl.c delete mode 100644 libdimension/gradient.c delete mode 100644 libdimension/inline.c delete mode 100644 libdimension/interior.c create mode 100644 libdimension/internal.h create mode 100644 libdimension/internal/bvh.h create mode 100644 libdimension/internal/compiler.h create mode 100644 libdimension/internal/concurrency.h create mode 100644 libdimension/internal/future.h create mode 100644 libdimension/internal/platform.h create mode 100644 libdimension/internal/polynomial.h create mode 100644 libdimension/internal/profile.h create mode 100644 libdimension/internal/prtree.h create mode 100644 libdimension/internal/rgba.h delete mode 100644 libdimension/lambertian.c delete mode 100644 libdimension/leopard.c delete mode 100644 libdimension/light.c delete mode 100644 libdimension/malloc.c delete mode 100644 libdimension/map.c create mode 100644 libdimension/math/matrix.c create mode 100644 libdimension/math/polynomial.c create mode 100644 libdimension/model/camera.c create mode 100644 libdimension/model/cameras/perspective.c create mode 100644 libdimension/model/finish.c create mode 100644 libdimension/model/finishes/lambertian.c create mode 100644 libdimension/model/finishes/phong.c create mode 100644 libdimension/model/finishes/reflection.c create mode 100644 libdimension/model/interior.c create mode 100644 libdimension/model/light.c create mode 100644 libdimension/model/lights/point_light.c create mode 100644 libdimension/model/object.c create mode 100644 libdimension/model/objects/cone.c create mode 100644 libdimension/model/objects/csg.c create mode 100644 libdimension/model/objects/cube.c create mode 100644 libdimension/model/objects/plane.c create mode 100644 libdimension/model/objects/sphere.c create mode 100644 libdimension/model/objects/torus.c create mode 100644 libdimension/model/objects/triangle.c create mode 100644 libdimension/model/objects/triangle_fan.c create mode 100644 libdimension/model/pigment.c create mode 100644 libdimension/model/pigments/canvas_pigment.c create mode 100644 libdimension/model/pigments/pigment_map.c create mode 100644 libdimension/model/pigments/solid_pigment.c create mode 100644 libdimension/model/scene.c create mode 100644 libdimension/model/texture.c delete mode 100644 libdimension/object.c delete mode 100644 libdimension/pattern.c create mode 100644 libdimension/pattern/checker.c create mode 100644 libdimension/pattern/gradient.c create mode 100644 libdimension/pattern/leopard.c create mode 100644 libdimension/pattern/map.c create mode 100644 libdimension/pattern/pattern.c delete mode 100644 libdimension/perspective.c delete mode 100644 libdimension/phong.c delete mode 100644 libdimension/pigment.c delete mode 100644 libdimension/pigment_map.c delete mode 100644 libdimension/plane.c delete mode 100644 libdimension/platform.c delete mode 100644 libdimension/platform.h create mode 100644 libdimension/platform/platform.c create mode 100644 libdimension/platform/timer.c delete mode 100644 libdimension/png-stubs.c delete mode 100644 libdimension/png.c delete mode 100644 libdimension/point_light.c delete mode 100644 libdimension/polynomial.c delete mode 100644 libdimension/pool.c delete mode 100644 libdimension/profile.c delete mode 100644 libdimension/profile.h delete mode 100644 libdimension/prtree.c delete mode 100644 libdimension/prtree.h delete mode 100644 libdimension/ray_trace.c delete mode 100644 libdimension/reflection.c create mode 100644 libdimension/render/render.c delete mode 100644 libdimension/rgba.c delete mode 100644 libdimension/rgba.h delete mode 100644 libdimension/scene.c delete mode 100644 libdimension/solid_pigment.c delete mode 100644 libdimension/sphere.c delete mode 100644 libdimension/texture.c delete mode 100644 libdimension/threads.c delete mode 100644 libdimension/threads.h delete mode 100644 libdimension/timer.c delete mode 100644 libdimension/torus.c delete mode 100644 libdimension/triangle.c delete mode 100644 libdimension/triangle_fan.c diff --git a/.gitignore b/.gitignore index e831053..1c206ec 100644 --- a/.gitignore +++ b/.gitignore @@ -27,7 +27,7 @@ Makefile.in # Files created by `make bench' /*/bench/*.bench -# Files created by make doc +# Files created by `make doc' /*/doc/ Doxyfile @@ -42,3 +42,4 @@ libdimension-python/dimension.c dimension/client.py dimension/wrapper.so __pycache__ +*.dirstamp diff --git a/HACKING b/HACKING index df8321c..172a9e0 100644 --- a/HACKING +++ b/HACKING @@ -3,15 +3,16 @@ Here's an overview of how the code is arranged: Library (LGPLv3): ================= -API: libdimension/dimension.h, libdimension/dimension/*.h - (Implementations for libdimension/dimension/$file.h - are generally at libdimension/$file.c) -Ray tracing algorithm: libdimension/ray_trace.c -Bounding hierarchy: libdimension/prtree.{c,h} -Library internals: libdimension/dimension-internal.h and its #includes +API: libdimension/dimension.h, libdimension/dimension/**/*.h +Ray tracing algorithm: libdimension/render/render.c +Bounding hierarchy: libdimension/bvh/prtree.{c,h} +Library internals: libdimension/internal.h, libdimension/internal/*.h Tests: libdimension/tests/*.{c,cpp} Benchmarks: libdimension/bench/*.c +Implementations for libdimension/dimension//.h are generally at +libdimension//.c + Python Module (LGPLv3): ======================= diff --git a/configure.ac b/configure.ac index 4ef71ea..b72858f 100644 --- a/configure.ac +++ b/configure.ac @@ -19,7 +19,7 @@ dnl Initialization AC_PREREQ([2.68]) AC_INIT([Dimension], [0.0], [tavianator@tavianator.com], [dimension], [http://www.tavianator.com/dimension/]) -AM_INIT_AUTOMAKE([parallel-tests color-tests std-options]) +AM_INIT_AUTOMAKE([subdir-objects parallel-tests color-tests std-options]) AM_SILENT_RULES([yes]) dnl Use C11 mode with GNU extensions by default diff --git a/dimension/client.py.in b/dimension/client.py.in index 2b6b084..5111a9c 100644 --- a/dimension/client.py.in +++ b/dimension/client.py.in @@ -152,7 +152,7 @@ def main(): scene.adc_bailout = float(args.adc_bailout) # Ray-trace the scene - with scene.ray_trace_async() as future: + with scene.render_async() as future: bar = None if not args.quiet: if scene.nthreads == 1: diff --git a/libdimension-python/dimension.pxd b/libdimension-python/dimension.pxd index df291a6..cb152be 100644 --- a/libdimension-python/dimension.pxd +++ b/libdimension-python/dimension.pxd @@ -403,8 +403,8 @@ cdef extern from "../libdimension/dimension.h": dmnsn_scene *dmnsn_new_scene(dmnsn_pool *pool) - void dmnsn_ray_trace(dmnsn_scene *scene) - dmnsn_future *dmnsn_ray_trace_async(dmnsn_scene *scene) + void dmnsn_render(dmnsn_scene *scene) + dmnsn_future *dmnsn_render_async(dmnsn_scene *scene) cdef extern from "platform.h": unsigned int dmnsn_terminal_width() diff --git a/libdimension-python/dimension.pyx b/libdimension-python/dimension.pyx index 2bdcc64..34d720e 100644 --- a/libdimension-python/dimension.pyx +++ b/libdimension-python/dimension.pyx @@ -1608,10 +1608,10 @@ cdef class Scene: def __get__(self): return _Timer(self._scene.render_timer) - def ray_trace(self): + def render(self): """Render the scene.""" - self.ray_trace_async().join() - def ray_trace_async(self): + self.render_async().join() + def render_async(self): """Render the scene, in the background.""" # Account for image dimensions in the camera # Do this here so subregion renders can tell us the broader image size @@ -1628,7 +1628,7 @@ cdef class Scene: # Ensure the default texture is complete cdef Texture default = Texture(pigment = Black) dmnsn_texture_cascade(default._texture, &self._scene.default_texture) - return _Future(dmnsn_ray_trace_async(self._scene)) + return _Future(dmnsn_render_async(self._scene)) def _quality_to_string(int quality): cdef str s = "" diff --git a/libdimension-python/tests/demo.py b/libdimension-python/tests/demo.py index d290a7f..2110606 100755 --- a/libdimension-python/tests/demo.py +++ b/libdimension-python/tests/demo.py @@ -49,12 +49,11 @@ scene = Scene(canvas = canvas, objects = objects, lights = lights, camera = camera) -scene.default_texture = Texture(finish = Ambient(sRGB(0.1)) - + Diffuse(sRGB(0.7))) +scene.default_texture = Texture(finish = Ambient(sRGB(0.1)) + Diffuse(sRGB(0.7))) scene.background = background scene.adc_bailout = 1/255 scene.recursion_limit = 5 -scene.ray_trace() +scene.render() if have_PNG: canvas.write_PNG("demo.png") diff --git a/libdimension/Makefile.am b/libdimension/Makefile.am index c488016..5c14363 100644 --- a/libdimension/Makefile.am +++ b/libdimension/Makefile.am @@ -17,129 +17,144 @@ ## along with this program. If not, see . ## ########################################################################### -SUBDIRS = . \ - bench \ - tests +SUBDIRS = . \ + bench \ + tests AM_CFLAGS = $(PTHREAD_CFLAGS) # Make dmnsn_error() backtraces useful AM_LDFLAGS = $(PTHREAD_LIBS) -rdynamic CC = $(PTHREAD_CC) -nobase_include_HEADERS = dimension.h \ - dimension/array.h \ - dimension/camera.h \ - dimension/cameras.h \ - dimension/canvas.h \ - dimension/color.h \ - dimension/compiler.h \ - dimension/csg.h \ - dimension/dictionary.h \ - dimension/error.h \ - dimension/finish.h \ - dimension/finishes.h \ - dimension/future.h \ - dimension/geometry.h \ - dimension/gl.h \ - dimension/interior.h \ - dimension/light.h \ - dimension/lights.h \ - dimension/malloc.h \ - dimension/map.h \ - dimension/object.h \ - dimension/objects.h \ - dimension/pattern.h \ - dimension/patterns.h \ - dimension/pigment.h \ - dimension/pigments.h \ - dimension/png.h \ - dimension/polynomial.h \ - dimension/pool.h \ - dimension/ray_trace.h \ - dimension/scene.h \ - dimension/tcolor.h \ - dimension/texture.h \ - dimension/timer.h +nobase_include_HEADERS = dimension.h \ + dimension/base.h \ + dimension/base/compiler.h \ + dimension/base/common.h \ + dimension/base/error.h \ + dimension/base/malloc.h \ + dimension/base/pool.h \ + dimension/base/array.h \ + dimension/base/dictionary.h \ + dimension/concurrency.h \ + dimension/concurrency/future.h \ + dimension/platform.h \ + dimension/platform/timer.h \ + dimension/math.h \ + dimension/math/aabb.h \ + dimension/math/matrix.h \ + dimension/math/ray.h \ + dimension/math/scalar.h \ + dimension/math/vector.h \ + dimension/color.h \ + dimension/color/color.h \ + dimension/color/tcolor.h \ + dimension/canvas.h \ + dimension/canvas/canvas.h \ + dimension/canvas/gl.h \ + dimension/canvas/png.h \ + dimension/pattern.h \ + dimension/pattern/pattern.h \ + dimension/pattern/patterns.h \ + dimension/pattern/map.h \ + dimension/model.h \ + dimension/model/camera.h \ + dimension/model/cameras.h \ + dimension/model/csg.h \ + dimension/model/finishes.h \ + dimension/model/finish.h \ + dimension/model/interior.h \ + dimension/model/light.h \ + dimension/model/lights.h \ + dimension/model/object.h \ + dimension/model/objects.h \ + dimension/model/pigment.h \ + dimension/model/pigments.h \ + dimension/model/scene.h \ + dimension/model/texture.h \ + dimension/render.h \ + dimension/render/render.h lib_LTLIBRARIES = libdimension.la -libdimension_la_SOURCES = $(nobase_include_HEADERS) \ - array.c \ - bvh.c \ - bvh.h \ - camera.c \ - canvas.c \ - canvas_pigment.c \ - checker.c \ - compiler-internal.h \ - cone.c \ - cube.c \ - csg.c \ - dictionary.c \ - dimension-internal.h \ - error.c \ - finish.c \ - future.c \ - future-internal.h \ - geometry.c \ - gradient.c \ - inline.c \ - interior.c \ - lambertian.c \ - leopard.c \ - light.c \ - malloc.c \ - map.c \ - object.c \ - pattern.c \ - perspective.c \ - phong.c \ - pigment.c \ - pigment_map.c \ - plane.c \ - platform.c \ - platform.h \ - profile.h \ - point_light.c \ - polynomial.c \ - pool.c \ - prtree.c \ - prtree.h \ - ray_trace.c \ - reflection.c \ - rgba.c \ - rgba.h \ - scene.c \ - solid_pigment.c \ - sphere.c \ - texture.c \ - threads.c \ - threads.h \ - timer.c \ - torus.c \ - triangle.c \ - triangle_fan.c +libdimension_la_SOURCES = $(nobase_include_HEADERS) \ + base/array.c \ + base/dictionary.c \ + base/error.c \ + base/inline.c \ + base/malloc.c \ + base/pool.c \ + bvh/bvh.c \ + bvh/prtree.c \ + canvas/canvas.c \ + canvas/rgba.c \ + concurrency/future.c \ + concurrency/threads.c \ + dimension.h \ + internal.h \ + internal/bvh.h \ + internal/compiler.h \ + internal/future.h \ + internal/platform.h \ + internal/polynomial.h \ + internal/profile.h \ + internal/prtree.h \ + internal/rgba.h \ + internal/threads.h \ + math/matrix.c \ + math/polynomial.c \ + model/camera.c \ + model/cameras/perspective.c \ + model/finish.c \ + model/finishes/lambertian.c \ + model/finishes/phong.c \ + model/finishes/reflection.c \ + model/interior.c \ + model/light.c \ + model/lights/point_light.c \ + model/object.c \ + model/objects/cone.c \ + model/objects/csg.c \ + model/objects/cube.c \ + model/objects/plane.c \ + model/objects/sphere.c \ + model/objects/torus.c \ + model/objects/triangle.c \ + model/objects/triangle_fan.c \ + model/pigment.c \ + model/pigments/canvas_pigment.c \ + model/pigments/pigment_map.c \ + model/pigments/solid_pigment.c \ + model/scene.c \ + model/texture.c \ + pattern/checker.c \ + pattern/gradient.c \ + pattern/leopard.c \ + pattern/map.c \ + pattern/pattern.c \ + platform/platform.c \ + platform/timer.c \ + render/render.c libdimension_la_CFLAGS = $(AM_CFLAGS) libdimension_la_LDFLAGS = -version-info 0:0:0 -no-undefined $(AM_LDFLAGS) libdimension_la_LIBADD = if PNG -libdimension_la_SOURCES += png.c +libdimension_la_SOURCES += canvas/png.c libdimension_la_CFLAGS += $(libpng_CFLAGS) libdimension_la_LIBADD += $(libpng_LIBS) else -libdimension_la_SOURCES += png-stubs.c +libdimension_la_SOURCES += canvas/png-stubs.c endif if GL -libdimension_la_SOURCES += gl.c +libdimension_la_SOURCES += canvas/gl.c libdimension_la_LIBADD += -lGL else -libdimension_la_SOURCES += gl-stubs.c +libdimension_la_SOURCES += canvas/gl-stubs.c endif if PROFILE -libdimension_la_SOURCES += profile.c +libdimension_la_SOURCES += base/profile.c endif pkgconfigdir = $(libdir)/pkgconfig diff --git a/libdimension/array.c b/libdimension/array.c deleted file mode 100644 index deea035..0000000 --- a/libdimension/array.c +++ /dev/null @@ -1,33 +0,0 @@ -/************************************************************************* - * Copyright (C) 2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Non-inline array functions. - */ - -#include "dimension.h" - -void -dmnsn_array_cleanup(void *ptr) -{ - dmnsn_array *array = ptr; - dmnsn_free(array->ptr); -} diff --git a/libdimension/base/array.c b/libdimension/base/array.c new file mode 100644 index 0000000..d8faed7 --- /dev/null +++ b/libdimension/base/array.c @@ -0,0 +1,33 @@ +/************************************************************************* + * Copyright (C) 2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Non-inline array functions. + */ + +#include "dimension/base.h" + +void +dmnsn_array_cleanup(void *ptr) +{ + dmnsn_array *array = ptr; + dmnsn_free(array->ptr); +} diff --git a/libdimension/base/dictionary.c b/libdimension/base/dictionary.c new file mode 100644 index 0000000..6e99abd --- /dev/null +++ b/libdimension/base/dictionary.c @@ -0,0 +1,228 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Associative arrays, implemented with PATRICIA tries. + */ + +#include "dimension/base.h" + +struct dmnsn_dictionary { + size_t obj_size; ///< The size of the objects in the trie. + char *prefix; ///< The local string prefix of the current node. + void *value; ///< The node's stored object, if it's a leaf. + dmnsn_array *children; ///< The node's children. +}; + +dmnsn_dictionary * +dmnsn_new_dictionary(size_t obj_size) +{ + dmnsn_dictionary *dict = DMNSN_MALLOC(dmnsn_dictionary); + dict->obj_size = obj_size; + dict->prefix = dmnsn_strdup(""); + dict->value = NULL; + dict->children = DMNSN_NEW_ARRAY(dmnsn_dictionary *); + return dict; +} + +void +dmnsn_delete_dictionary(dmnsn_dictionary *dict) +{ + if (dict) { + dmnsn_free(dict->prefix); + dmnsn_free(dict->value); + DMNSN_ARRAY_FOREACH (dmnsn_dictionary **, subtrie, dict->children) { + dmnsn_delete_dictionary(*subtrie); + } + dmnsn_delete_array(dict->children); + + dmnsn_free(dict); + } +} + +bool +dmnsn_dictionary_get(const dmnsn_dictionary *dict, const char *key, void *obj) +{ + const void *value = dmnsn_dictionary_at(dict, key); + if (value) { + memcpy(obj, value, dict->obj_size); + return true; + } else { + return false; + } +} + +void * +dmnsn_dictionary_at(const dmnsn_dictionary *dict, const char *key) +{ + // PATRICIA trie search: O(k), where k is the length of the longest string in + // the trie. + + size_t len = strlen(dict->prefix); + if (strncmp(key, dict->prefix, len) != 0) + return NULL; + key += len; + + while (true) { + if (*key == '\0' && dict->value) { + return dict->value; + } else { + dmnsn_dictionary **first = dmnsn_array_first(dict->children), **subtrie; + ptrdiff_t size = dmnsn_array_size(dict->children); + for (subtrie = first; subtrie - first < size; ++subtrie) { + len = strlen((*subtrie)->prefix); + if (strncmp(key, (*subtrie)->prefix, len) == 0) { + dict = *subtrie; + key += len; + break; + } + } + + if (subtrie - first == size) + break; + } + } + + return NULL; +} + +void +dmnsn_dictionary_insert(dmnsn_dictionary *dict, const char *key, + const void *obj) +{ + // PATRICIA trie insertion: O(k), where k is the length of the longest string + // in the trie. + + while (true) { + if (dict->prefix[0] == '\0' && !dict->value + && dmnsn_array_size(dict->children) == 0) + { + // Replace an empty tree with a single-element tree + dict->prefix = dmnsn_realloc(dict->prefix, strlen(key) + 1); + strcpy(dict->prefix, key); + + dict->value = dmnsn_malloc(dict->obj_size); + memcpy(dict->value, obj, dict->obj_size); + break; + } + + char *prefix = dict->prefix; + while (*prefix == *key && *prefix && *key) { + ++prefix; + ++key; + } + + if (*key == '\0' && *prefix == '\0') { + // Complete match + if (!dict->value) { + dict->value = dmnsn_malloc(dict->obj_size); + } + memcpy(dict->value, obj, dict->obj_size); + break; + } else if (*prefix == '\0') { + // Partial match; key starts with prefix + dmnsn_dictionary **first = dmnsn_array_first(dict->children), **subtrie; + ptrdiff_t size = dmnsn_array_size(dict->children); + for (subtrie = first; subtrie - first < size; ++subtrie) { + if ((*subtrie)->prefix[0] == key[0]) { + dict = *subtrie; + break; + } + } + + if (subtrie - first == size) { + // No submatch found, add a new child + dmnsn_dictionary *child = dmnsn_new_dictionary(dict->obj_size); + dmnsn_array_push(dict->children, &child); + dict = child; + } + } else { + // Split the tree + dmnsn_dictionary *copy = dmnsn_new_dictionary(dict->obj_size); + copy->prefix = dmnsn_realloc(copy->prefix, strlen(prefix) + 1); + strcpy(copy->prefix, prefix); + *prefix = '\0'; + + copy->value = dict->value; + dict->value = NULL; + + dmnsn_array *temp = copy->children; + copy->children = dict->children; + dict->children = temp; + + dmnsn_dictionary *subtrie = dmnsn_new_dictionary(dict->obj_size); + dmnsn_array_push(dict->children, ©); + dmnsn_array_push(dict->children, &subtrie); + dict = subtrie; + } + } +} + +bool +dmnsn_dictionary_remove(dmnsn_dictionary *dict, const char *key) +{ + // PATRICIA trie removal: O(k), where k is the length of the longest string + // in the trie. + + // This implementation doesn't actually collapse the tree back upwards if a + // node is left with only one child, to reduce complexity. + + size_t len = strlen(dict->prefix); + if (strncmp(key, dict->prefix, len) != 0) + return false; + key += len; + + while (true) { + if (*key == '\0' && dict->value) { + dmnsn_free(dict->value); + dict->value = NULL; + return true; + } else { + dmnsn_dictionary **first = dmnsn_array_first(dict->children), **subtrie; + ptrdiff_t size = dmnsn_array_size(dict->children); + for (subtrie = first; subtrie - first < size; ++subtrie) { + len = strlen((*subtrie)->prefix); + if (strncmp(key, (*subtrie)->prefix, len) == 0) { + dict = *subtrie; + key += len; + break; + } + } + + if (subtrie - first == size) + break; + } + } + + return false; +} + +void +dmnsn_dictionary_apply(dmnsn_dictionary *dict, dmnsn_callback_fn *callback) +{ + if (dict->value) { + callback(dict->value); + } + + DMNSN_ARRAY_FOREACH (dmnsn_dictionary **, subtrie, dict->children) { + dmnsn_dictionary_apply(*subtrie, callback); + } +} diff --git a/libdimension/base/error.c b/libdimension/base/error.c new file mode 100644 index 0000000..6b8d18e --- /dev/null +++ b/libdimension/base/error.c @@ -0,0 +1,144 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Error handling. + */ + +#include "internal.h" +#include "internal/platform.h" +#include +#include +#include +#include +#include +#include +#include + +/// Report internal errors in this file. +#define DMNSN_LOCAL_ERROR(str) \ + do { \ + fprintf(stderr, "Dimension ERROR: %s, %s:%u: %s\n", \ + DMNSN_FUNC, __FILE__, __LINE__, (str)); \ + abort(); \ + } while (0) + +/// The default fatal error handler. +static void dmnsn_default_fatal_error_fn(void); + +/// The current fatal error handler. +static atomic(dmnsn_fatal_error_fn *) dmnsn_fatal = ATOMIC_VAR_INIT(dmnsn_default_fatal_error_fn); + +/// The current resilience. +static atomic_bool dmnsn_always_die = ATOMIC_VAR_INIT(false); + +void +dmnsn_report_impl(bool die, const char *func, const char *file, unsigned int line, const char *str) +{ + // Save the value of errno + int err = errno; + + bool always_die = atomic_load(&dmnsn_always_die); + + // Print the diagnostic string + fprintf(stderr, "Dimension %s: %s, %s:%u: %s\n", + die ? "ERROR" : "WARNING", func, file, line, str); + + // Print the value of errno + if (err != 0) { + fprintf(stderr, "Last error: %d", err); +#if DMNSN_STRERROR_R + char errbuf[256]; + if (strerror_r(err, errbuf, 256) == 0) { + fprintf(stderr, " (%s)", errbuf); + } +#elif DMNSN_SYS_ERRLIST + if (err >= 0 && err < sys_nerr) { + fprintf(stderr, " (%s)", sys_errlist[err]); + } +#endif + fprintf(stderr, "\n"); + } + + // Print a stack trace to standard error + dmnsn_backtrace(stderr); + + // Call the fatal error handler if needed + if (die || always_die) { + static __thread bool thread_exiting = false; + + if (thread_exiting) { + if (die) { + // Prevent infinite recursion if the fatal error function itself calls + // dmnsn_error() (not dmnsn_warning()) + DMNSN_LOCAL_ERROR("Error raised while in error handler, aborting."); + } + } else { + thread_exiting = true; + + dmnsn_fatal_error_fn *fatal = dmnsn_get_fatal_error_fn(); + fatal(); + DMNSN_LOCAL_ERROR("Fatal error handler didn't exit."); + } + } +} + +void +dmnsn_report_warning(const char *func, const char *file, unsigned int line, const char *str) +{ + dmnsn_report_impl(false, func, file, line, str); +} + +DMNSN_NORETURN +dmnsn_report_error(const char *func, const char *file, unsigned int line, const char *str) +{ + dmnsn_report_impl(true, func, file, line, str); + DMNSN_UNREACHABLE(); +} + +void +dmnsn_die_on_warnings(bool always_die) +{ + atomic_store(&dmnsn_always_die, always_die); +} + +dmnsn_fatal_error_fn * +dmnsn_get_fatal_error_fn(void) +{ + dmnsn_fatal_error_fn *fatal = atomic_load(&dmnsn_fatal); + return fatal; +} + +void +dmnsn_set_fatal_error_fn(dmnsn_fatal_error_fn *fatal) +{ + atomic_store(&dmnsn_fatal, fatal); +} + +static void +dmnsn_default_fatal_error_fn(void) +{ + if (dmnsn_is_main_thread()) { + exit(EXIT_FAILURE); + } else { + pthread_exit(NULL); + } +} diff --git a/libdimension/base/inline.c b/libdimension/base/inline.c new file mode 100644 index 0000000..b0622fe --- /dev/null +++ b/libdimension/base/inline.c @@ -0,0 +1,43 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Emit definitions of inline functions, if necessary. + */ + +// Set DMNSN_INLINE to produce definitions of inline functions, emitted here, +// if needed +#ifdef __cplusplus + // C++ inline semantics + #define DMNSN_INLINE inline +#elif __STDC_VERSION__ >= 199901L + // C99 inline semantics + #define DMNSN_INLINE +#elif defined(__GNUC__) + // GCC inline semantics + #define DMNSN_INLINE __inline__ +#else + // Unknown C - mark functions static and hope the compiler is smart enough to + // inline them + #define DMNSN_INLINE static +#endif + +#include "dimension.h" diff --git a/libdimension/base/malloc.c b/libdimension/base/malloc.c new file mode 100644 index 0000000..6aaf68c --- /dev/null +++ b/libdimension/base/malloc.c @@ -0,0 +1,94 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Dynamic memory. + */ + +#include "internal.h" +#include +#include +#include + +#if DMNSN_DEBUG +static atomic_size_t dmnsn_allocs = ATOMIC_VAR_INIT(0); +#endif + +void * +dmnsn_malloc(size_t size) +{ + void *ptr = malloc(size); + if (!ptr) { + dmnsn_error("Memory allocation failed."); + } + +#if DMNSN_DEBUG + atomic_fetch_add(&dmnsn_allocs, 1); +#endif + + return ptr; +} + +void * +dmnsn_realloc(void *ptr, size_t size) +{ +#if DMNSN_DEBUG + if (!ptr) { + atomic_fetch_add(&dmnsn_allocs, 1); + } +#endif + + ptr = realloc(ptr, size); + if (!ptr) { + dmnsn_error("Memory allocation failed."); + } + return ptr; +} + +char * +dmnsn_strdup(const char *s) +{ + char *copy = dmnsn_malloc(strlen(s) + 1); + strcpy(copy, s); + return copy; +} + +void +dmnsn_free(void *ptr) +{ +#if DMNSN_DEBUG + if (ptr) { + atomic_fetch_sub(&dmnsn_allocs, 1); + } +#endif + + free(ptr); +} + +#if DMNSN_DEBUG +DMNSN_LATE_DESTRUCTOR static void +dmnsn_leak_check(void) +{ + if (atomic_load_explicit(&dmnsn_allocs, memory_order_relaxed) > 0) { + dmnsn_warning("Leaking memory."); + } +} +#endif diff --git a/libdimension/base/pool.c b/libdimension/base/pool.c new file mode 100644 index 0000000..4bfdd7d --- /dev/null +++ b/libdimension/base/pool.c @@ -0,0 +1,177 @@ +/************************************************************************* + * Copyright (C) 2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Memory pool implementation. + */ + +#include "internal.h" +#include "internal/concurrency.h" +#include + +/// Number of pointers per block, we want a block to fit in a single page. +#define DMNSN_POOL_BLOCK_SIZE (4096/sizeof(void *) - 4) +/// Number of pointers per tidy block +#define DMNSN_TIDY_BLOCK_SIZE ((4096 - 4*sizeof(void *))/(sizeof(void *) + sizeof(dmnsn_callback_fn *))) + +/// A single block in a thread-specific pool. +typedef struct dmnsn_pool_block { + /// Current index into allocs[]. + size_t i; + /// All allocations in the current block. + void *allocs[DMNSN_POOL_BLOCK_SIZE]; + /// Tail pointer to the previous block in the global chain. + struct dmnsn_pool_block *prev; +} dmnsn_pool_block; + +/// A single tidy block in a thread-specific pool. +typedef struct dmnsn_tidy_block { + /// Current index into allocs[]. + size_t i; + /// All allocations in the current block. + void *allocs[DMNSN_TIDY_BLOCK_SIZE]; + /// All cleanup callbacks in the current block. + dmnsn_callback_fn *cleanup_fns[DMNSN_TIDY_BLOCK_SIZE]; + /// Tail pointer to the previous tidy block in the global chain. + struct dmnsn_tidy_block *prev; +} dmnsn_tidy_block; + +/// dmnsn_pool implementation. +struct dmnsn_pool { + /// Thread-local regular block. + pthread_key_t thread_block; + /// Thread-local tidy block. + pthread_key_t thread_tidy_block; + + /// Global chain of regular blocks. + atomic(dmnsn_pool_block *) chain; + /// Global chain of tidy blocks. + atomic(dmnsn_tidy_block *) tidy_chain; +}; + +dmnsn_pool * +dmnsn_new_pool(void) +{ + dmnsn_pool *pool = DMNSN_MALLOC(dmnsn_pool); + + dmnsn_key_create(&pool->thread_block, NULL); + dmnsn_key_create(&pool->thread_tidy_block, NULL); + + atomic_store_explicit(&pool->chain, NULL, memory_order_relaxed); + atomic_store_explicit(&pool->tidy_chain, NULL, memory_order_relaxed); + + return pool; +} + +void * +dmnsn_palloc(dmnsn_pool *pool, size_t size) +{ + dmnsn_pool_block *old_block = pthread_getspecific(pool->thread_block); + + dmnsn_pool_block *new_block = old_block; + if (dmnsn_unlikely(!old_block || old_block->i == DMNSN_POOL_BLOCK_SIZE)) { + new_block = DMNSN_MALLOC(dmnsn_pool_block); + new_block->i = 0; + } + + void *result = dmnsn_malloc(size); + new_block->allocs[new_block->i++] = result; + + if (dmnsn_unlikely(new_block != old_block)) { + dmnsn_setspecific(pool->thread_block, new_block); + + // Atomically update pool->chain + dmnsn_pool_block *old_chain = atomic_exchange(&pool->chain, new_block); + new_block->prev = old_chain; + } + + return result; +} + +void * +dmnsn_palloc_tidy(dmnsn_pool *pool, size_t size, dmnsn_callback_fn *cleanup_fn) +{ + dmnsn_assert(cleanup_fn != NULL, "NULL cleanup_fn"); + + dmnsn_tidy_block *old_block = pthread_getspecific(pool->thread_tidy_block); + + dmnsn_tidy_block *new_block = old_block; + if (dmnsn_unlikely(!old_block || old_block->i == DMNSN_TIDY_BLOCK_SIZE)) { + new_block = DMNSN_MALLOC(dmnsn_tidy_block); + new_block->i = 0; + } + + void *result = dmnsn_malloc(size); + + size_t i = new_block->i; + new_block->allocs[i] = result; + new_block->cleanup_fns[i] = cleanup_fn; + ++new_block->i; + + if (dmnsn_unlikely(new_block != old_block)) { + dmnsn_setspecific(pool->thread_tidy_block, new_block); + + // Atomically update pool->tidy_chain + dmnsn_tidy_block *old_chain = atomic_exchange(&pool->tidy_chain, new_block); + new_block->prev = old_chain; + } + + return result; +} + +void +dmnsn_delete_pool(dmnsn_pool *pool) +{ + if (!pool) { + return; + } + + dmnsn_pool_block *block = atomic_load_explicit(&pool->chain, memory_order_relaxed); + while (block) { + // Free all the allocations + for (size_t i = block->i; i-- > 0;) { + dmnsn_free(block->allocs[i]); + } + + // Free the block itself and go to the previous one + dmnsn_pool_block *saved = block; + block = block->prev; + dmnsn_free(saved); + } + + dmnsn_tidy_block *tidy_block = atomic_load_explicit(&pool->tidy_chain, memory_order_relaxed); + while (tidy_block) { + // Free all the allocations + for (size_t i = tidy_block->i; i-- > 0;) { + void *ptr = tidy_block->allocs[i]; + tidy_block->cleanup_fns[i](ptr); + dmnsn_free(ptr); + } + + // Free the block itself and go to the previous one + dmnsn_tidy_block *saved = tidy_block; + tidy_block = tidy_block->prev; + dmnsn_free(saved); + } + + dmnsn_key_delete(pool->thread_tidy_block); + dmnsn_free(pool); +} diff --git a/libdimension/base/profile.c b/libdimension/base/profile.c new file mode 100644 index 0000000..87f27c1 --- /dev/null +++ b/libdimension/base/profile.c @@ -0,0 +1,168 @@ +/************************************************************************* + * Copyright (C) 2010-2011 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Branch profile accounting. + */ + +#include "internal.h" +#include "internal/concurrency.h" +#include +#include +#include + +/// Information on one predicted branch. +typedef struct { + char *location; + uint64_t predicted, branches; +} dmnsn_branch; + +/// Count of mispredicted branches. +static dmnsn_dictionary *dmnsn_profile = NULL; +/// Mutex which protects \c dmnsn_profile. +static pthread_mutex_t dmnsn_profile_mutex = PTHREAD_MUTEX_INITIALIZER; + +/// Thread-local count of mispredicted branches. +static pthread_key_t dmnsn_thread_profile; +/// Initialize the thread-specific pointer exactly once. +static pthread_once_t dmnsn_thread_profile_once = PTHREAD_ONCE_INIT; + +/// Add thread-specific profile data to the global counts. +static void +dmnsn_profile_globalize(void *ptr) +{ + dmnsn_branch *branch = ptr; + dmnsn_branch *global = dmnsn_dictionary_at(dmnsn_profile, branch->location); + if (global) { + global->predicted += branch->predicted; + global->branches += branch->branches; + dmnsn_free(branch->location); + } else { + dmnsn_dictionary_insert(dmnsn_profile, branch->location, branch); + } +} + +/// Destructor function for thread-specific profile data. +static void +dmnsn_delete_thread_profile(void *ptr) +{ + dmnsn_dictionary *thread_profile = ptr; + + dmnsn_lock_mutex(&dmnsn_profile_mutex); + dmnsn_dictionary_apply(thread_profile, dmnsn_profile_globalize); + dmnsn_unlock_mutex(&dmnsn_profile_mutex); + + dmnsn_delete_dictionary(thread_profile); +} + +/// Initialize the thread-specific pointer. +static void +dmnsn_initialize_thread_profile(void) +{ + dmnsn_key_create(&dmnsn_thread_profile, dmnsn_delete_thread_profile); + + dmnsn_lock_mutex(&dmnsn_profile_mutex); + dmnsn_profile = dmnsn_new_dictionary(sizeof(dmnsn_branch)); + dmnsn_unlock_mutex(&dmnsn_profile_mutex); +} + +/// Get the thread-specific profile data. +static dmnsn_dictionary * +dmnsn_get_thread_profile(void) +{ + dmnsn_once(&dmnsn_thread_profile_once, dmnsn_initialize_thread_profile); + return pthread_getspecific(dmnsn_thread_profile); +} + +/// Set the thread-specific profile data. +static void +dmnsn_set_thread_profile(dmnsn_dictionary *thread_profile) +{ + dmnsn_setspecific(dmnsn_thread_profile, thread_profile); +} + +bool +dmnsn_expect(bool result, bool expected, const char *func, const char *file, + unsigned int line) +{ + int size = snprintf(NULL, 0, "%s:%s:%u", file, func, line) + 1; + if (size < 1) { + dmnsn_error("sprintf() failed."); + } + + char key[size]; + if (snprintf(key, size, "%s:%s:%u", file, func, line) < 0) { + dmnsn_error("sprintf() failed."); + } + + dmnsn_dictionary *thread_profile = dmnsn_get_thread_profile(); + if (!thread_profile) { + thread_profile = dmnsn_new_dictionary(sizeof(dmnsn_branch)); + dmnsn_set_thread_profile(thread_profile); + } + + dmnsn_branch *branch = dmnsn_dictionary_at(thread_profile, key); + if (branch) { + ++branch->branches; + if (result == expected) { + ++branch->predicted; + } + } else { + dmnsn_branch new_branch = { + .location = dmnsn_strdup(key), + .predicted = (result == expected) ? 1 : 0, + .branches = 1 + }; + dmnsn_dictionary_insert(thread_profile, key, &new_branch); + } + + return result; +} + +static void +dmnsn_print_bad_prediction(void *ptr) +{ + dmnsn_branch *branch = ptr; + double rate = ((double)branch->predicted)/branch->branches; + if (rate < 0.75 || branch->branches < 100000) { + fprintf(stderr, + "Bad branch prediction: %s: %" PRIu64 "/%" PRIu64 " (%g%%)\n", + branch->location, branch->predicted, branch->branches, 100.0*rate); + } + + dmnsn_free(branch->location); +} + +DMNSN_DESTRUCTOR static void +dmnsn_print_bad_predictions(void) +{ + dmnsn_dictionary *thread_profile = dmnsn_get_thread_profile(); + if (thread_profile) { + dmnsn_delete_thread_profile(thread_profile); + dmnsn_set_thread_profile(NULL); + } + + dmnsn_lock_mutex(&dmnsn_profile_mutex); + dmnsn_dictionary_apply(dmnsn_profile, dmnsn_print_bad_prediction); + dmnsn_delete_dictionary(dmnsn_profile); + dmnsn_profile = NULL; + dmnsn_unlock_mutex(&dmnsn_profile_mutex); +} diff --git a/libdimension/bench/future.c b/libdimension/bench/future.c index fd26363..c8fb529 100644 --- a/libdimension/bench/future.c +++ b/libdimension/bench/future.c @@ -17,9 +17,9 @@ * along with this program. If not, see . * *************************************************************************/ -#include "../platform.c" -#include "../future.c" -#include "../threads.c" +#include "../platform/platform.c" +#include "../concurrency/future.c" +#include "../concurrency/threads.c" #include #include diff --git a/libdimension/bench/geometry.c b/libdimension/bench/geometry.c index 68c9885..59d27e3 100644 --- a/libdimension/bench/geometry.c +++ b/libdimension/bench/geometry.c @@ -17,17 +17,18 @@ * along with this program. If not, see . * *************************************************************************/ -#include "dimension.h" +#include "dimension/math.h" #include #include +#include int main(void) { dmnsn_vector vector, vector2; dmnsn_matrix matrix, matrix2; - dmnsn_line line; - dmnsn_bounding_box box; + dmnsn_ray ray; + dmnsn_aabb box; double result; sandglass_t sandglass; @@ -74,19 +75,19 @@ main(void) }); printf("dmnsn_rotation_matrix(): %ld\n", sandglass.grains); - // dmnsn_new_line() + // dmnsn_new_ray() vector2 = dmnsn_new_vector(3.0, 2.0, 1.0); sandglass_bench_fine(&sandglass, { - line = dmnsn_new_line(vector, vector2); + ray = dmnsn_new_ray(vector, vector2); }); - printf("dmnsn_new_line(): %ld\n", sandglass.grains); + printf("dmnsn_new_ray(): %ld\n", sandglass.grains); - // dmnsn_new_bounding_box() + // dmnsn_new_aabb() vector2 = dmnsn_new_vector(3.0, 4.0, 5.0); sandglass_bench_fine(&sandglass, { - box = dmnsn_new_bounding_box(vector, vector2); + box = dmnsn_new_aabb(vector, vector2); }); - printf("dmnsn_new_bounding_box(): %ld\n", sandglass.grains); + printf("dmnsn_new_aabb(): %ld\n", sandglass.grains); // dmnsn_vector_add() sandglass_bench_fine(&sandglass, { @@ -175,23 +176,23 @@ main(void) }); printf("dmnsn_transform_normal(): %ld\n", sandglass.grains); - // dmnsn_transform_line() + // dmnsn_transform_ray() sandglass_bench_fine(&sandglass, { - line = dmnsn_transform_line(matrix, line); + ray = dmnsn_transform_ray(matrix, ray); }); - printf("dmnsn_transform_line(): %ld\n", sandglass.grains); + printf("dmnsn_transform_ray(): %ld\n", sandglass.grains); - // dmnsn_transform_bounding_box() + // dmnsn_transform_aabb() sandglass_bench_fine(&sandglass, { - box = dmnsn_transform_bounding_box(matrix, box); + box = dmnsn_transform_aabb(matrix, box); }); - printf("dmnsn_transform_bounding_box(): %ld\n", sandglass.grains); + printf("dmnsn_transform_aabb(): %ld\n", sandglass.grains); - // dmnsn_line_point() + // dmnsn_ray_point() sandglass_bench_fine(&sandglass, { - vector = dmnsn_line_point(line, result); + vector = dmnsn_ray_point(ray, result); }); - printf("dmnsn_line_point(): %ld\n", sandglass.grains); + printf("dmnsn_ray_point(): %ld\n", sandglass.grains); return EXIT_SUCCESS; } diff --git a/libdimension/bench/polynomial.c b/libdimension/bench/polynomial.c index bf3d2b0..33fd390 100644 --- a/libdimension/bench/polynomial.c +++ b/libdimension/bench/polynomial.c @@ -17,7 +17,8 @@ * along with this program. If not, see . * *************************************************************************/ -#include "dimension.h" +#define DMNSN_INLINE extern inline +#include "../math/polynomial.c" #include #include diff --git a/libdimension/bench/prtree.c b/libdimension/bench/prtree.c index 6c5d3ef..6b5e7c1 100644 --- a/libdimension/bench/prtree.c +++ b/libdimension/bench/prtree.c @@ -17,19 +17,19 @@ * along with this program. If not, see . * *************************************************************************/ -#include "../platform.c" -#include "../threads.c" -#include "../future.c" -#include "../bvh.c" -#include "../prtree.c" +#include "../platform/platform.c" +#include "../concurrency/threads.c" +#include "../concurrency/future.c" +#include "../bvh/bvh.c" +#include "../bvh/prtree.c" #include #include static bool -dmnsn_fake_intersection_fn(const dmnsn_object *object, dmnsn_line line, +dmnsn_fake_intersection_fn(const dmnsn_object *object, dmnsn_ray ray, dmnsn_intersection *intersection) { - intersection->t = (object->bounding_box.min.z - line.x0.z)/line.n.z; + intersection->t = (object->aabb.min.z - ray.x0.z)/ray.n.z; intersection->normal = dmnsn_x; return true; } @@ -40,7 +40,7 @@ dmnsn_fake_inside_fn(const dmnsn_object *object, dmnsn_vector point) return true; } -static dmnsn_bounding_box +static dmnsn_aabb dmnsn_fake_bounding_fn(const dmnsn_object *object, dmnsn_matrix trans) { dmnsn_vector a, b; @@ -53,7 +53,7 @@ dmnsn_fake_bounding_fn(const dmnsn_object *object, dmnsn_matrix trans) b.y = 2.0*((double)rand())/RAND_MAX - 1.0; b.z = 2.0*((double)rand())/RAND_MAX - 1.0; - return dmnsn_new_bounding_box(dmnsn_vector_min(a, b), dmnsn_vector_max(a, b)); + return dmnsn_new_aabb(dmnsn_vector_min(a, b), dmnsn_vector_max(a, b)); } static dmnsn_object_vtable dmnsn_fake_vtable = { @@ -99,7 +99,7 @@ main(void) printf("dmnsn_new_bvh(DMNSN_BVH_PRTREE): %ld\n", sandglass.grains); // dmnsn_bvh_intersection() - dmnsn_line ray = dmnsn_new_line( + dmnsn_ray ray = dmnsn_new_ray( dmnsn_new_vector( 1.0, 1.0, -2.0), dmnsn_new_vector(-0.5, -0.5, 1.0) ); diff --git a/libdimension/bench/triangle.c b/libdimension/bench/triangle.c index b2b91d0..4a5a456 100644 --- a/libdimension/bench/triangle.c +++ b/libdimension/bench/triangle.c @@ -43,21 +43,21 @@ main(void) dmnsn_object_precompute(triangle); dmnsn_intersection intersection; - dmnsn_line line; + dmnsn_ray ray; bool intersected; // Intersecting case - line = dmnsn_new_line(dmnsn_new_vector(2.0, 1.0, -1.0), dmnsn_z); + ray = dmnsn_new_ray(dmnsn_new_vector(2.0, 1.0, -1.0), dmnsn_z); sandglass_bench_fine(&sandglass, { - intersected = dmnsn_object_intersection(triangle, line, &intersection); + intersected = dmnsn_object_intersection(triangle, ray, &intersection); }); dmnsn_assert(intersected, "Didn't intersect"); printf("dmnsn_triangle_intersection(true): %ld\n", sandglass.grains); // Non-intersecting case - line = dmnsn_new_line(dmnsn_new_vector(3.0, 3.0, -1.0), dmnsn_z); + ray = dmnsn_new_ray(dmnsn_new_vector(3.0, 3.0, -1.0), dmnsn_z); sandglass_bench_fine(&sandglass, { - intersected = dmnsn_object_intersection(triangle, line, &intersection); + intersected = dmnsn_object_intersection(triangle, ray, &intersection); }); dmnsn_assert(!intersected, "Intersected"); printf("dmnsn_triangle_intersection(false): %ld\n", sandglass.grains); diff --git a/libdimension/bvh.c b/libdimension/bvh.c deleted file mode 100644 index d1bd0fc..0000000 --- a/libdimension/bvh.c +++ /dev/null @@ -1,404 +0,0 @@ -/************************************************************************* - * Copyright (C) 2012-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * BVH implementation. These are the hottest code paths in libdimension. - */ - -#include "dimension-internal.h" - -/// Implementation for DMNSN_BVH_NONE: just stick all objects in one node. -static dmnsn_bvh_node * -dmnsn_new_stupid_bvh(const dmnsn_array *objects) -{ - dmnsn_bvh_node *root = dmnsn_new_bvh_node(dmnsn_array_size(objects)); - - DMNSN_ARRAY_FOREACH (dmnsn_object **, object, objects) { - dmnsn_bvh_node *leaf = dmnsn_new_bvh_leaf_node(*object); - dmnsn_bvh_node_add(root, leaf); - } - - return root; -} - -// Implementation of opaque dmnsn_bvh type. -struct dmnsn_bvh { - dmnsn_array *unbounded; ///< The unbounded objects. - dmnsn_array *bounded; ///< The BVH of the bounded objects. - pthread_key_t intersection_cache; ///< The thread-local intersection cache. -}; - -/// A flat BVH node for storing in an array for fast pre-order traversal. -typedef struct dmnsn_flat_bvh_node { - dmnsn_bounding_box bounding_box; // The bounding box of this node. - dmnsn_object *object; // The referenced object, for leaf nodes. - ptrdiff_t skip; // Displacement to the next sibling. -} dmnsn_flat_bvh_node; - -/// Add an object or its children, if any, to an array. -static void -dmnsn_split_add_object(dmnsn_array *objects, const dmnsn_object *object) -{ - if (object->split_children) { - DMNSN_ARRAY_FOREACH (const dmnsn_object **, child, object->children) { - dmnsn_split_add_object(objects, *child); - } - } else { - dmnsn_array_push(objects, &object); - } -} - -/// Split unions to create the input for the BVH. -static dmnsn_array * -dmnsn_split_objects(const dmnsn_array *objects) -{ - dmnsn_array *split = DMNSN_NEW_ARRAY(dmnsn_object *); - DMNSN_ARRAY_FOREACH (const dmnsn_object **, object, objects) { - dmnsn_split_add_object(split, *object); - } - return split; -} - -/// Split unbounded objects into a new array. -static dmnsn_array * -dmnsn_split_unbounded(dmnsn_array *objects) -{ - dmnsn_array *unbounded = DMNSN_NEW_ARRAY(dmnsn_object *); - - dmnsn_object **array = dmnsn_array_first(objects); - size_t i, skip; - for (i = 0, skip = 0; i < dmnsn_array_size(objects); ++i) { - if (dmnsn_bounding_box_is_infinite(array[i]->bounding_box)) { - dmnsn_array_push(unbounded, &array[i]); - ++skip; - } else { - array[i - skip] = array[i]; - } - } - dmnsn_array_resize(objects, i - skip); - - return unbounded; -} - -/// Recursively flatten a BVH into an array of flat nodes. -static void -dmnsn_flatten_bvh_recursive(dmnsn_bvh_node *node, dmnsn_array *flat) -{ - size_t currenti = dmnsn_array_size(flat); - dmnsn_array_resize(flat, currenti + 1); - dmnsn_flat_bvh_node *flatnode = dmnsn_array_at(flat, currenti); - - flatnode->bounding_box = node->bounding_box; - flatnode->object = node->object; - - for (size_t i = 0; i < node->nchildren && node->children[i]; ++i) { - dmnsn_flatten_bvh_recursive(node->children[i], flat); - } - - // Array could have been realloc()'d somewhere else above - flatnode = dmnsn_array_at(flat, currenti); - flatnode->skip = dmnsn_array_size(flat) - currenti; -} - -/// Flatten a BVH into an array of flat nodes. -static dmnsn_array * -dmnsn_flatten_bvh(dmnsn_bvh_node *root) -{ - dmnsn_array *flat = DMNSN_NEW_ARRAY(dmnsn_flat_bvh_node); - if (root) { - dmnsn_flatten_bvh_recursive(root, flat); - } - return flat; -} - -dmnsn_bvh *dmnsn_new_bvh(const dmnsn_array *objects, dmnsn_bvh_kind kind) -{ - dmnsn_bvh *bvh = DMNSN_MALLOC(dmnsn_bvh); - - dmnsn_array *bounded = dmnsn_split_objects(objects); - bvh->unbounded = dmnsn_split_unbounded(bounded); - - dmnsn_bvh_node *root = NULL; - if (dmnsn_array_size(bounded) > 0) { - switch (kind) { - case DMNSN_BVH_NONE: - root = dmnsn_new_stupid_bvh(bounded); - break; - case DMNSN_BVH_PRTREE: - root = dmnsn_new_prtree(bounded); - break; - default: - dmnsn_unreachable("Invalid BVH kind."); - } - } - bvh->bounded = dmnsn_flatten_bvh(root); - - dmnsn_delete_bvh_node(root); - dmnsn_delete_array(bounded); - - dmnsn_key_create(&bvh->intersection_cache, dmnsn_free); - - return bvh; -} - -void -dmnsn_delete_bvh(dmnsn_bvh *bvh) -{ - if (bvh) { - dmnsn_free(pthread_getspecific(bvh->intersection_cache)); - dmnsn_key_delete(bvh->intersection_cache); - dmnsn_delete_array(bvh->bounded); - dmnsn_delete_array(bvh->unbounded); - dmnsn_free(bvh); - } -} - -/// A line with pre-calculated reciprocals to avoid divisions. -typedef struct dmnsn_optimized_line { - dmnsn_vector x0; ///< The origin of the line. - dmnsn_vector n_inv; ///< The inverse of each component of the line's slope -} dmnsn_optimized_line; - -/// Precompute inverses for faster ray-box intersection tests. -static inline dmnsn_optimized_line -dmnsn_optimize_line(dmnsn_line line) -{ - dmnsn_optimized_line optline = { - .x0 = line.x0, - .n_inv = dmnsn_new_vector(1.0/line.n.x, 1.0/line.n.y, 1.0/line.n.z) - }; - return optline; -} - -/// Ray-AABB intersection test, by the slab method. Highly optimized. -static inline bool -dmnsn_ray_box_intersection(dmnsn_optimized_line optline, - dmnsn_bounding_box box, double t) -{ - // This is actually correct, even though it appears not to handle edge cases - // (line.n.{x,y,z} == 0). It works because the infinities that result from - // dividing by zero will still behave correctly in the comparisons. Lines - // which are parallel to an axis and outside the box will have tmin == inf - // or tmax == -inf, while lines inside the box will have tmin and tmax - // unchanged. - - double tx1 = (box.min.x - optline.x0.x)*optline.n_inv.x; - double tx2 = (box.max.x - optline.x0.x)*optline.n_inv.x; - - double tmin = dmnsn_min(tx1, tx2); - double tmax = dmnsn_max(tx1, tx2); - - double ty1 = (box.min.y - optline.x0.y)*optline.n_inv.y; - double ty2 = (box.max.y - optline.x0.y)*optline.n_inv.y; - - tmin = dmnsn_max(tmin, dmnsn_min(ty1, ty2)); - tmax = dmnsn_min(tmax, dmnsn_max(ty1, ty2)); - - double tz1 = (box.min.z - optline.x0.z)*optline.n_inv.z; - double tz2 = (box.max.z - optline.x0.z)*optline.n_inv.z; - - tmin = dmnsn_max(tmin, dmnsn_min(tz1, tz2)); - tmax = dmnsn_min(tmax, dmnsn_max(tz1, tz2)); - - return tmax >= dmnsn_max(0.0, tmin) && tmin < t; -} - -/// The number of intersections to cache. -#define DMNSN_INTERSECTION_CACHE_SIZE 32 - -/// An array of cached intersections. -typedef struct dmnsn_intersection_cache { - size_t i; - dmnsn_object *objects[DMNSN_INTERSECTION_CACHE_SIZE]; -} dmnsn_intersection_cache; - -static dmnsn_intersection_cache * -dmnsn_get_intersection_cache(const dmnsn_bvh *bvh) -{ - dmnsn_intersection_cache *cache - = pthread_getspecific(bvh->intersection_cache); - - if (!cache) { - cache = DMNSN_MALLOC(dmnsn_intersection_cache); - cache->i = 0; - for (size_t i = 0; i < DMNSN_INTERSECTION_CACHE_SIZE; ++i) { - cache->objects[i] = NULL; - } - dmnsn_setspecific(bvh->intersection_cache, cache); - } - - return cache; -} - -/// Test for a closer object intersection than we've found so far. -static inline bool -dmnsn_closer_intersection(dmnsn_object *object, dmnsn_line ray, - dmnsn_intersection *intersection, double *t) -{ - dmnsn_intersection local_intersection; - if (dmnsn_object_intersection(object, ray, &local_intersection)) { - if (local_intersection.t < *t) { - *intersection = local_intersection; - *t = local_intersection.t; - return true; - } - } - return false; -} - -DMNSN_HOT bool -dmnsn_bvh_intersection(const dmnsn_bvh *bvh, dmnsn_line ray, - dmnsn_intersection *intersection, bool reset) -{ - double t = INFINITY; - - // Search the unbounded objects - DMNSN_ARRAY_FOREACH (dmnsn_object **, object, bvh->unbounded) { - dmnsn_closer_intersection(*object, ray, intersection, &t); - } - - // Precalculate 1.0/ray.n.{x,y,z} to save time in intersection tests - dmnsn_optimized_line optline = dmnsn_optimize_line(ray); - - // Search the intersection cache - dmnsn_intersection_cache *cache = dmnsn_get_intersection_cache(bvh); - if (dmnsn_unlikely(reset)) { - cache->i = 0; - } - dmnsn_object *cached = NULL, *found = NULL; - if (dmnsn_likely(cache->i < DMNSN_INTERSECTION_CACHE_SIZE)) { - cached = cache->objects[cache->i]; - } - if (cached && dmnsn_ray_box_intersection(optline, cached->bounding_box, t)) { - if (dmnsn_closer_intersection(cached, ray, intersection, &t)) { - found = cached; - } - } - - // Search the bounded objects - dmnsn_flat_bvh_node *node = dmnsn_array_first(bvh->bounded); - dmnsn_flat_bvh_node *last = dmnsn_array_last(bvh->bounded); - while (node <= last) { - if (dmnsn_ray_box_intersection(optline, node->bounding_box, t)) { - if (node->object && node->object != cached) { - if (dmnsn_closer_intersection(node->object, ray, intersection, &t)) { - found = node->object; - } - } - ++node; - } else { - node += node->skip; - } - } - - // Update the cache - if (dmnsn_likely(cache->i < DMNSN_INTERSECTION_CACHE_SIZE)) { - cache->objects[cache->i] = found; - ++cache->i; - } - - return !isinf(t); -} - -DMNSN_HOT bool -dmnsn_bvh_inside(const dmnsn_bvh *bvh, dmnsn_vector point) -{ - // Search the unbounded objects - DMNSN_ARRAY_FOREACH (dmnsn_object **, object, bvh->unbounded) { - if (dmnsn_object_inside(*object, point)) - return true; - } - - // Search the bounded objects - dmnsn_flat_bvh_node *node = dmnsn_array_first(bvh->bounded); - dmnsn_flat_bvh_node *last = dmnsn_array_last(bvh->bounded); - while (node <= last) { - if (dmnsn_bounding_box_contains(node->bounding_box, point)) { - if (node->object && dmnsn_object_inside(node->object, point)) { - return true; - } - ++node; - } else { - node += node->skip; - } - } - - return false; -} - -dmnsn_bounding_box -dmnsn_bvh_bounding_box(const dmnsn_bvh *bvh) -{ - if (dmnsn_array_size(bvh->unbounded) > 0) { - return dmnsn_infinite_bounding_box(); - } else if (dmnsn_array_size(bvh->bounded) > 0) { - dmnsn_flat_bvh_node *root = dmnsn_array_first(bvh->bounded); - return root->bounding_box; - } else { - return dmnsn_zero_bounding_box(); - } -} - -dmnsn_bvh_node * -dmnsn_new_bvh_node(unsigned int max_children) -{ - dmnsn_bvh_node *node = dmnsn_malloc(sizeof(dmnsn_bvh_node) + max_children*sizeof(dmnsn_bvh_node *)); - node->bounding_box = dmnsn_zero_bounding_box(); - node->object = NULL; - node->nchildren = 0; - node->max_children = max_children; - return node; -} - -dmnsn_bvh_node * -dmnsn_new_bvh_leaf_node(dmnsn_object *object) -{ - dmnsn_bvh_node *node = DMNSN_MALLOC(dmnsn_bvh_node); - node->bounding_box = object->bounding_box; - node->object = object; - node->nchildren = 0; - node->max_children = 0; - return node; -} - -void -dmnsn_delete_bvh_node(dmnsn_bvh_node *node) -{ - if (node) { - for (size_t i = 0; i < node->nchildren; ++i) { - dmnsn_delete_bvh_node(node->children[i]); - } - dmnsn_free(node); - } -} - -void -dmnsn_bvh_node_add(dmnsn_bvh_node *parent, dmnsn_bvh_node *child) -{ - dmnsn_assert(parent->nchildren < parent->max_children, - "Too many BVH children inserted."); - - parent->bounding_box.min = dmnsn_vector_min(parent->bounding_box.min, - child->bounding_box.min); - parent->bounding_box.max = dmnsn_vector_max(parent->bounding_box.max, - child->bounding_box.max); - parent->children[parent->nchildren++] = child; -} diff --git a/libdimension/bvh.h b/libdimension/bvh.h deleted file mode 100644 index f965632..0000000 --- a/libdimension/bvh.h +++ /dev/null @@ -1,74 +0,0 @@ -/************************************************************************* - * Copyright (C) 2012-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file. - * Bounding volume hierarchy. This generic interface allows different data - * structures to be represented in the same way, thus sharing code for their - * traversal algorithm. - */ - -#include - -/// A bounding volume hierarchy. -typedef struct dmnsn_bvh dmnsn_bvh; - -/// Available BVH implementations. -typedef enum dmnsn_bvh_kind { - DMNSN_BVH_NONE, - DMNSN_BVH_PRTREE, -} dmnsn_bvh_kind; - -/// Create a BVH. -DMNSN_INTERNAL dmnsn_bvh *dmnsn_new_bvh(const dmnsn_array *objects, - dmnsn_bvh_kind kind); -/// Delete a BVH. -DMNSN_INTERNAL void dmnsn_delete_bvh(dmnsn_bvh *bvh); - -/// Find the closest ray-object intersection in the tree. -DMNSN_INTERNAL bool dmnsn_bvh_intersection(const dmnsn_bvh *bvh, - dmnsn_line ray, - dmnsn_intersection *intersection, - bool reset); -/// Determine whether a point is inside any object in the tree. -DMNSN_INTERNAL bool dmnsn_bvh_inside(const dmnsn_bvh *bvh, dmnsn_vector point); -/// Return the bounding box of the whole hierarchy. -DMNSN_INTERNAL dmnsn_bounding_box dmnsn_bvh_bounding_box(const dmnsn_bvh *bvh); - -/// A non-flat BVH representation, used by BVH implementations. -typedef struct dmnsn_bvh_node { - dmnsn_bounding_box bounding_box; ///< The bounding box of this node. - dmnsn_object *object; ///< The object, for leaf nodes. - unsigned int nchildren; ///< How many children this node has. - unsigned int max_children; ///< Maximum number of children. - struct dmnsn_bvh_node *children[]; ///< Flexible array of children. -} dmnsn_bvh_node; - -/// Create a BVH node. -DMNSN_INTERNAL dmnsn_bvh_node *dmnsn_new_bvh_node(unsigned int max_children); - -/// Create a BVH leaf node. -DMNSN_INTERNAL dmnsn_bvh_node *dmnsn_new_bvh_leaf_node(dmnsn_object *object); - -/// Delete a BVH node. -DMNSN_INTERNAL void dmnsn_delete_bvh_node(dmnsn_bvh_node *node); - -/// Add a child to a BVH node. -DMNSN_INTERNAL void dmnsn_bvh_node_add(dmnsn_bvh_node *parent, dmnsn_bvh_node *child); diff --git a/libdimension/bvh/bvh.c b/libdimension/bvh/bvh.c new file mode 100644 index 0000000..eab2c28 --- /dev/null +++ b/libdimension/bvh/bvh.c @@ -0,0 +1,402 @@ +/************************************************************************* + * Copyright (C) 2012-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * BVH implementation. These are the hottest code paths in libdimension. + */ + +#include "internal/bvh.h" +#include "internal/concurrency.h" +#include "internal/prtree.h" +#include + +/// Implementation for DMNSN_BVH_NONE: just stick all objects in one node. +static dmnsn_bvh_node * +dmnsn_new_stupid_bvh(const dmnsn_array *objects) +{ + dmnsn_bvh_node *root = dmnsn_new_bvh_node(dmnsn_array_size(objects)); + + DMNSN_ARRAY_FOREACH (dmnsn_object **, object, objects) { + dmnsn_bvh_node *leaf = dmnsn_new_bvh_leaf_node(*object); + dmnsn_bvh_node_add(root, leaf); + } + + return root; +} + +// Implementation of opaque dmnsn_bvh type. +struct dmnsn_bvh { + dmnsn_array *unbounded; ///< The unbounded objects. + dmnsn_array *bounded; ///< The BVH of the bounded objects. + pthread_key_t intersection_cache; ///< The thread-local intersection cache. +}; + +/// A flat BVH node for storing in an array for fast pre-order traversal. +typedef struct dmnsn_flat_bvh_node { + dmnsn_aabb aabb; ///< The bounding box of this node. + dmnsn_object *object; ///< The referenced object, for leaf nodes. + ptrdiff_t skip; ///< Displacement to the next sibling. +} dmnsn_flat_bvh_node; + +/// Add an object or its children, if any, to an array. +static void +dmnsn_split_add_object(dmnsn_array *objects, const dmnsn_object *object) +{ + if (object->split_children) { + DMNSN_ARRAY_FOREACH (const dmnsn_object **, child, object->children) { + dmnsn_split_add_object(objects, *child); + } + } else { + dmnsn_array_push(objects, &object); + } +} + +/// Split unions to create the input for the BVH. +static dmnsn_array * +dmnsn_split_objects(const dmnsn_array *objects) +{ + dmnsn_array *split = DMNSN_NEW_ARRAY(dmnsn_object *); + DMNSN_ARRAY_FOREACH (const dmnsn_object **, object, objects) { + dmnsn_split_add_object(split, *object); + } + return split; +} + +/// Split unbounded objects into a new array. +static dmnsn_array * +dmnsn_split_unbounded(dmnsn_array *objects) +{ + dmnsn_array *unbounded = DMNSN_NEW_ARRAY(dmnsn_object *); + + dmnsn_object **array = dmnsn_array_first(objects); + size_t i, skip; + for (i = 0, skip = 0; i < dmnsn_array_size(objects); ++i) { + if (dmnsn_aabb_is_infinite(array[i]->aabb)) { + dmnsn_array_push(unbounded, &array[i]); + ++skip; + } else { + array[i - skip] = array[i]; + } + } + dmnsn_array_resize(objects, i - skip); + + return unbounded; +} + +/// Recursively flatten a BVH into an array of flat nodes. +static void +dmnsn_flatten_bvh_recursive(dmnsn_bvh_node *node, dmnsn_array *flat) +{ + size_t currenti = dmnsn_array_size(flat); + dmnsn_array_resize(flat, currenti + 1); + dmnsn_flat_bvh_node *flatnode = dmnsn_array_at(flat, currenti); + + flatnode->aabb = node->aabb; + flatnode->object = node->object; + + for (size_t i = 0; i < node->nchildren && node->children[i]; ++i) { + dmnsn_flatten_bvh_recursive(node->children[i], flat); + } + + // Array could have been realloc()'d somewhere else above + flatnode = dmnsn_array_at(flat, currenti); + flatnode->skip = dmnsn_array_size(flat) - currenti; +} + +/// Flatten a BVH into an array of flat nodes. +static dmnsn_array * +dmnsn_flatten_bvh(dmnsn_bvh_node *root) +{ + dmnsn_array *flat = DMNSN_NEW_ARRAY(dmnsn_flat_bvh_node); + if (root) { + dmnsn_flatten_bvh_recursive(root, flat); + } + return flat; +} + +dmnsn_bvh *dmnsn_new_bvh(const dmnsn_array *objects, dmnsn_bvh_kind kind) +{ + dmnsn_bvh *bvh = DMNSN_MALLOC(dmnsn_bvh); + + dmnsn_array *bounded = dmnsn_split_objects(objects); + bvh->unbounded = dmnsn_split_unbounded(bounded); + + dmnsn_bvh_node *root = NULL; + if (dmnsn_array_size(bounded) > 0) { + switch (kind) { + case DMNSN_BVH_NONE: + root = dmnsn_new_stupid_bvh(bounded); + break; + case DMNSN_BVH_PRTREE: + root = dmnsn_new_prtree(bounded); + break; + default: + dmnsn_unreachable("Invalid BVH kind."); + } + } + bvh->bounded = dmnsn_flatten_bvh(root); + + dmnsn_delete_bvh_node(root); + dmnsn_delete_array(bounded); + + dmnsn_key_create(&bvh->intersection_cache, dmnsn_free); + + return bvh; +} + +void +dmnsn_delete_bvh(dmnsn_bvh *bvh) +{ + if (bvh) { + dmnsn_free(pthread_getspecific(bvh->intersection_cache)); + dmnsn_key_delete(bvh->intersection_cache); + dmnsn_delete_array(bvh->bounded); + dmnsn_delete_array(bvh->unbounded); + dmnsn_free(bvh); + } +} + +/// A ray with pre-calculated reciprocals to avoid divisions. +typedef struct dmnsn_optimized_ray { + dmnsn_vector x0; ///< The origin of the ray. + dmnsn_vector n_inv; ///< The inverse of each component of the ray's slope +} dmnsn_optimized_ray; + +/// Precompute inverses for faster ray-box intersection tests. +static inline dmnsn_optimized_ray +dmnsn_optimize_ray(dmnsn_ray ray) +{ + dmnsn_optimized_ray optray = { + .x0 = ray.x0, + .n_inv = dmnsn_new_vector(1.0/ray.n.x, 1.0/ray.n.y, 1.0/ray.n.z) + }; + return optray; +} + +/// Ray-AABB intersection test, by the slab method. Highly optimized. +static inline bool +dmnsn_ray_box_intersection(dmnsn_optimized_ray optray, dmnsn_aabb box, double t) +{ + // This is actually correct, even though it appears not to handle edge cases + // (ray.n.{x,y,z} == 0). It works because the infinities that result from + // dividing by zero will still behave correctly in the comparisons. Rays + // which are parallel to an axis and outside the box will have tmin == inf + // or tmax == -inf, while rays inside the box will have tmin and tmax + // unchanged. + + double tx1 = (box.min.x - optray.x0.x)*optray.n_inv.x; + double tx2 = (box.max.x - optray.x0.x)*optray.n_inv.x; + + double tmin = dmnsn_min(tx1, tx2); + double tmax = dmnsn_max(tx1, tx2); + + double ty1 = (box.min.y - optray.x0.y)*optray.n_inv.y; + double ty2 = (box.max.y - optray.x0.y)*optray.n_inv.y; + + tmin = dmnsn_max(tmin, dmnsn_min(ty1, ty2)); + tmax = dmnsn_min(tmax, dmnsn_max(ty1, ty2)); + + double tz1 = (box.min.z - optray.x0.z)*optray.n_inv.z; + double tz2 = (box.max.z - optray.x0.z)*optray.n_inv.z; + + tmin = dmnsn_max(tmin, dmnsn_min(tz1, tz2)); + tmax = dmnsn_min(tmax, dmnsn_max(tz1, tz2)); + + return tmax >= dmnsn_max(0.0, tmin) && tmin < t; +} + +/// The number of intersections to cache. +#define DMNSN_INTERSECTION_CACHE_SIZE 32 + +/// An array of cached intersections. +typedef struct dmnsn_intersection_cache { + size_t i; + dmnsn_object *objects[DMNSN_INTERSECTION_CACHE_SIZE]; +} dmnsn_intersection_cache; + +static dmnsn_intersection_cache * +dmnsn_get_intersection_cache(const dmnsn_bvh *bvh) +{ + dmnsn_intersection_cache *cache + = pthread_getspecific(bvh->intersection_cache); + + if (!cache) { + cache = DMNSN_MALLOC(dmnsn_intersection_cache); + cache->i = 0; + for (size_t i = 0; i < DMNSN_INTERSECTION_CACHE_SIZE; ++i) { + cache->objects[i] = NULL; + } + dmnsn_setspecific(bvh->intersection_cache, cache); + } + + return cache; +} + +/// Test for a closer object intersection than we've found so far. +static inline bool +dmnsn_closer_intersection(dmnsn_object *object, dmnsn_ray ray, dmnsn_intersection *intersection, double *t) +{ + dmnsn_intersection local_intersection; + if (dmnsn_object_intersection(object, ray, &local_intersection)) { + if (local_intersection.t < *t) { + *intersection = local_intersection; + *t = local_intersection.t; + return true; + } + } + return false; +} + +DMNSN_HOT bool +dmnsn_bvh_intersection(const dmnsn_bvh *bvh, dmnsn_ray ray, dmnsn_intersection *intersection, bool reset) +{ + double t = INFINITY; + + // Search the unbounded objects + DMNSN_ARRAY_FOREACH (dmnsn_object **, object, bvh->unbounded) { + dmnsn_closer_intersection(*object, ray, intersection, &t); + } + + // Precalculate 1.0/ray.n.{x,y,z} to save time in intersection tests + dmnsn_optimized_ray optray = dmnsn_optimize_ray(ray); + + // Search the intersection cache + dmnsn_intersection_cache *cache = dmnsn_get_intersection_cache(bvh); + if (dmnsn_unlikely(reset)) { + cache->i = 0; + } + dmnsn_object *cached = NULL, *found = NULL; + if (dmnsn_likely(cache->i < DMNSN_INTERSECTION_CACHE_SIZE)) { + cached = cache->objects[cache->i]; + } + if (cached && dmnsn_ray_box_intersection(optray, cached->aabb, t)) { + if (dmnsn_closer_intersection(cached, ray, intersection, &t)) { + found = cached; + } + } + + // Search the bounded objects + dmnsn_flat_bvh_node *node = dmnsn_array_first(bvh->bounded); + dmnsn_flat_bvh_node *last = dmnsn_array_last(bvh->bounded); + while (node <= last) { + if (dmnsn_ray_box_intersection(optray, node->aabb, t)) { + if (node->object && node->object != cached) { + if (dmnsn_closer_intersection(node->object, ray, intersection, &t)) { + found = node->object; + } + } + ++node; + } else { + node += node->skip; + } + } + + // Update the cache + if (dmnsn_likely(cache->i < DMNSN_INTERSECTION_CACHE_SIZE)) { + cache->objects[cache->i] = found; + ++cache->i; + } + + return !isinf(t); +} + +DMNSN_HOT bool +dmnsn_bvh_inside(const dmnsn_bvh *bvh, dmnsn_vector point) +{ + // Search the unbounded objects + DMNSN_ARRAY_FOREACH (dmnsn_object **, object, bvh->unbounded) { + if (dmnsn_object_inside(*object, point)) + return true; + } + + // Search the bounded objects + dmnsn_flat_bvh_node *node = dmnsn_array_first(bvh->bounded); + dmnsn_flat_bvh_node *last = dmnsn_array_last(bvh->bounded); + while (node <= last) { + if (dmnsn_aabb_contains(node->aabb, point)) { + if (node->object && dmnsn_object_inside(node->object, point)) { + return true; + } + ++node; + } else { + node += node->skip; + } + } + + return false; +} + +dmnsn_aabb +dmnsn_bvh_aabb(const dmnsn_bvh *bvh) +{ + if (dmnsn_array_size(bvh->unbounded) > 0) { + return dmnsn_infinite_aabb(); + } else if (dmnsn_array_size(bvh->bounded) > 0) { + dmnsn_flat_bvh_node *root = dmnsn_array_first(bvh->bounded); + return root->aabb; + } else { + return dmnsn_zero_aabb(); + } +} + +dmnsn_bvh_node * +dmnsn_new_bvh_node(unsigned int max_children) +{ + dmnsn_bvh_node *node = dmnsn_malloc(sizeof(dmnsn_bvh_node) + max_children*sizeof(dmnsn_bvh_node *)); + node->aabb = dmnsn_zero_aabb(); + node->object = NULL; + node->nchildren = 0; + node->max_children = max_children; + return node; +} + +dmnsn_bvh_node * +dmnsn_new_bvh_leaf_node(dmnsn_object *object) +{ + dmnsn_bvh_node *node = DMNSN_MALLOC(dmnsn_bvh_node); + node->aabb = object->aabb; + node->object = object; + node->nchildren = 0; + node->max_children = 0; + return node; +} + +void +dmnsn_delete_bvh_node(dmnsn_bvh_node *node) +{ + if (node) { + for (size_t i = 0; i < node->nchildren; ++i) { + dmnsn_delete_bvh_node(node->children[i]); + } + dmnsn_free(node); + } +} + +void +dmnsn_bvh_node_add(dmnsn_bvh_node *parent, dmnsn_bvh_node *child) +{ + dmnsn_assert(parent->nchildren < parent->max_children, + "Too many BVH children inserted."); + + parent->aabb.min = dmnsn_vector_min(parent->aabb.min, child->aabb.min); + parent->aabb.max = dmnsn_vector_max(parent->aabb.max, child->aabb.max); + parent->children[parent->nchildren++] = child; +} diff --git a/libdimension/bvh/prtree.c b/libdimension/bvh/prtree.c new file mode 100644 index 0000000..c8e4e54 --- /dev/null +++ b/libdimension/bvh/prtree.c @@ -0,0 +1,372 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Priority R-tree implementation. + */ + +#include "internal/platform.h" +#include "internal/prtree.h" +#include "internal/concurrency.h" +#include + +/// Number of children per PR-node. +#define DMNSN_PRTREE_B 8 +/// Number of priority leaves per pseudo-PR-node (must be 2*ndimensions). +#define DMNSN_PSEUDO_B 6 + +/// The side of the split that a node ended up on. +typedef enum dmnsn_prnode_color { + DMNSN_PRTREE_LEAF, ///< Priority leaf. + DMNSN_PRTREE_LEFT, ///< Left child. + DMNSN_PRTREE_RIGHT ///< Right child. +} dmnsn_prnode_color; + +/** + * A BVH node with associated color. Compared to storing the color in the + * \p dmnsn_bvh_node itself, this method has decreased cache performance during + * sorting (due to an extra pointer chase), but increased performance during + * tree building (because it's much smaller than a full \p dmnsn_bvh_node). + * Overall it gives about a 25% improvement. + */ +typedef struct dmnsn_colored_prnode { + dmnsn_prnode_color color; + dmnsn_bvh_node *node; +} dmnsn_colored_prnode; + +/// Construct an empty PR-node. +static inline dmnsn_bvh_node * +dmnsn_new_prnode(void) +{ + return dmnsn_new_bvh_node(DMNSN_PRTREE_B); +} + +/// Comparator types. +enum { + DMNSN_XMIN, + DMNSN_YMIN, + DMNSN_ZMIN, + DMNSN_XMAX, + DMNSN_YMAX, + DMNSN_ZMAX +}; + +// List sorting comparators + +static int +dmnsn_xmin_comp(const void *l, const void *r) +{ + double lval = (*(const dmnsn_colored_prnode **)l)->node->aabb.min.x; + double rval = (*(const dmnsn_colored_prnode **)r)->node->aabb.min.x; + return (lval > rval) - (lval < rval); +} + +static int +dmnsn_ymin_comp(const void *l, const void *r) +{ + double lval = (*(const dmnsn_colored_prnode **)l)->node->aabb.min.y; + double rval = (*(const dmnsn_colored_prnode **)r)->node->aabb.min.y; + return (lval > rval) - (lval < rval); +} + +static int +dmnsn_zmin_comp(const void *l, const void *r) +{ + double lval = (*(const dmnsn_colored_prnode **)l)->node->aabb.min.z; + double rval = (*(const dmnsn_colored_prnode **)r)->node->aabb.min.z; + return (lval > rval) - (lval < rval); +} + +static int +dmnsn_xmax_comp(const void *l, const void *r) +{ + double lval = (*(const dmnsn_colored_prnode **)l)->node->aabb.max.x; + double rval = (*(const dmnsn_colored_prnode **)r)->node->aabb.max.x; + return (lval < rval) - (lval > rval); +} + +static int +dmnsn_ymax_comp(const void *l, const void *r) +{ + double lval = (*(const dmnsn_colored_prnode **)l)->node->aabb.max.y; + double rval = (*(const dmnsn_colored_prnode **)r)->node->aabb.max.y; + return (lval < rval) - (lval > rval); +} + +static int +dmnsn_zmax_comp(const void *l, const void *r) +{ + double lval = (*(const dmnsn_colored_prnode **)l)->node->aabb.max.z; + double rval = (*(const dmnsn_colored_prnode **)r)->node->aabb.max.z; + return (lval < rval) - (lval > rval); +} + +/// All comparators. +static dmnsn_array_comparator_fn *const dmnsn_comparators[DMNSN_PSEUDO_B] = { + [DMNSN_XMIN] = dmnsn_xmin_comp, + [DMNSN_YMIN] = dmnsn_ymin_comp, + [DMNSN_ZMIN] = dmnsn_zmin_comp, + [DMNSN_XMAX] = dmnsn_xmax_comp, + [DMNSN_YMAX] = dmnsn_ymax_comp, + [DMNSN_ZMAX] = dmnsn_zmax_comp, +}; + +/// Add the priority leaves for this level. +static void +dmnsn_add_priority_leaves(dmnsn_colored_prnode **sorted_leaves[DMNSN_PSEUDO_B], + size_t start, size_t end, + dmnsn_array *new_leaves) +{ + for (size_t i = 0; i < DMNSN_PSEUDO_B; ++i) { + dmnsn_bvh_node *leaf = NULL; + dmnsn_colored_prnode **leaves = sorted_leaves[i]; + + for (size_t j = start; + j < end && (!leaf || leaf->nchildren < DMNSN_PRTREE_B); + ++j) { + // Skip all the previously found extreme nodes + if (leaves[j]->color == DMNSN_PRTREE_LEAF) { + continue; + } + + if (!leaf) { + leaf = dmnsn_new_prnode(); + } + leaves[j]->color = DMNSN_PRTREE_LEAF; + dmnsn_bvh_node_add(leaf, leaves[j]->node); + } + + if (leaf) { + dmnsn_array_push(new_leaves, &leaf); + } else { + return; + } + } +} + +/// Get rid of the extreme nodes. +static void +dmnsn_filter_priority_leaves(dmnsn_colored_prnode **leaves, size_t start, size_t *endp) +{ + size_t i, skip, end; + for (i = start, skip = 0, end = *endp; i < end; ++i) { + if (leaves[i]->color == DMNSN_PRTREE_LEAF) { + ++skip; + } else { + leaves[i - skip] = leaves[i]; + } + } + + *endp -= skip; +} + +/// Split the leaves and mark the left and right child nodes. +static void +dmnsn_split_sorted_leaves_easy(dmnsn_colored_prnode **leaves, size_t start, size_t *midp, size_t end) +{ + size_t i, mid = start + (end - start + 1)/2; + for (i = start; i < mid; ++i) { + leaves[i]->color = DMNSN_PRTREE_LEFT; + } + for (; i < end; ++i) { + leaves[i]->color = DMNSN_PRTREE_RIGHT; + } + + *midp = mid; +} + +/// Split the leaves using the coloring from dmnsn_split_sorted_leaves_easy(). +static void +dmnsn_split_sorted_leaves_hard(dmnsn_colored_prnode **leaves, dmnsn_colored_prnode **buffer, size_t start, size_t end) +{ + size_t i, j, skip; + for (i = start, j = 0, skip = 0; i < end; ++i) { + if (leaves[i]->color == DMNSN_PRTREE_LEFT) { + leaves[i - skip] = leaves[i]; + } else { + if (leaves[i]->color == DMNSN_PRTREE_RIGHT) { + buffer[j] = leaves[i]; + ++j; + } + ++skip; + } + } + + size_t mid = i - skip; + for (i = 0; i < j; ++i) { + leaves[mid + i] = buffer[i]; + } +} + +/// Split the sorted lists into the left and right subtrees. +static void +dmnsn_split_sorted_leaves(dmnsn_colored_prnode **sorted_leaves[DMNSN_PSEUDO_B], + size_t start, size_t *midp, size_t *endp, + dmnsn_colored_prnode **buffer, int i) +{ + size_t orig_end = *endp; + + // Filter the extreme nodes in the ith list + dmnsn_filter_priority_leaves(sorted_leaves[i], start, endp); + + // Split the ith list + dmnsn_split_sorted_leaves_easy(sorted_leaves[i], start, midp, *endp); + + // Split the rest of the lists + for (size_t j = 0; j < DMNSN_PSEUDO_B; ++j) { + if (j == i) { + continue; + } + + dmnsn_split_sorted_leaves_hard(sorted_leaves[j], buffer, start, orig_end); + } +} + +/// Recursively constructs an implicit pseudo-PR-tree and collects the priority +/// leaves. +static void +dmnsn_priority_leaves_recursive(dmnsn_colored_prnode **sorted_leaves[DMNSN_PSEUDO_B], + size_t start, size_t end, + dmnsn_colored_prnode **buffer, + dmnsn_array *new_leaves, + int comparator) +{ + dmnsn_add_priority_leaves(sorted_leaves, start, end, new_leaves); + + size_t mid; + dmnsn_split_sorted_leaves(sorted_leaves, start, &mid, &end, buffer, comparator); + + int next = (comparator + 1)%DMNSN_PSEUDO_B; + + if (start < mid) { + dmnsn_priority_leaves_recursive(sorted_leaves, start, mid, buffer, new_leaves, next); + } + + if (mid < end) { + dmnsn_priority_leaves_recursive(sorted_leaves, mid, end, buffer, new_leaves, next); + } +} + +/// Sort each dimension in parallel with more than this many leaves. +#define DMNSN_PARALLEL_SORT_THRESHOLD 1024 + +typedef struct { + dmnsn_colored_prnode *colored_leaves; + dmnsn_colored_prnode ***sorted_leaves; + size_t nleaves; +} dmnsn_sort_leaves_payload; + +static dmnsn_colored_prnode ** +dmnsn_sort_leaf_array(dmnsn_colored_prnode *colored_leaves, size_t nleaves, int comparator) +{ + dmnsn_colored_prnode **sorted_leaves = dmnsn_malloc(nleaves*sizeof(dmnsn_colored_prnode *)); + + for (size_t i = 0; i < nleaves; ++i) { + sorted_leaves[i] = colored_leaves + i; + } + + qsort(sorted_leaves, nleaves, sizeof(dmnsn_colored_prnode *), dmnsn_comparators[comparator]); + + return sorted_leaves; +} + +static int +dmnsn_sort_leaves(void *ptr, unsigned int thread, unsigned int nthreads) +{ + dmnsn_sort_leaves_payload *payload = ptr; + + for (unsigned int i = thread; i < DMNSN_PSEUDO_B; i += nthreads) { + payload->sorted_leaves[i] = dmnsn_sort_leaf_array(payload->colored_leaves, payload->nleaves, i); + } + + return 0; +} + +/// Constructs an implicit pseudo-PR-tree and returns the priority leaves. +static dmnsn_array * +dmnsn_priority_leaves(const dmnsn_array *leaves, unsigned int nthreads) +{ + dmnsn_bvh_node **leaves_arr = dmnsn_array_first(leaves); + size_t nleaves = dmnsn_array_size(leaves); + + dmnsn_colored_prnode *colored_leaves = dmnsn_malloc(nleaves*sizeof(dmnsn_colored_prnode)); + for (size_t i = 0; i < nleaves; ++i) { + colored_leaves[i].color = DMNSN_PRTREE_LEFT; // Mustn't be _LEAF + colored_leaves[i].node = leaves_arr[i]; + } + + dmnsn_colored_prnode **sorted_leaves[DMNSN_PSEUDO_B]; + + if (nleaves >= DMNSN_PARALLEL_SORT_THRESHOLD && nthreads > 1) { + dmnsn_sort_leaves_payload payload = { + .colored_leaves = colored_leaves, + .sorted_leaves = sorted_leaves, + .nleaves = nleaves, + }; + dmnsn_execute_concurrently(NULL, dmnsn_sort_leaves, &payload, nthreads); + } else { + for (size_t i = 0; i < DMNSN_PSEUDO_B; ++i) { + sorted_leaves[i] = dmnsn_sort_leaf_array(colored_leaves, nleaves, i); + } + } + + size_t buffer_size = nleaves/2; + dmnsn_colored_prnode **buffer = dmnsn_malloc(buffer_size*sizeof(dmnsn_colored_prnode *)); + + dmnsn_array *new_leaves = DMNSN_NEW_ARRAY(dmnsn_bvh_node *); + + dmnsn_priority_leaves_recursive(sorted_leaves, 0, nleaves, buffer, new_leaves, 0); + + dmnsn_free(buffer); + for (size_t i = 0; i < DMNSN_PSEUDO_B; ++i) { + dmnsn_free(sorted_leaves[i]); + } + dmnsn_free(colored_leaves); + + return new_leaves; +} + +dmnsn_bvh_node * +dmnsn_new_prtree(const dmnsn_array *objects) +{ + if (dmnsn_array_size(objects) == 0) { + return NULL; + } + + // Make the initial array of leaves + dmnsn_array *leaves = DMNSN_NEW_ARRAY(dmnsn_bvh_node *); + DMNSN_ARRAY_FOREACH (dmnsn_object **, object, objects) { + dmnsn_bvh_node *node = dmnsn_new_bvh_leaf_node(*object); + dmnsn_array_push(leaves, &node); + } + + unsigned int ncpus = dmnsn_ncpus(); + unsigned int nthreads = ncpus < DMNSN_PSEUDO_B ? ncpus : DMNSN_PSEUDO_B; + while (dmnsn_array_size(leaves) > 1) { + dmnsn_array *new_leaves = dmnsn_priority_leaves(leaves, nthreads); + dmnsn_delete_array(leaves); + leaves = new_leaves; + } + + dmnsn_bvh_node *root = *(dmnsn_bvh_node **)dmnsn_array_first(leaves); + dmnsn_delete_array(leaves); + return root; +} diff --git a/libdimension/camera.c b/libdimension/camera.c deleted file mode 100644 index 550b41c..0000000 --- a/libdimension/camera.c +++ /dev/null @@ -1,51 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Cameras. - */ - -#include "dimension-internal.h" -#include - -// Allocate a new dummy camera -dmnsn_camera * -dmnsn_new_camera(dmnsn_pool *pool) -{ - dmnsn_camera *camera = DMNSN_PALLOC(pool, dmnsn_camera); - dmnsn_init_camera(camera); - return camera; -} - -// Initialize a camera -void -dmnsn_init_camera(dmnsn_camera *camera) -{ - camera->trans = dmnsn_identity_matrix(); -} - -// Invoke the camera ray function -dmnsn_line -dmnsn_camera_ray(const dmnsn_camera *camera, double x, double y) -{ - dmnsn_line ray = camera->ray_fn(camera, x, y); - return dmnsn_transform_line(camera->trans, ray); -} diff --git a/libdimension/canvas.c b/libdimension/canvas.c deleted file mode 100644 index 2aade47..0000000 --- a/libdimension/canvas.c +++ /dev/null @@ -1,92 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Canveses. - */ - -#include "dimension-internal.h" - -dmnsn_canvas * -dmnsn_new_canvas(dmnsn_pool *pool, size_t width, size_t height) -{ - dmnsn_canvas *canvas = DMNSN_PALLOC(pool, dmnsn_canvas); - canvas->width = width; - canvas->height = height; - canvas->optimizers = DMNSN_PALLOC_ARRAY(pool, dmnsn_canvas_optimizer *); - canvas->pixels = dmnsn_palloc(pool, sizeof(dmnsn_tcolor)*width*height); - return canvas; -} - -void -dmnsn_init_canvas_optimizer(dmnsn_canvas_optimizer *optimizer) -{ - optimizer->optimizer_fn = NULL; -} - -// Set a canvas optimizer -void -dmnsn_canvas_optimize(dmnsn_canvas *canvas, const dmnsn_canvas_optimizer *optimizer) -{ - dmnsn_array_push(canvas->optimizers, &optimizer); -} - -// Find an optimizer if it's already installed -dmnsn_canvas_optimizer * -dmnsn_canvas_find_optimizer(const dmnsn_canvas *canvas, dmnsn_canvas_optimizer_fn *optimizer_fn) -{ - DMNSN_ARRAY_FOREACH (dmnsn_canvas_optimizer **, i, canvas->optimizers) { - if ((*i)->optimizer_fn == optimizer_fn) { - return *i; - } - } - - return NULL; -} - -// Set the value of a pixel -void -dmnsn_canvas_set_pixel(dmnsn_canvas *canvas, size_t x, size_t y, - dmnsn_tcolor tcolor) -{ - dmnsn_assert(x < canvas->width && y < canvas->height, - "Canvas access out of bounds."); - dmnsn_assert(!dmnsn_tcolor_isnan(tcolor), "Pixel has NaN component."); - - // Set the pixel - canvas->pixels[y*canvas->width + x] = tcolor; - - // Call the optimizers - DMNSN_ARRAY_FOREACH (dmnsn_canvas_optimizer **, i, canvas->optimizers) { - (*i)->optimizer_fn(*i, canvas, x, y); - } -} - -// Fill a canvas with a solid color -void -dmnsn_canvas_clear(dmnsn_canvas *canvas, dmnsn_tcolor tcolor) -{ - for (size_t x = 0; x < canvas->width; ++x) { - for (size_t y = 0; y < canvas->height; ++y) { - dmnsn_canvas_set_pixel(canvas, x, y, tcolor); - } - } -} diff --git a/libdimension/canvas/canvas.c b/libdimension/canvas/canvas.c new file mode 100644 index 0000000..350cd60 --- /dev/null +++ b/libdimension/canvas/canvas.c @@ -0,0 +1,92 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Canveses. + */ + +#include "dimension/canvas.h" + +dmnsn_canvas * +dmnsn_new_canvas(dmnsn_pool *pool, size_t width, size_t height) +{ + dmnsn_canvas *canvas = DMNSN_PALLOC(pool, dmnsn_canvas); + canvas->width = width; + canvas->height = height; + canvas->optimizers = DMNSN_PALLOC_ARRAY(pool, dmnsn_canvas_optimizer *); + canvas->pixels = dmnsn_palloc(pool, sizeof(dmnsn_tcolor)*width*height); + return canvas; +} + +void +dmnsn_init_canvas_optimizer(dmnsn_canvas_optimizer *optimizer) +{ + optimizer->optimizer_fn = NULL; +} + +// Set a canvas optimizer +void +dmnsn_canvas_optimize(dmnsn_canvas *canvas, const dmnsn_canvas_optimizer *optimizer) +{ + dmnsn_array_push(canvas->optimizers, &optimizer); +} + +// Find an optimizer if it's already installed +dmnsn_canvas_optimizer * +dmnsn_canvas_find_optimizer(const dmnsn_canvas *canvas, dmnsn_canvas_optimizer_fn *optimizer_fn) +{ + DMNSN_ARRAY_FOREACH (dmnsn_canvas_optimizer **, i, canvas->optimizers) { + if ((*i)->optimizer_fn == optimizer_fn) { + return *i; + } + } + + return NULL; +} + +// Set the value of a pixel +void +dmnsn_canvas_set_pixel(dmnsn_canvas *canvas, size_t x, size_t y, + dmnsn_tcolor tcolor) +{ + dmnsn_assert(x < canvas->width && y < canvas->height, + "Canvas access out of bounds."); + dmnsn_assert(!dmnsn_tcolor_isnan(tcolor), "Pixel has NaN component."); + + // Set the pixel + canvas->pixels[y*canvas->width + x] = tcolor; + + // Call the optimizers + DMNSN_ARRAY_FOREACH (dmnsn_canvas_optimizer **, i, canvas->optimizers) { + (*i)->optimizer_fn(*i, canvas, x, y); + } +} + +// Fill a canvas with a solid color +void +dmnsn_canvas_clear(dmnsn_canvas *canvas, dmnsn_tcolor tcolor) +{ + for (size_t x = 0; x < canvas->width; ++x) { + for (size_t y = 0; y < canvas->height; ++y) { + dmnsn_canvas_set_pixel(canvas, x, y, tcolor); + } + } +} diff --git a/libdimension/canvas/gl-stubs.c b/libdimension/canvas/gl-stubs.c new file mode 100644 index 0000000..df31308 --- /dev/null +++ b/libdimension/canvas/gl-stubs.c @@ -0,0 +1,48 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Stubs for GL functions when compiled with --disable-gl. + */ + +#include "dimension.h" +#include + +int +dmnsn_gl_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas) +{ + errno = ENOSYS; + return -1; +} + +int +dmnsn_gl_write_canvas(const dmnsn_canvas *canvas) +{ + errno = ENOSYS; + return -1; +} + +int +dmnsn_gl_read_canvas(dmnsn_canvas *canvas, size_t x0, size_t y0) +{ + errno = ENOSYS; + return NULL; +} diff --git a/libdimension/canvas/gl.c b/libdimension/canvas/gl.c new file mode 100644 index 0000000..900f53c --- /dev/null +++ b/libdimension/canvas/gl.c @@ -0,0 +1,113 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * OpenGL import/export. + */ + +#include "internal/rgba.h" +#include "dimension/canvas.h" +#include +#include +#include +#include + +// Optimize canvas for GL drawing +int +dmnsn_gl_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas) +{ + dmnsn_rgba8_optimize_canvas(pool, canvas); + return 0; +} + +// Write canvas to GL framebuffer. Returns 0 on success, nonzero on failure +int +dmnsn_gl_write_canvas(const dmnsn_canvas *canvas) +{ + size_t width = canvas->width; + size_t height = canvas->height; + + // Check if we can optimize this + dmnsn_rgba8_optimizer *rgba8 = (dmnsn_rgba8_optimizer *)dmnsn_canvas_find_optimizer(canvas, dmnsn_rgba8_optimizer_fn); + if (rgba8) { + glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_BYTE, rgba8->data); + return glGetError() == GL_NO_ERROR ? 0 : 1; + } + + // We couldn't, so transform the canvas to RGB now + GLubyte *pixels = dmnsn_malloc(4*width*height*sizeof(GLubyte)); + + for (size_t y = 0; y < height; ++y) { + for (size_t x = 0; x < width; ++x) { + GLubyte *pixel = pixels + 4*(y*width + x); + + dmnsn_tcolor tcolor = dmnsn_canvas_get_pixel(canvas, x, y); + tcolor = dmnsn_tcolor_remove_filter(tcolor); + tcolor.c = dmnsn_color_to_sRGB(tcolor.c); + tcolor = dmnsn_tcolor_clamp(tcolor); + + pixel[0] = lround(tcolor.c.R*UINT8_MAX); + pixel[1] = lround(tcolor.c.G*UINT8_MAX); + pixel[2] = lround(tcolor.c.B*UINT8_MAX); + pixel[3] = lround(tcolor.T*UINT8_MAX); + } + } + + glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + dmnsn_free(pixels); + return glGetError() == GL_NO_ERROR ? 0 : 1; +} + +// Read a canvas from a GL framebuffer. Returns NULL on failure. +int +dmnsn_gl_read_canvas(dmnsn_canvas *canvas, size_t x0, size_t y0) +{ + size_t width = canvas->width; + size_t height = canvas->height; + + // Array of 16-bit ints in RGBA order + GLushort *pixels = dmnsn_malloc(4*width*height*sizeof(GLushort)); + glReadPixels(x0, y0, width, height, GL_RGBA, GL_UNSIGNED_SHORT, pixels); + if (glGetError() != GL_NO_ERROR) { + dmnsn_free(pixels); + return -1; + } + + for (size_t y = 0; y < height; ++y) { + for (size_t x = 0; x < width; ++x) { + GLushort *pixel = pixels + 4*(y*width + x); + + dmnsn_tcolor tcolor = dmnsn_new_tcolor5( + (double)pixel[0]/UINT16_MAX, + (double)pixel[1]/UINT16_MAX, + (double)pixel[2]/UINT16_MAX, + (double)pixel[3]/UINT16_MAX, + 0.0 + ); + tcolor.c = dmnsn_color_from_sRGB(tcolor.c); + dmnsn_canvas_set_pixel(canvas, x, y, tcolor); + } + } + + dmnsn_free(pixels); + return 0; +} diff --git a/libdimension/canvas/png-stubs.c b/libdimension/canvas/png-stubs.c new file mode 100644 index 0000000..9c752a5 --- /dev/null +++ b/libdimension/canvas/png-stubs.c @@ -0,0 +1,62 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Stubs for PNG functions when compiled with --disable-png. + */ + +#include "dimension.h" +#include + +int +dmnsn_png_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas) +{ + errno = ENOSYS; + return -1; +} + +int +dmnsn_png_write_canvas(const dmnsn_canvas *canvas, FILE *file) +{ + errno = ENOSYS; + return -1; +} + +dmnsn_future * +dmnsn_png_write_canvas_async(const dmnsn_canvas *canvas, FILE *file) +{ + errno = ENOSYS; + return NULL; +} + +dmnsn_canvas * +dmnsn_png_read_canvas(dmnsn_pool *pool, FILE *file) +{ + errno = ENOSYS; + return NULL; +} + +dmnsn_future * +dmnsn_png_read_canvas_async(dmnsn_canvas **canvas, dmnsn_pool *pool, FILE *file) +{ + errno = ENOSYS; + return NULL; +} diff --git a/libdimension/canvas/png.c b/libdimension/canvas/png.c new file mode 100644 index 0000000..b1d29a3 --- /dev/null +++ b/libdimension/canvas/png.c @@ -0,0 +1,393 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * PNG import/export. + */ + +#include "internal/concurrency.h" +#include "internal/platform.h" +#include "internal/rgba.h" +#include "dimension/canvas.h" +#include +#include +#include +#include +#include + +int +dmnsn_png_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas) +{ + dmnsn_rgba16_optimize_canvas(pool, canvas); + return 0; +} + +/// Payload type for PNG write thread callback. +typedef struct { + dmnsn_future *future; + const dmnsn_canvas *canvas; + FILE *file; +} dmnsn_png_write_payload; + +/// Payload type for PNG read thread callback. +typedef struct { + dmnsn_future *future; + dmnsn_canvas **canvas; + dmnsn_pool *pool; + FILE *file; +} dmnsn_png_read_payload; + +/// PNG write thread callback. +static int dmnsn_png_write_canvas_thread(void *ptr); +/// PNG read thread callback. +static int dmnsn_png_read_canvas_thread(void *ptr); + +int +dmnsn_png_write_canvas(const dmnsn_canvas *canvas, FILE *file) +{ + dmnsn_future *future = dmnsn_png_write_canvas_async(canvas, file); + return dmnsn_future_join(future); +} + +dmnsn_future * +dmnsn_png_write_canvas_async(const dmnsn_canvas *canvas, FILE *file) +{ + dmnsn_future *future = dmnsn_new_future(); + + dmnsn_png_write_payload *payload = DMNSN_MALLOC(dmnsn_png_write_payload); + payload->future = future; + payload->canvas = canvas; + payload->file = file; + + // Create the worker thread + dmnsn_new_thread(future, dmnsn_png_write_canvas_thread, payload); + + return future; +} + +// Read a canvas from the PNG file `file'. Return NULL on error. +dmnsn_canvas * +dmnsn_png_read_canvas(dmnsn_pool *pool, FILE *file) +{ + dmnsn_canvas *canvas; + dmnsn_future *future = dmnsn_png_read_canvas_async(&canvas, pool, file); + dmnsn_future_join(future); + return canvas; +} + +// Read a canvas from a png file in the background +dmnsn_future * +dmnsn_png_read_canvas_async(dmnsn_canvas **canvas, dmnsn_pool *pool, FILE *file) +{ + dmnsn_future *future = dmnsn_new_future(); + dmnsn_png_read_payload *payload = DMNSN_MALLOC(dmnsn_png_read_payload); + + payload->future = future; + payload->canvas = canvas; + *payload->canvas = NULL; + payload->pool = pool; + payload->file = file; + + // Create the worker thread + dmnsn_new_thread(future, dmnsn_png_read_canvas_thread, payload); + + return future; +} + +////////////////////// +// Thread callbacks // +////////////////////// + +// Write a PNG file +static int +dmnsn_png_write_canvas_thread(void *ptr) +{ + dmnsn_png_write_payload *payload = ptr; + + png_uint_32 width = payload->canvas->width; + png_uint_32 height = payload->canvas->height; + + dmnsn_future_set_total(payload->future, height); + + png_structp png_ptr + = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) { + // Couldn't create libpng write struct + dmnsn_free(payload); + return -1; + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + // Couldn't create libpng info struct + png_destroy_write_struct(&png_ptr, NULL); + dmnsn_free(payload); + return -1; + } + + // libpng will longjmp here if it encounters an error from here on + uint16_t *row = NULL; + if (setjmp(png_jmpbuf(png_ptr))) { + // libpng error + dmnsn_free(row); + png_destroy_write_struct(&png_ptr, &info_ptr); + dmnsn_free(payload); + return -1; + } + + // Associate file with the libpng write struct + png_init_io(png_ptr, payload->file); + + // Set header correctly for 16-bit sRGB image + png_set_IHDR(png_ptr, info_ptr, width, height, 16, + PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE); + + // We think of transparency in the opposite way that PNG does + png_set_invert_alpha(png_ptr); + + // Write the info struct + png_write_info(png_ptr, info_ptr); + + if (dmnsn_is_little_endian()) { + // We are little-endian; swap the byte order of the pixels + png_set_swap(png_ptr); + } + + // Check if we can optimize this + dmnsn_rgba16_optimizer *rgba16 = (dmnsn_rgba16_optimizer *)dmnsn_canvas_find_optimizer(payload->canvas, dmnsn_rgba16_optimizer_fn); + if (rgba16) { + for (size_t y = 0; y < height; ++y) { + // Invert the rows. PNG coordinates are fourth quadrant. + uint16_t *row_opt = rgba16->data + 4*(height - y - 1)*width; + png_write_row(png_ptr, (png_bytep)row_opt); + dmnsn_future_increment(payload->future); + } + + // Finish the PNG file + png_write_end(png_ptr, info_ptr); + png_destroy_write_struct(&png_ptr, &info_ptr); + dmnsn_free(payload); + return 0; + } + + // Allocate the temporary row of RGBA values + row = dmnsn_malloc(4*sizeof(uint16_t)*width); + + // Write the pixels + for (size_t y = 0; y < height; ++y) { + for (size_t x = 0; x < width; ++x) { + // Invert the rows. PNG coordinates are fourth quadrant. + dmnsn_tcolor tcolor = dmnsn_canvas_get_pixel(payload->canvas, + x, height - y - 1); + tcolor = dmnsn_tcolor_remove_filter(tcolor); + tcolor.c = dmnsn_color_to_sRGB(tcolor.c); + tcolor = dmnsn_tcolor_clamp(tcolor); + + row[4*x] = lround(tcolor.c.R*UINT16_MAX); + row[4*x + 1] = lround(tcolor.c.G*UINT16_MAX); + row[4*x + 2] = lround(tcolor.c.B*UINT16_MAX); + row[4*x + 3] = lround(tcolor.T*UINT16_MAX); + } + + // Write the row + png_write_row(png_ptr, (png_bytep)row); + dmnsn_future_increment(payload->future); + } + + // Finish the PNG file + png_write_end(png_ptr, info_ptr); + + dmnsn_free(row); + png_destroy_write_struct(&png_ptr, &info_ptr); + dmnsn_free(payload); + return 0; +} + +/// Thread-specific pointer to the appropriate dmnsn_future* for +/// dmnsn_png_read_row_callback. +static __thread dmnsn_future *dmnsn_tl_png_read_future; + +/// Callback to increment the progress after a row has been read. +static void +dmnsn_png_read_row_callback(png_structp png_ptr, png_uint_32 row, int pass) +{ + dmnsn_future_increment(dmnsn_tl_png_read_future); +} + +// Read a PNG file +static int +dmnsn_png_read_canvas_thread(void *ptr) +{ + dmnsn_png_read_payload *payload = ptr; + dmnsn_tl_png_read_future = payload->future; + + png_byte header[8]; + if (fread(header, 1, 8, payload->file) != 8) { + dmnsn_free(payload); + return -1; + } + if (png_sig_cmp(header, 0, 8)) { + // payload->file is not a PNG file + dmnsn_free(payload); + errno = EINVAL; + return -1; + } + + // Create the libpng read struct + png_structp png_ptr + = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) { + dmnsn_free(payload); + return -1; + } + + // Create the libpng info struct + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, NULL, NULL); + dmnsn_free(payload); + return -1; + } + + // libpng will longjmp here if it encounters an error from here on + png_bytep image = NULL; + png_bytep *row_pointers = NULL; + if (setjmp(png_jmpbuf(png_ptr))) { + // libpng error + dmnsn_free(row_pointers); + dmnsn_free(image); + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + dmnsn_free(payload); + return -1; + } + + // Associate the read struct with the file, and tell it we've already checked + // 8 bytes of signature + png_init_io(png_ptr, payload->file); + png_set_sig_bytes(png_ptr, 8); + + // Read the PNG header into info struct + png_read_info(png_ptr, info_ptr); + + // Get useful information from the info struct + png_uint_32 width, height; + int bit_depth, color_type, interlace_type, compression_type, filter_method; + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, + &interlace_type, &compression_type, &filter_method); + int number_of_passes = png_set_interlace_handling(png_ptr); + + dmnsn_future_set_total(payload->future, (number_of_passes + 1)*height); + png_set_read_status_fn(png_ptr, dmnsn_png_read_row_callback); + + // - Convert paletted images to RGB. + // - Convert a tRNS chunk to an alpha channel + // - Convert grayscale to RGB + // - Invert the alpha channel + if (color_type == PNG_COLOR_TYPE_PALETTE) { + png_set_palette_to_rgb(png_ptr); + } + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { + png_set_tRNS_to_alpha(png_ptr); + } + if (color_type == PNG_COLOR_TYPE_GRAY + || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + png_set_gray_to_rgb(png_ptr); + } + png_set_invert_alpha(png_ptr); + + // Update the info struct + png_read_update_info(png_ptr, info_ptr); + + // Get bytes/image row + png_uint_32 rowbytes = png_get_rowbytes(png_ptr, info_ptr); + + // Allocate the temporary image buffer + image = dmnsn_malloc(rowbytes*height); + + // Allocate and set an array of pointers to rows in image + row_pointers = dmnsn_malloc(sizeof(png_bytep)*height); + + for (size_t y = 0; y < height; ++y) { + row_pointers[y] = image + y*rowbytes; + } + + // Read the image to memory all at once. At the expense of greater memory + // use, this handles interlacing for us. + png_read_image(png_ptr, row_pointers); + + // Allocate the canvas + *payload->canvas = dmnsn_new_canvas(payload->pool, width, height); + + // Now we convert the image to our canvas format. This depends on the image + // bit depth (which has been scaled up to at least 8 or 16), and the presence + // of an alpha channel. + for (size_t y = 0; y < height; ++y) { + for (size_t x = 0; x < width; ++x) { + dmnsn_tcolor tcolor; + tcolor.F = 0.0; + + if (color_type & PNG_COLOR_MASK_ALPHA) { + if (bit_depth == 16) { + png_bytep png_pixel = image + 8*(y*width + x); + tcolor.c.R = (double)((png_pixel[0] << 8) + png_pixel[1])/UINT16_MAX; + tcolor.c.G = (double)((png_pixel[2] << 8) + png_pixel[3])/UINT16_MAX; + tcolor.c.B = (double)((png_pixel[4] << 8) + png_pixel[5])/UINT16_MAX; + tcolor.T = (double)((png_pixel[6] << 8) + png_pixel[7])/UINT16_MAX; + } else { + png_bytep png_pixel = image + 4*(y*width + x); + tcolor.c.R = (double)png_pixel[0]/UINT8_MAX; + tcolor.c.G = (double)png_pixel[1]/UINT8_MAX; + tcolor.c.B = (double)png_pixel[2]/UINT8_MAX; + tcolor.T = (double)png_pixel[3]/UINT8_MAX; + } + } else { + tcolor.T = 0.0; + + if (bit_depth == 16) { + png_bytep png_pixel = image + 6*(y*width + x); + tcolor.c.R = (double)((png_pixel[0] << 8) + png_pixel[1])/UINT16_MAX; + tcolor.c.G = (double)((png_pixel[2] << 8) + png_pixel[3])/UINT16_MAX; + tcolor.c.B = (double)((png_pixel[4] << 8) + png_pixel[5])/UINT16_MAX; + } else { + png_bytep png_pixel = image + 3*(y*width + x); + tcolor.c.R = (double)png_pixel[0]/UINT8_MAX; + tcolor.c.G = (double)png_pixel[1]/UINT8_MAX; + tcolor.c.B = (double)png_pixel[2]/UINT8_MAX; + } + } + + tcolor.c = dmnsn_color_from_sRGB(tcolor.c); + dmnsn_canvas_set_pixel(*payload->canvas, x, height - y - 1, tcolor); + } + + dmnsn_future_increment(payload->future); + } + + dmnsn_free(row_pointers); + dmnsn_free(image); + png_read_end(png_ptr, NULL); + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + dmnsn_free(payload); + return 0; +} diff --git a/libdimension/canvas/rgba.c b/libdimension/canvas/rgba.c new file mode 100644 index 0000000..4abf457 --- /dev/null +++ b/libdimension/canvas/rgba.c @@ -0,0 +1,95 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * 16-bit RGBA canvas optimizer. + */ + +#include "internal/rgba.h" +#include + +void +dmnsn_rgba8_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas) +{ + if (dmnsn_canvas_find_optimizer(canvas, dmnsn_rgba8_optimizer_fn)) { + return; + } + + size_t ndata = 4*canvas->width*canvas->height; + dmnsn_rgba8_optimizer *rgba8 = dmnsn_palloc(pool, sizeof(dmnsn_rgba8_optimizer) + ndata*sizeof(uint8_t)); + + dmnsn_canvas_optimizer *optimizer = &rgba8->optimizer; + dmnsn_init_canvas_optimizer(optimizer); + optimizer->optimizer_fn = dmnsn_rgba8_optimizer_fn; + + dmnsn_canvas_optimize(canvas, optimizer); +} + +void +dmnsn_rgba16_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas) +{ + if (dmnsn_canvas_find_optimizer(canvas, dmnsn_rgba16_optimizer_fn)) { + return; + } + + size_t ndata = 4*canvas->width*canvas->height; + dmnsn_rgba16_optimizer *rgba16 = dmnsn_palloc(pool, sizeof(dmnsn_rgba16_optimizer) + ndata*sizeof(uint16_t)); + + dmnsn_canvas_optimizer *optimizer = &rgba16->optimizer; + dmnsn_init_canvas_optimizer(optimizer); + optimizer->optimizer_fn = dmnsn_rgba16_optimizer_fn; + + dmnsn_canvas_optimize(canvas, optimizer); +} + +void +dmnsn_rgba8_optimizer_fn(dmnsn_canvas_optimizer *optimizer, const dmnsn_canvas *canvas, size_t x, size_t y) +{ + dmnsn_rgba8_optimizer *rgba8 = (dmnsn_rgba8_optimizer *)optimizer; + + uint8_t *pixel = rgba8->data + 4*(y*canvas->width + x); + dmnsn_tcolor tcolor = dmnsn_canvas_get_pixel(canvas, x, y); + tcolor = dmnsn_tcolor_remove_filter(tcolor); + tcolor.c = dmnsn_color_to_sRGB(tcolor.c); + tcolor = dmnsn_tcolor_clamp(tcolor); + + pixel[0] = lround(tcolor.c.R*UINT8_MAX); + pixel[1] = lround(tcolor.c.G*UINT8_MAX); + pixel[2] = lround(tcolor.c.B*UINT8_MAX); + pixel[3] = lround(tcolor.T*UINT8_MAX); +} + +void +dmnsn_rgba16_optimizer_fn(dmnsn_canvas_optimizer *optimizer, const dmnsn_canvas *canvas, size_t x, size_t y) +{ + dmnsn_rgba16_optimizer *rgba16 = (dmnsn_rgba16_optimizer *)optimizer; + + uint16_t *pixel = rgba16->data + 4*(y*canvas->width + x); + dmnsn_tcolor tcolor = dmnsn_canvas_get_pixel(canvas, x, y); + tcolor = dmnsn_tcolor_remove_filter(tcolor); + tcolor.c = dmnsn_color_to_sRGB(tcolor.c); + tcolor = dmnsn_tcolor_clamp(tcolor); + + pixel[0] = lround(tcolor.c.R*UINT16_MAX); + pixel[1] = lround(tcolor.c.G*UINT16_MAX); + pixel[2] = lround(tcolor.c.B*UINT16_MAX); + pixel[3] = lround(tcolor.T*UINT16_MAX); +} diff --git a/libdimension/canvas_pigment.c b/libdimension/canvas_pigment.c deleted file mode 100644 index 2b1e2b6..0000000 --- a/libdimension/canvas_pigment.c +++ /dev/null @@ -1,57 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Image maps. - */ - -#include "dimension.h" - -/// Canvas pigment type. -typedef struct dmnsn_canvas_pigment { - dmnsn_pigment pigment; - dmnsn_canvas *canvas; -} dmnsn_canvas_pigment; - -/// Canvas pigment color callback. -static dmnsn_tcolor -dmnsn_canvas_pigment_fn(const dmnsn_pigment *pigment, dmnsn_vector v) -{ - const dmnsn_canvas_pigment *canvas_pigment = (const dmnsn_canvas_pigment *)pigment; - dmnsn_canvas *canvas = canvas_pigment->canvas; - - size_t x = llround((fmod(v.x, 1.0) + 1.0)*(canvas->width - 1)); - size_t y = llround((fmod(v.y, 1.0) + 1.0)*(canvas->height - 1)); - return dmnsn_canvas_get_pixel(canvas, x%canvas->width, y%canvas->height); -} - -// Create a canvas color -dmnsn_pigment * -dmnsn_new_canvas_pigment(dmnsn_pool *pool, dmnsn_canvas *canvas) -{ - dmnsn_canvas_pigment *canvas_pigment = DMNSN_PALLOC(pool, dmnsn_canvas_pigment); - canvas_pigment->canvas = canvas; - - dmnsn_pigment *pigment = &canvas_pigment->pigment; - dmnsn_init_pigment(pigment); - pigment->pigment_fn = dmnsn_canvas_pigment_fn; - return pigment; -} diff --git a/libdimension/checker.c b/libdimension/checker.c deleted file mode 100644 index 740abe8..0000000 --- a/libdimension/checker.c +++ /dev/null @@ -1,63 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Checker pattern. - */ - -#include "dimension-internal.h" - -/// Checker pattern callback. -static double -dmnsn_checker_pattern_fn(const dmnsn_pattern *checker, dmnsn_vector v) -{ - double xmod = fmod(v.x, 2.0); - double ymod = fmod(v.y, 2.0); - double zmod = fmod(v.z, 2.0); - - if (xmod < -dmnsn_epsilon) - xmod += 2.0; - if (ymod < -dmnsn_epsilon) - ymod += 2.0; - if (zmod < -dmnsn_epsilon) - zmod += 2.0; - - // Return 0 when an even number of coordinates are in [0, 1), 1 otherwise - unsigned int n = 0; - if (xmod >= 1.0) - ++n; - if (ymod >= 1.0) - ++n; - if (zmod >= 1.0) - ++n; - return (n%2 == 0) ? 0.0 : 1.0; -} - -/// The singleton instance. -static dmnsn_pattern dmnsn_checker_instance = { - .pattern_fn = dmnsn_checker_pattern_fn, -}; - -dmnsn_pattern * -dmnsn_new_checker_pattern(dmnsn_pool *pool) -{ - return &dmnsn_checker_instance; -} diff --git a/libdimension/compiler-internal.h b/libdimension/compiler-internal.h deleted file mode 100644 index 40e749d..0000000 --- a/libdimension/compiler-internal.h +++ /dev/null @@ -1,92 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Internally-used compiler abstractions. - */ - -#include - -/** - * @def dmnsn_likely - * Indicate that a test is likely to succeed. - * @param test The test to perform. - * @return The truth value of \p test. - */ -/** - * @def dmnsn_unlikely - * Indicate that a test is unlikely to succeed. - * @param test The test to perform. - * @return The truth value of \p test. - */ -#ifdef DMNSN_PROFILE - #define dmnsn_likely(test) \ - dmnsn_expect(!!(test), true, DMNSN_FUNC, __FILE__, __LINE__) - #define dmnsn_unlikely(test) \ - dmnsn_expect(!!(test), false, DMNSN_FUNC, __FILE__, __LINE__) -#elif DMNSN_GNUC - #define dmnsn_likely(test) __builtin_expect(!!(test), true) - #define dmnsn_unlikely(test) __builtin_expect(!!(test), false) -#else - #define dmnsn_likely(test) (!!(test)) - #define dmnsn_unlikely(test) (!!(test)) -#endif - -/** - * @def DMNSN_HOT - * Mark a function as a hot path. - */ -/** - * @def DMNSN_INTERNAL - * Mark a function as internal linkage. - */ -/** - * @def DMNSN_DESTRUCTOR - * Queue a function to run at program termination. - */ -/** - * @def DMNSN_LATE_DESTRUCTOR - * Queue a function to run at program termination, after those labeled - * DMNSN_DESTRUCTOR. - */ -#if DMNSN_GNUC - #define DMNSN_HOT __attribute__((hot)) - #define DMNSN_INTERNAL __attribute__((visibility("hidden"))) - #define DMNSN_DESTRUCTOR __attribute__((destructor(102))) - #define DMNSN_LATE_DESTRUCTOR __attribute__((destructor(101))) -#else - #define DMNSN_HOT - #define DMNSN_INTERNAL - #define DMNSN_DESTRUCTOR - #define DMNSN_LATE_DESTRUCTOR -#endif - -/// Synonym for _Atomic that stdatomic.h doesn't define for some reason -#define atomic _Atomic - -/// C11-compliant alloca variant -#define DMNSN_ALLOCA(var, size) DMNSN_ALLOCA_IMPL(var, size, __LINE__) - -#define DMNSN_ALLOCA_IMPL(var, size, ctr) DMNSN_ALLOCA_IMPL2(var, size, ctr) - -#define DMNSN_ALLOCA_IMPL2(var, size, ctr) \ - alignas(max_align_t) char dmnsn_alloca##ctr[size]; \ - var = (void *)dmnsn_alloca##ctr diff --git a/libdimension/concurrency/future.c b/libdimension/concurrency/future.c new file mode 100644 index 0000000..90ffa24 --- /dev/null +++ b/libdimension/concurrency/future.c @@ -0,0 +1,281 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Future objects. + */ + +#include "internal.h" +#include "internal/concurrency.h" +#include "internal/future.h" +#include + +/** + * Since C doesn't support anything like C++'s mutable, we fake it by casting + * away the constness. This is okay since all valid dmnsn_futures live on the + * heap, so cannot be const. + */ +#define MUTATE(future) ((dmnsn_future *)(future)) + +// Allocate a new dmnsn_future* +dmnsn_future * +dmnsn_new_future(void) +{ + dmnsn_future *future = DMNSN_MALLOC(dmnsn_future); + future->progress = 0; + future->total = 1; + + dmnsn_initialize_mutex(&future->mutex); + dmnsn_initialize_cond(&future->cond); + + future->min_wait = 1.0; + + future->nthreads = future->nrunning = 1; + future->npaused = 0; + dmnsn_initialize_cond(&future->none_running_cond); + dmnsn_initialize_cond(&future->all_running_cond); + dmnsn_initialize_cond(&future->resume_cond); + + return future; +} + +static void +dmnsn_delete_future(dmnsn_future *future) +{ + if (future) { + dmnsn_destroy_cond(&future->resume_cond); + dmnsn_destroy_cond(&future->all_running_cond); + dmnsn_destroy_cond(&future->none_running_cond); + dmnsn_destroy_cond(&future->cond); + dmnsn_destroy_mutex(&future->mutex); + dmnsn_free(future); + } +} + +// Join the worker thread and delete `future'. +int +dmnsn_future_join(dmnsn_future *future) +{ + void *ptr; + int retval = -1; + + if (future) { + dmnsn_assert(future->npaused == 0, "Attempt to join future while paused"); + + // Get the thread's return value + dmnsn_join_thread(future->thread, &ptr); + if (ptr && ptr != PTHREAD_CANCELED) { + retval = *(int *)ptr; + dmnsn_free(ptr); + } + + // Free the future object + dmnsn_delete_future(future); + } + + return retval; +} + +// Cancel a background thread +void +dmnsn_future_cancel(dmnsn_future *future) +{ + pthread_cancel(future->thread); +} + +/** + * Get the current progress, without locking anything. + * + * future->mutex must be locked for this call to be safe. + */ +static inline double +dmnsn_future_progress_unlocked(const dmnsn_future *future) +{ + return (double)future->progress/future->total; +} + +// Get the current progress of the worker thread, in [0.0, 1.0] +double +dmnsn_future_progress(const dmnsn_future *future) +{ + dmnsn_future *mfuture = MUTATE(future); + double progress; + + dmnsn_lock_mutex(&mfuture->mutex); + progress = dmnsn_future_progress_unlocked(mfuture); + dmnsn_unlock_mutex(&mfuture->mutex); + + return progress; +} + +// Find out whether the task is complete. +bool +dmnsn_future_is_done(const dmnsn_future *future) +{ + dmnsn_future *mfuture = MUTATE(future); + bool result; + + dmnsn_lock_mutex(&mfuture->mutex); + result = future->progress == future->total; + dmnsn_unlock_mutex(&mfuture->mutex); + + return result; +} + +// Wait until dmnsn_future_progress(future) >= progress +void +dmnsn_future_wait(const dmnsn_future *future, double progress) +{ + dmnsn_future *mfuture = MUTATE(future); + + dmnsn_lock_mutex(&mfuture->mutex); + while (dmnsn_future_progress_unlocked(mfuture) < progress) { + // Set the minimum waited-on value + if (progress < mfuture->min_wait) { + mfuture->min_wait = progress; + } + + dmnsn_cond_wait_safely(&mfuture->cond, &mfuture->mutex); + } + dmnsn_unlock_mutex(&mfuture->mutex); +} + +// Pause all threads working on a future. +void +dmnsn_future_pause(dmnsn_future *future) +{ + dmnsn_lock_mutex(&future->mutex); + while (future->nrunning < future->nthreads) { + dmnsn_cond_wait_safely(&future->all_running_cond, &future->mutex); + } + ++future->npaused; + while (future->nrunning > 0) { + dmnsn_cond_wait_safely(&future->none_running_cond, &future->mutex); + } + dmnsn_unlock_mutex(&future->mutex); +} + +// Resume all threads working on a future. +void +dmnsn_future_resume(dmnsn_future *future) +{ + dmnsn_lock_mutex(&future->mutex); + dmnsn_assert(future->npaused > 0, "dmnsn_future_resume() without matching dmnsn_future_pause()"); + if (--future->npaused == 0) { + dmnsn_cond_broadcast(&future->resume_cond); + } + dmnsn_unlock_mutex(&future->mutex); +} + +// Set the total number of loop iterations +void +dmnsn_future_set_total(dmnsn_future *future, size_t total) +{ + dmnsn_lock_mutex(&future->mutex); + future->total = total; + dmnsn_unlock_mutex(&future->mutex); +} + +static void +dmnsn_future_increment_cleanup(void *ptr) +{ + dmnsn_future *future = ptr; + ++future->nrunning; + dmnsn_unlock_mutex_impl(&future->mutex); +} + +// Increment the number of completed loop iterations +void +dmnsn_future_increment(dmnsn_future *future) +{ + // Allow a thread to be canceled whenever it increments a future object -- + // this is close to PTHREAD_CANCEL_ASYNCHRONOUS but allows consistent state + // on cancellation + pthread_testcancel(); + + dmnsn_lock_mutex(&future->mutex); + ++future->progress; + + if (dmnsn_future_progress_unlocked(future) >= future->min_wait) { + future->min_wait = 1.0; + dmnsn_cond_broadcast(&future->cond); + } + + if (future->npaused > 0) { + dmnsn_assert(future->nrunning > 0, "More worker threads than expected"); + + if (--future->nrunning == 0) { + dmnsn_cond_broadcast(&future->none_running_cond); + } + + pthread_cleanup_push(dmnsn_future_increment_cleanup, future); + do { + dmnsn_cond_wait(&future->resume_cond, &future->mutex); + } while (future->npaused > 0); + pthread_cleanup_pop(false); + + if (++future->nrunning == future->nthreads) { + dmnsn_cond_broadcast(&future->all_running_cond); + } + } + dmnsn_unlock_mutex(&future->mutex); +} + +// Immediately set to 100% completion +void +dmnsn_future_finish(dmnsn_future *future) +{ + dmnsn_lock_mutex(&future->mutex); + future->progress = future->total; + future->nthreads = future->nrunning = 0; + dmnsn_cond_broadcast(&future->cond); + dmnsn_cond_broadcast(&future->none_running_cond); + dmnsn_cond_broadcast(&future->all_running_cond); + dmnsn_unlock_mutex(&future->mutex); +} + +// Set the number of threads +void +dmnsn_future_set_nthreads(dmnsn_future *future, unsigned int nthreads) +{ + dmnsn_lock_mutex(&future->mutex); + dmnsn_assert(future->nrunning == future->nthreads, + "dmnsn_future_set_nthreads() called with paused threads"); + future->nthreads = future->nrunning = nthreads; + dmnsn_unlock_mutex(&future->mutex); +} + +// Notify completion of a worker thread +void +dmnsn_future_finish_thread(dmnsn_future *future) +{ + dmnsn_lock_mutex(&future->mutex); + dmnsn_assert(future->nthreads > 0, + "dmnsn_future_finish_thread() called with no threads"); + --future->nthreads; + + dmnsn_assert(future->nrunning > 0, + "dmnsn_future_finish_thread() called with no running threads"); + if (--future->nrunning == 0) { + dmnsn_cond_broadcast(&future->none_running_cond); + } + dmnsn_unlock_mutex(&future->mutex); +} diff --git a/libdimension/concurrency/threads.c b/libdimension/concurrency/threads.c new file mode 100644 index 0000000..93d2ea9 --- /dev/null +++ b/libdimension/concurrency/threads.c @@ -0,0 +1,326 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Background threading. + */ + +#include "internal.h" +#include "internal/concurrency.h" +#include "internal/future.h" +#include + +/// The payload to pass to the pthread callback. +typedef struct dmnsn_thread_payload { + dmnsn_thread_fn *thread_fn; + void *arg; + dmnsn_future *future; +} dmnsn_thread_payload; + +/// Clean up after a thread. +static void +dmnsn_thread_cleanup(void *arg) +{ + dmnsn_thread_payload *payload = arg; + dmnsn_future *future = payload->future; + dmnsn_free(payload); + + dmnsn_future_finish(future); +} + +/// pthread callback -- call the real thread callback. +static void * +dmnsn_thread(void *arg) +{ + dmnsn_thread_payload *payload = arg; + int *ret; + + pthread_cleanup_push(dmnsn_thread_cleanup, payload); + ret = DMNSN_MALLOC(int); + *ret = payload->thread_fn(payload->arg); + pthread_cleanup_pop(true); + return ret; +} + +void +dmnsn_new_thread(dmnsn_future *future, dmnsn_thread_fn *thread_fn, void *arg) +{ + dmnsn_thread_payload *payload = DMNSN_MALLOC(dmnsn_thread_payload); + payload->thread_fn = thread_fn; + payload->arg = arg; + payload->future = future; + + if (pthread_create(&future->thread, NULL, dmnsn_thread, payload) != 0) { + dmnsn_error("Couldn't start thread."); + } +} + +/// Payload for threads executed by dmnsn_execute_concurrently(). +typedef struct dmnsn_ccthread_payload { + dmnsn_future *future; + dmnsn_ccthread_fn *ccthread_fn; + void *arg; + unsigned int thread, nthreads; + int ret; + bool running; +} dmnsn_ccthread_payload; + +static void * +dmnsn_concurrent_thread(void *ptr) +{ + dmnsn_ccthread_payload *payload = ptr; + payload->ret = payload->ccthread_fn(payload->arg, payload->thread, + payload->nthreads); + if (payload->future) { + dmnsn_future_finish_thread(payload->future); + } + return NULL; +} + +typedef struct dmnsn_ccthread_cleanup_payload { + dmnsn_future *future; + pthread_t *threads; + dmnsn_ccthread_payload *payloads; + unsigned int nthreads; +} dmnsn_ccthread_cleanup_payload; + +static void +dmnsn_ccthread_cleanup(void *ptr) +{ + dmnsn_ccthread_cleanup_payload *payload = ptr; + + for (unsigned int i = 0; i < payload->nthreads; ++i) { + if (payload->payloads[i].running) { + pthread_cancel(payload->threads[i]); + } + } + + for (unsigned int i = 0; i < payload->nthreads; ++i) { + if (payload->payloads[i].running) { + dmnsn_join_thread(payload->threads[i], NULL); + } + } + + if (payload->future) { + dmnsn_future_set_nthreads(payload->future, 1); + } +} + +int +dmnsn_execute_concurrently(dmnsn_future *future, dmnsn_ccthread_fn *ccthread_fn, + void *arg, unsigned int nthreads) +{ + dmnsn_assert(nthreads > 0, "Attempt to execute using 0 concurrent threads."); + + if (future) { + dmnsn_future_set_nthreads(future, nthreads); + } + + pthread_t threads[nthreads]; + dmnsn_ccthread_payload payloads[nthreads]; + for (unsigned int i = 0; i < nthreads; ++i) { + payloads[i].running = false; + } + + int ret = 0; + dmnsn_ccthread_cleanup_payload cleanup_payload = { + .future = future, + .threads = threads, + .payloads = payloads, + .nthreads = nthreads, + }; + pthread_cleanup_push(dmnsn_ccthread_cleanup, &cleanup_payload); + for (unsigned int i = 0; i < nthreads; ++i) { + payloads[i].future = future; + payloads[i].ccthread_fn = ccthread_fn; + payloads[i].arg = arg; + payloads[i].thread = i; + payloads[i].nthreads = nthreads; + payloads[i].ret = -1; + if (pthread_create(&threads[i], NULL, dmnsn_concurrent_thread, + &payloads[i]) != 0) + { + dmnsn_error("Couldn't start worker thread."); + } + payloads[i].running = true; + } + + for (unsigned int i = 0; i < nthreads; ++i) { + dmnsn_join_thread(threads[i], NULL); + payloads[i].running = false; + if (payloads[i].ret != 0) { + ret = payloads[i].ret; + } + } + pthread_cleanup_pop(false); + + if (future) { + dmnsn_future_set_nthreads(future, 1); + } + + return ret; +} + +// pthread wrappers + +void +dmnsn_initialize_mutex(pthread_mutex_t *mutex) +{ + if (pthread_mutex_init(mutex, NULL) != 0) { + dmnsn_error("Couldn't initialize mutex."); + } +} + +void +dmnsn_lock_mutex_impl(pthread_mutex_t *mutex) +{ + if (pthread_mutex_lock(mutex) != 0) { + dmnsn_error("Couldn't lock mutex."); + } +} + +void +dmnsn_unlock_mutex_impl(void *mutex) +{ + if (pthread_mutex_unlock(mutex) != 0) { + dmnsn_error("Couldn't unlock mutex."); + } +} + +void +dmnsn_destroy_mutex(pthread_mutex_t *mutex) +{ + if (pthread_mutex_destroy(mutex) != 0) { + dmnsn_warning("Couldn't destroy mutex."); + } +} + +void +dmnsn_initialize_rwlock(pthread_rwlock_t *rwlock) +{ + if (pthread_rwlock_init(rwlock, NULL) != 0) { + dmnsn_error("Couldn't initialize read-write lock."); + } +} + +void +dmnsn_read_lock_impl(pthread_rwlock_t *rwlock) +{ + if (pthread_rwlock_rdlock(rwlock) != 0) { + dmnsn_error("Couldn't acquire read lock."); + } +} + +void +dmnsn_write_lock_impl(pthread_rwlock_t *rwlock) +{ + if (pthread_rwlock_wrlock(rwlock) != 0) { + dmnsn_error("Couldn't acquire write lock."); + } +} + +void +dmnsn_unlock_rwlock_impl(pthread_rwlock_t *rwlock) +{ + if (pthread_rwlock_unlock(rwlock) != 0) { + dmnsn_error("Couldn't unlock read-write lock."); + } +} + +void +dmnsn_destroy_rwlock(pthread_rwlock_t *rwlock) +{ + if (pthread_rwlock_destroy(rwlock) != 0) { + dmnsn_warning("Couldn't destroy read-write lock."); + } +} + +void +dmnsn_initialize_cond(pthread_cond_t *cond) +{ + if (pthread_cond_init(cond, NULL) != 0) { + dmnsn_error("Couldn't initialize condition variable."); + } +} + +void +dmnsn_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + if (pthread_cond_wait(cond, mutex) != 0) { + dmnsn_error("Couldn't wait on condition variable."); + } +} + +void +dmnsn_cond_broadcast(pthread_cond_t *cond) +{ + if (pthread_cond_broadcast(cond) != 0) { + dmnsn_error("Couldn't signal condition variable."); + } +} + +void +dmnsn_destroy_cond(pthread_cond_t *cond) +{ + if (pthread_cond_destroy(cond) != 0) { + dmnsn_warning("Couldn't destroy condition variable."); + } +} + +void +dmnsn_once(pthread_once_t *once, dmnsn_once_fn *once_fn) +{ + if (pthread_once(once, once_fn) != 0) { + dmnsn_error("Couldn't call one-shot function."); + } +} + +void +dmnsn_key_create(pthread_key_t *key, dmnsn_callback_fn *destructor) +{ + if (pthread_key_create(key, destructor) != 0) { + dmnsn_error("Couldn't initialize thread-specific pointer."); + } +} + +void +dmnsn_setspecific(pthread_key_t key, const void *value) +{ + if (pthread_setspecific(key, value) != 0) { + dmnsn_error("Couldn't set thread-specific pointer."); + } +} + +void +dmnsn_key_delete(pthread_key_t key) +{ + if (pthread_key_delete(key) != 0) { + dmnsn_warning("Couldn't destroy thread-specific pointer."); + } +} + +void +dmnsn_join_thread(pthread_t thread, void **retval) +{ + if (pthread_join(thread, retval) != 0) { + dmnsn_error("Couldn't join thread."); + } +} diff --git a/libdimension/cone.c b/libdimension/cone.c deleted file mode 100644 index 1e95b0a..0000000 --- a/libdimension/cone.c +++ /dev/null @@ -1,206 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Cones/cylinders. - */ - -#include "dimension.h" -#include - -/// Cone type. -typedef struct dmnsn_cone { - dmnsn_object object; - double r1, r2; -} dmnsn_cone; - -/// Intersection callback for a cone. -static bool -dmnsn_cone_intersection_fn(const dmnsn_object *object, dmnsn_line l, - dmnsn_intersection *intersection) -{ - const dmnsn_cone *cone = (const dmnsn_cone *)object; - double r1 = cone->r1, r2 = cone->r2; - - // Solve (x0 + nx*t)^2 + (z0 + nz*t)^2 == (((r2 - r1)*(y0 + ny*t) + r1 + r2)/2)^2 - double poly[3], x[2]; - poly[2] = l.n.x*l.n.x + l.n.z*l.n.z - l.n.y*l.n.y*(r2 - r1)*(r2 - r1)/4.0; - poly[1] = 2.0*(l.n.x*l.x0.x + l.n.z*l.x0.z) - - l.n.y*(r2 - r1)*(l.x0.y*(r2 - r1) + r2 + r1)/2.0; - poly[0] = l.x0.x*l.x0.x + l.x0.z*l.x0.z - - (l.x0.y*(r2 - r1) + r2 + r1)*(l.x0.y*(r2 - r1) + r2 + r1)/4.0; - - size_t n = dmnsn_polynomial_solve(poly, 2, x); - - if (n > 0) { - double t = x[0]; - dmnsn_vector p; - if (n == 2) { - t = dmnsn_min(t, x[1]); - p = dmnsn_line_point(l, t); - - if (p.y <= -1.0 || p.y >= 1.0) { - t = dmnsn_max(x[0], x[1]); - p = dmnsn_line_point(l, t); - } - } else { - p = dmnsn_line_point(l, t); - } - - if (t >= 0.0 && p.y >= -1.0 && p.y <= 1.0) { - double r = ((r2 - r1)*p.y + r1 + r2)/2.0; - dmnsn_vector norm = dmnsn_new_vector(p.x, -r*(r2 - r1)/2.0, p.z); - intersection->t = t; - intersection->normal = norm; - return true; - } - } - - return false; -} - -/// Inside callback for a cone. -static bool -dmnsn_cone_inside_fn(const dmnsn_object *object, dmnsn_vector point) -{ - const dmnsn_cone *cone = (const dmnsn_cone *)object; - double r1 = cone->r1, r2 = cone->r2; - double r = (point.y*(r2 - r1) + r1 + r2)/2.0; - return point.x*point.x + point.z*point.z < r*r - && point.y > -1.0 && point.y < 1.0; -} - -/// Cone bounding callback. -static dmnsn_bounding_box -dmnsn_cone_bounding_fn(const dmnsn_object *object, dmnsn_matrix trans) -{ - const dmnsn_cone *cone = (const dmnsn_cone *)object; - - double rmax = dmnsn_max(cone->r1, cone->r2); - dmnsn_bounding_box box = dmnsn_symmetric_bounding_box(dmnsn_new_vector(rmax, 1.0, rmax)); - return dmnsn_transform_bounding_box(trans, box); -} - -/// Cone vtable. -static const dmnsn_object_vtable dmnsn_cone_vtable = { - .intersection_fn = dmnsn_cone_intersection_fn, - .inside_fn = dmnsn_cone_inside_fn, - .bounding_fn = dmnsn_cone_bounding_fn, -}; - -/// Cone cap type. -typedef struct dmnsn_cone_cap { - dmnsn_object object; - double r; -} dmnsn_cone_cap; - -/// Cone cap intersection function. -static bool -dmnsn_cone_cap_intersection_fn(const dmnsn_object *object, dmnsn_line l, - dmnsn_intersection *intersection) -{ - if (l.n.y != 0.0) { - const dmnsn_cone_cap *cap = (const dmnsn_cone_cap *)object; - double r = cap->r; - double t = -l.x0.y/l.n.y; - dmnsn_vector p = dmnsn_line_point(l, t); - if (t >= 0.0 && p.x*p.x + p.z*p.z <= r*r) { - intersection->t = t; - intersection->normal = dmnsn_new_vector(0.0, -1.0, 0.0); - return true; - } - } - - return false; -} - -/// Inside callback for a cone cap. -static bool -dmnsn_cone_cap_inside_fn(const dmnsn_object *object, dmnsn_vector point) -{ - return false; -} - -/// Cone cap bounding callback. -static dmnsn_bounding_box -dmnsn_cone_cap_bounding_fn(const dmnsn_object *object, dmnsn_matrix trans) -{ - const dmnsn_cone_cap *cap = (const dmnsn_cone_cap *)object; - dmnsn_bounding_box box = dmnsn_symmetric_bounding_box(dmnsn_new_vector(cap->r, 0.0, cap->r)); - return dmnsn_transform_bounding_box(trans, box); -} - -/// Cone cap vtable. -static const dmnsn_object_vtable dmnsn_cone_cap_vtable = { - .intersection_fn = dmnsn_cone_cap_intersection_fn, - .inside_fn = dmnsn_cone_cap_inside_fn, - .bounding_fn = dmnsn_cone_cap_bounding_fn, -}; - -/// Allocate a new cone cap. -dmnsn_object * -dmnsn_new_cone_cap(dmnsn_pool *pool, double r) -{ - dmnsn_cone_cap *cap = DMNSN_PALLOC(pool, dmnsn_cone_cap); - cap->r = r; - - dmnsn_object *object = &cap->object; - dmnsn_init_object(object); - object->vtable = &dmnsn_cone_cap_vtable; - return object; -} - -// Allocate a new cone object -dmnsn_object * -dmnsn_new_cone(dmnsn_pool *pool, double r1, double r2, bool open) -{ - dmnsn_cone *cone = DMNSN_PALLOC(pool, dmnsn_cone); - cone->r1 = r1; - cone->r2 = r2; - - dmnsn_object *object = &cone->object; - dmnsn_init_object(object); - object->vtable = &dmnsn_cone_vtable; - - if (open) { - return object; - } - - // Implement closed cones as a union with the caps - dmnsn_object *cap1 = dmnsn_new_cone_cap(pool, r1); - dmnsn_object *cap2 = dmnsn_new_cone_cap(pool, r2); - cap1->intrinsic_trans = dmnsn_translation_matrix( - dmnsn_new_vector(0.0, -1.0, 0.0) - ); - cap2->intrinsic_trans = dmnsn_translation_matrix( - dmnsn_new_vector(0.0, +1.0, 0.0) - ); - // Flip the normal around for the top cap - cap2->intrinsic_trans.n[1][1] = -1.0; - - dmnsn_array *withcaps = DMNSN_PALLOC_ARRAY(pool, dmnsn_object *); - dmnsn_array_push(withcaps, &cone); - dmnsn_array_push(withcaps, &cap1); - dmnsn_array_push(withcaps, &cap2); - dmnsn_object *cone_cap_union = dmnsn_new_csg_union(pool, withcaps); - - return cone_cap_union; -} diff --git a/libdimension/csg.c b/libdimension/csg.c deleted file mode 100644 index e31cb0e..0000000 --- a/libdimension/csg.c +++ /dev/null @@ -1,332 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Constructive solid geometry. - */ - -#include "dimension-internal.h" -#include - -//////////// -// Unions // -//////////// - -typedef struct { - dmnsn_object object; - dmnsn_bvh *bvh; -} dmnsn_csg_union; - -/// CSG union intersection callback. -static bool -dmnsn_csg_union_intersection_fn(const dmnsn_object *object, - dmnsn_line line, - dmnsn_intersection *intersection) -{ - const dmnsn_csg_union *csg = (const dmnsn_csg_union *)object; - return dmnsn_bvh_intersection(csg->bvh, line, intersection, true); -} - -/// CSG union inside callback. -static bool -dmnsn_csg_union_inside_fn(const dmnsn_object *object, dmnsn_vector point) -{ - const dmnsn_csg_union *csg = (const dmnsn_csg_union *)object; - return dmnsn_bvh_inside(csg->bvh, point); -} - -/// CSG union precomputation callback. -static void -dmnsn_csg_union_precompute_fn(dmnsn_object *object) -{ - dmnsn_csg_union *csg = (dmnsn_csg_union *)object; - csg->object.trans_inv = dmnsn_identity_matrix(); - - dmnsn_bvh *bvh = dmnsn_new_bvh(csg->object.children, DMNSN_BVH_PRTREE); - csg->bvh = bvh; - csg->object.bounding_box = dmnsn_bvh_bounding_box(bvh); -} - -/// CSG union vtable. -static const dmnsn_object_vtable dmnsn_csg_union_vtable = { - .intersection_fn = dmnsn_csg_union_intersection_fn, - .inside_fn = dmnsn_csg_union_inside_fn, - .precompute_fn = dmnsn_csg_union_precompute_fn, -}; - -/// CSG union destruction callback. -static void -dmnsn_csg_union_cleanup(void *ptr) -{ - dmnsn_csg_union *csg = ptr; - dmnsn_delete_bvh(csg->bvh); -} - -// Bulk-load a union -dmnsn_object * -dmnsn_new_csg_union(dmnsn_pool *pool, dmnsn_array *objects) -{ - dmnsn_csg_union *csg = DMNSN_PALLOC_TIDY(pool, dmnsn_csg_union, dmnsn_csg_union_cleanup); - csg->bvh = NULL; - - dmnsn_object *object = &csg->object; - dmnsn_init_object(object); - - object->vtable = &dmnsn_csg_union_vtable; - object->children = objects; - object->split_children = true; - - return object; -} - -/** - * Generic CSG intersection callback. - * @param[in] csg The CSG object. - * @param[in] line The intersection ray. - * @param[out] intersection The intersection data. - * @param[in] inside1 Whether the first object is allowed inside the - * second object. - * @param[in] inside2 Whether the second object is allowed inside the - * first object. - * @return Whether \p line intersected \p csg. - */ -static bool -dmnsn_csg_intersection_fn(const dmnsn_object *csg, dmnsn_line line, - dmnsn_intersection *intersection, - bool inside1, bool inside2) -{ - const dmnsn_object *A = *(dmnsn_object **)dmnsn_array_first(csg->children); - const dmnsn_object *B = *(dmnsn_object **)dmnsn_array_last(csg->children); - - dmnsn_intersection i1, i2; - bool is_i1 = dmnsn_object_intersection(A, line, &i1); - bool is_i2 = dmnsn_object_intersection(B, line, &i2); - - double oldt = 0.0; - while (is_i1) { - i1.ray = line; - i1.t += oldt; - oldt = i1.t + dmnsn_epsilon; - - dmnsn_vector point = dmnsn_line_point(i1.ray, i1.t); - if (inside2 ^ dmnsn_object_inside(B, point)) { - dmnsn_line newline = line; - newline.x0 = dmnsn_line_point(line, i1.t); - newline = dmnsn_line_add_epsilon(newline); - is_i1 = dmnsn_object_intersection(A, newline, &i1); - } else { - break; - } - } - - oldt = 0.0; - while (is_i2) { - i2.ray = line; - i2.t += oldt; - oldt = i2.t + dmnsn_epsilon; - - dmnsn_vector point = dmnsn_line_point(i2.ray, i2.t); - if (inside1 ^ dmnsn_object_inside(A, point)) { - dmnsn_line newline = line; - newline.x0 = dmnsn_line_point(line, i2.t); - newline = dmnsn_line_add_epsilon(newline); - is_i2 = dmnsn_object_intersection(B, newline, &i2); - } else { - break; - } - } - - if (is_i1 && is_i2) { - if (i1.t < i2.t) { - *intersection = i1; - } else { - *intersection = i2; - } - } else if (is_i1) { - *intersection = i1; - } else if (is_i2) { - *intersection = i2; - } else { - return false; - } - - return true; -} - -/////////////////// -// Intersections // -/////////////////// - -/// CSG intersection intersection callback. -static bool -dmnsn_csg_intersection_intersection_fn(const dmnsn_object *csg, - dmnsn_line line, - dmnsn_intersection *intersection) -{ - return dmnsn_csg_intersection_fn(csg, line, intersection, true, true); -} - -/// CSG intersection inside callback. -static bool -dmnsn_csg_intersection_inside_fn(const dmnsn_object *csg, dmnsn_vector point) -{ - const dmnsn_object *A = *(dmnsn_object **)dmnsn_array_first(csg->children); - const dmnsn_object *B = *(dmnsn_object **)dmnsn_array_last(csg->children); - return dmnsn_object_inside(A, point) && dmnsn_object_inside(B, point); -} - -/// CSG intersection precomputation callback. -static void -dmnsn_csg_intersection_precompute_fn(dmnsn_object *csg) -{ - dmnsn_object *A = *(dmnsn_object **)dmnsn_array_first(csg->children); - dmnsn_object *B = *(dmnsn_object **)dmnsn_array_last(csg->children); - - csg->trans_inv = dmnsn_identity_matrix(); - csg->bounding_box.min = dmnsn_vector_max(A->bounding_box.min, B->bounding_box.min); - csg->bounding_box.max = dmnsn_vector_min(A->bounding_box.max, B->bounding_box.max); -} - -/// CSG intersection vtable. -static const dmnsn_object_vtable dmnsn_csg_intersection_vtable = { - .intersection_fn = dmnsn_csg_intersection_intersection_fn, - .inside_fn = dmnsn_csg_intersection_inside_fn, - .precompute_fn = dmnsn_csg_intersection_precompute_fn, -}; - -dmnsn_object * -dmnsn_new_csg_intersection(dmnsn_pool *pool, dmnsn_object *A, dmnsn_object *B) -{ - dmnsn_object *csg = dmnsn_new_object(pool); - csg->vtable = &dmnsn_csg_intersection_vtable; - - csg->children = DMNSN_PALLOC_ARRAY(pool, dmnsn_object *); - dmnsn_array_push(csg->children, &A); - dmnsn_array_push(csg->children, &B); - - return csg; -} - -///////////////// -// Differences // -///////////////// - -/// CSG difference intersection callback. -static bool -dmnsn_csg_difference_intersection_fn(const dmnsn_object *csg, - dmnsn_line line, - dmnsn_intersection *intersection) -{ - return dmnsn_csg_intersection_fn(csg, line, intersection, true, false); -} - -/// CSG difference inside callback. -static bool -dmnsn_csg_difference_inside_fn(const dmnsn_object *csg, dmnsn_vector point) -{ - const dmnsn_object *A = *(dmnsn_object **)dmnsn_array_first(csg->children); - const dmnsn_object *B = *(dmnsn_object **)dmnsn_array_last(csg->children); - return dmnsn_object_inside(A, point) && !dmnsn_object_inside(B, point); -} - -/// CSG difference precomputation callback. -static void -dmnsn_csg_difference_precompute_fn(dmnsn_object *csg) -{ - dmnsn_object *A = *(dmnsn_object **)dmnsn_array_first(csg->children); - - csg->trans_inv = dmnsn_identity_matrix(); - csg->bounding_box = A->bounding_box; -} - -/// CSG difference vtable. -static const dmnsn_object_vtable dmnsn_csg_difference_vtable = { - .intersection_fn = dmnsn_csg_difference_intersection_fn, - .inside_fn = dmnsn_csg_difference_inside_fn, - .precompute_fn = dmnsn_csg_difference_precompute_fn, -}; - -dmnsn_object * -dmnsn_new_csg_difference(dmnsn_pool *pool, dmnsn_object *A, dmnsn_object *B) -{ - dmnsn_object *csg = dmnsn_new_object(pool); - csg->vtable = &dmnsn_csg_difference_vtable; - - csg->children = DMNSN_PALLOC_ARRAY(pool, dmnsn_object *); - dmnsn_array_push(csg->children, &A); - dmnsn_array_push(csg->children, &B); - - return csg; -} - -//////////// -// Merges // -//////////// - -/// CSG merge intersection callback. -static bool -dmnsn_csg_merge_intersection_fn(const dmnsn_object *csg, - dmnsn_line line, - dmnsn_intersection *intersection) -{ - return dmnsn_csg_intersection_fn(csg, line, intersection, false, false); -} - -/// CSG merge inside callback. -static bool -dmnsn_csg_merge_inside_fn(const dmnsn_object *csg, dmnsn_vector point) -{ - const dmnsn_object *A = *(dmnsn_object **)dmnsn_array_first(csg->children); - const dmnsn_object *B = *(dmnsn_object **)dmnsn_array_last(csg->children); - return dmnsn_object_inside(A, point) || dmnsn_object_inside(B, point); -} - -/// CSG merge precomputation callback. -static void -dmnsn_csg_merge_precompute_fn(dmnsn_object *csg) -{ - dmnsn_object *A = *(dmnsn_object **)dmnsn_array_first(csg->children); - dmnsn_object *B = *(dmnsn_object **)dmnsn_array_last(csg->children); - - csg->trans_inv = dmnsn_identity_matrix(); - csg->bounding_box.min = dmnsn_vector_min(A->bounding_box.min, B->bounding_box.min); - csg->bounding_box.max = dmnsn_vector_max(A->bounding_box.max, B->bounding_box.max); -} - -/// CSG merge vtable. -static const dmnsn_object_vtable dmnsn_csg_merge_vtable = { - .intersection_fn = dmnsn_csg_merge_intersection_fn, - .inside_fn = dmnsn_csg_merge_inside_fn, - .precompute_fn = dmnsn_csg_merge_precompute_fn, -}; - -dmnsn_object * -dmnsn_new_csg_merge(dmnsn_pool *pool, dmnsn_object *A, dmnsn_object *B) -{ - dmnsn_object *csg = dmnsn_new_object(pool); - csg->vtable = &dmnsn_csg_merge_vtable; - - csg->children = DMNSN_PALLOC_ARRAY(pool, dmnsn_object *); - dmnsn_array_push(csg->children, &A); - dmnsn_array_push(csg->children, &B); - - return csg; -} diff --git a/libdimension/cube.c b/libdimension/cube.c deleted file mode 100644 index c455467..0000000 --- a/libdimension/cube.c +++ /dev/null @@ -1,154 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Cubes. - */ - -#include "dimension.h" -#include - -/// Intersection callback for a cube. -static bool -dmnsn_cube_intersection_fn(const dmnsn_object *cube, dmnsn_line line, - dmnsn_intersection *intersection) -{ - // Clip the given line against the X, Y, and Z slabs - - dmnsn_vector nmin, nmax; - double tmin, tmax; - - double tx1 = (-1.0 - line.x0.x)/line.n.x; - double tx2 = (+1.0 - line.x0.x)/line.n.x; - - if (tx1 < tx2) { - tmin = tx1; - tmax = tx2; - nmin = dmnsn_new_vector(-1.0, 0.0, 0.0); - nmax = dmnsn_new_vector(+1.0, 0.0, 0.0); - } else { - tmin = tx2; - tmax = tx1; - nmin = dmnsn_new_vector(+1.0, 0.0, 0.0); - nmax = dmnsn_new_vector(-1.0, 0.0, 0.0); - } - - if (tmin > tmax) - return false; - - double ty1 = (-1.0 - line.x0.y)/line.n.y; - double ty2 = (+1.0 - line.x0.y)/line.n.y; - - if (ty1 < ty2) { - if (ty1 > tmin) { - tmin = ty1; - nmin = dmnsn_new_vector(0.0, -1.0, 0.0); - } - if (ty2 < tmax) { - tmax = ty2; - nmax = dmnsn_new_vector(0.0, +1.0, 0.0); - } - } else { - if (ty2 > tmin) { - tmin = ty2; - nmin = dmnsn_new_vector(0.0, +1.0, 0.0); - } - if (ty1 < tmax) { - tmax = ty1; - nmax = dmnsn_new_vector(0.0, -1.0, 0.0); - } - } - - if (tmin > tmax) - return false; - - double tz1 = (-1.0 - line.x0.z)/line.n.z; - double tz2 = (+1.0 - line.x0.z)/line.n.z; - - if (tz1 < tz2) { - if (tz1 > tmin) { - tmin = tz1; - nmin = dmnsn_new_vector(0.0, 0.0, -1.0); - } - if (tz2 < tmax) { - tmax = tz2; - nmax = dmnsn_new_vector(0.0, 0.0, +1.0); - } - } else { - if (tz2 > tmin) { - tmin = tz2; - nmin = dmnsn_new_vector(0.0, 0.0, +1.0); - } - if (tz1 < tmax) { - tmax = tz1; - nmax = dmnsn_new_vector(0.0, 0.0, -1.0); - } - } - - if (tmin > tmax) - return false; - - if (tmin < 0.0) { - tmin = tmax; - nmin = nmax; - } - - if (tmin >= 0.0) { - intersection->t = tmin; - intersection->normal = nmin; - return true; - } else { - return false; - } -} - -/// Inside callback for a cube. -static bool -dmnsn_cube_inside_fn(const dmnsn_object *cube, dmnsn_vector point) -{ - return point.x > -1.0 && point.x < 1.0 - && point.y > -1.0 && point.y < 1.0 - && point.z > -1.0 && point.z < 1.0; -} - -/// Boundary callback for a cube. -static dmnsn_bounding_box -dmnsn_cube_bounding_fn(const dmnsn_object *object, dmnsn_matrix trans) -{ - dmnsn_bounding_box box = dmnsn_symmetric_bounding_box(dmnsn_new_vector(1.0, 1.0, 1.0)); - return dmnsn_transform_bounding_box(trans, box); -} - -/// Cube vtable. -static const dmnsn_object_vtable dmnsn_cube_vtable = { - .intersection_fn = dmnsn_cube_intersection_fn, - .inside_fn = dmnsn_cube_inside_fn, - .bounding_fn = dmnsn_cube_bounding_fn, -}; - -// Allocate a new cube object -dmnsn_object * -dmnsn_new_cube(dmnsn_pool *pool) -{ - dmnsn_object *cube = dmnsn_new_object(pool); - cube->vtable = &dmnsn_cube_vtable; - return cube; -} diff --git a/libdimension/dictionary.c b/libdimension/dictionary.c deleted file mode 100644 index 84ebedf..0000000 --- a/libdimension/dictionary.c +++ /dev/null @@ -1,228 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Associative arrays, implemented with PATRICIA tries. - */ - -#include "dimension.h" - -struct dmnsn_dictionary { - size_t obj_size; ///< The size of the objects in the trie. - char *prefix; ///< The local string prefix of the current node. - void *value; ///< The node's stored object, if it's a leaf. - dmnsn_array *children; ///< The node's children. -}; - -dmnsn_dictionary * -dmnsn_new_dictionary(size_t obj_size) -{ - dmnsn_dictionary *dict = DMNSN_MALLOC(dmnsn_dictionary); - dict->obj_size = obj_size; - dict->prefix = dmnsn_strdup(""); - dict->value = NULL; - dict->children = DMNSN_NEW_ARRAY(dmnsn_dictionary *); - return dict; -} - -void -dmnsn_delete_dictionary(dmnsn_dictionary *dict) -{ - if (dict) { - dmnsn_free(dict->prefix); - dmnsn_free(dict->value); - DMNSN_ARRAY_FOREACH (dmnsn_dictionary **, subtrie, dict->children) { - dmnsn_delete_dictionary(*subtrie); - } - dmnsn_delete_array(dict->children); - - dmnsn_free(dict); - } -} - -bool -dmnsn_dictionary_get(const dmnsn_dictionary *dict, const char *key, void *obj) -{ - const void *value = dmnsn_dictionary_at(dict, key); - if (value) { - memcpy(obj, value, dict->obj_size); - return true; - } else { - return false; - } -} - -void * -dmnsn_dictionary_at(const dmnsn_dictionary *dict, const char *key) -{ - // PATRICIA trie search: O(k), where k is the length of the longest string in - // the trie. - - size_t len = strlen(dict->prefix); - if (strncmp(key, dict->prefix, len) != 0) - return NULL; - key += len; - - while (true) { - if (*key == '\0' && dict->value) { - return dict->value; - } else { - dmnsn_dictionary **first = dmnsn_array_first(dict->children), **subtrie; - ptrdiff_t size = dmnsn_array_size(dict->children); - for (subtrie = first; subtrie - first < size; ++subtrie) { - len = strlen((*subtrie)->prefix); - if (strncmp(key, (*subtrie)->prefix, len) == 0) { - dict = *subtrie; - key += len; - break; - } - } - - if (subtrie - first == size) - break; - } - } - - return NULL; -} - -void -dmnsn_dictionary_insert(dmnsn_dictionary *dict, const char *key, - const void *obj) -{ - // PATRICIA trie insertion: O(k), where k is the length of the longest string - // in the trie. - - while (true) { - if (dict->prefix[0] == '\0' && !dict->value - && dmnsn_array_size(dict->children) == 0) - { - // Replace an empty tree with a single-element tree - dict->prefix = dmnsn_realloc(dict->prefix, strlen(key) + 1); - strcpy(dict->prefix, key); - - dict->value = dmnsn_malloc(dict->obj_size); - memcpy(dict->value, obj, dict->obj_size); - break; - } - - char *prefix = dict->prefix; - while (*prefix == *key && *prefix && *key) { - ++prefix; - ++key; - } - - if (*key == '\0' && *prefix == '\0') { - // Complete match - if (!dict->value) { - dict->value = dmnsn_malloc(dict->obj_size); - } - memcpy(dict->value, obj, dict->obj_size); - break; - } else if (*prefix == '\0') { - // Partial match; key starts with prefix - dmnsn_dictionary **first = dmnsn_array_first(dict->children), **subtrie; - ptrdiff_t size = dmnsn_array_size(dict->children); - for (subtrie = first; subtrie - first < size; ++subtrie) { - if ((*subtrie)->prefix[0] == key[0]) { - dict = *subtrie; - break; - } - } - - if (subtrie - first == size) { - // No submatch found, add a new child - dmnsn_dictionary *child = dmnsn_new_dictionary(dict->obj_size); - dmnsn_array_push(dict->children, &child); - dict = child; - } - } else { - // Split the tree - dmnsn_dictionary *copy = dmnsn_new_dictionary(dict->obj_size); - copy->prefix = dmnsn_realloc(copy->prefix, strlen(prefix) + 1); - strcpy(copy->prefix, prefix); - *prefix = '\0'; - - copy->value = dict->value; - dict->value = NULL; - - dmnsn_array *temp = copy->children; - copy->children = dict->children; - dict->children = temp; - - dmnsn_dictionary *subtrie = dmnsn_new_dictionary(dict->obj_size); - dmnsn_array_push(dict->children, ©); - dmnsn_array_push(dict->children, &subtrie); - dict = subtrie; - } - } -} - -bool -dmnsn_dictionary_remove(dmnsn_dictionary *dict, const char *key) -{ - // PATRICIA trie removal: O(k), where k is the length of the longest string - // in the trie. - - // This implementation doesn't actually collapse the tree back upwards if a - // node is left with only one child, to reduce complexity. - - size_t len = strlen(dict->prefix); - if (strncmp(key, dict->prefix, len) != 0) - return false; - key += len; - - while (true) { - if (*key == '\0' && dict->value) { - dmnsn_free(dict->value); - dict->value = NULL; - return true; - } else { - dmnsn_dictionary **first = dmnsn_array_first(dict->children), **subtrie; - ptrdiff_t size = dmnsn_array_size(dict->children); - for (subtrie = first; subtrie - first < size; ++subtrie) { - len = strlen((*subtrie)->prefix); - if (strncmp(key, (*subtrie)->prefix, len) == 0) { - dict = *subtrie; - key += len; - break; - } - } - - if (subtrie - first == size) - break; - } - } - - return false; -} - -void -dmnsn_dictionary_apply(dmnsn_dictionary *dict, dmnsn_callback_fn *callback) -{ - if (dict->value) { - callback(dict->value); - } - - DMNSN_ARRAY_FOREACH (dmnsn_dictionary **, subtrie, dict->children) { - dmnsn_dictionary_apply(*subtrie, callback); - } -} diff --git a/libdimension/dimension-internal.h b/libdimension/dimension-internal.h deleted file mode 100644 index 53fa24c..0000000 --- a/libdimension/dimension-internal.h +++ /dev/null @@ -1,40 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * The internal libdimension API. These functions and types are used to - * implement libdimension, but are not part of its public API. - */ - -#ifndef DIMENSION_INTERNAL_H -#define DIMENSION_INTERNAL_H - -#include "dimension.h" -#include "compiler-internal.h" -#include "profile.h" -#include "platform.h" -#include "future-internal.h" -#include "threads.h" -#include "bvh.h" -#include "prtree.h" -#include "rgba.h" - -#endif // DIMENSION_INTERNAL_H diff --git a/libdimension/dimension.h b/libdimension/dimension.h index fc3a681..db67a48 100644 --- a/libdimension/dimension.h +++ b/libdimension/dimension.h @@ -22,6 +22,9 @@ * @file * The main \#include file for the Dimension library. This file declares all * of its public functions and types. + * + * To import only a subset of the libdimension's functionality, you can include + * the headers for submodules directly. */ /** @@ -31,55 +34,18 @@ * all rendering-related tasks for Dimension. */ -#ifndef DIMENSION_H -#define DIMENSION_H - -/* Include compiler.h first for DMNSN_CXX */ -#include +#ifndef DMNSN_H +#define DMNSN_H -#if DMNSN_CXX -/* We've been included from a C++ file; mark everything here as extern "C" */ -extern "C" { -#endif - -/* Include all the libdimension headers */ -#include -#include -#include -#include -#include -#include -#include -#include +/* Include all modules. */ +#include +#include +#include #include -#include -#include #include -#include #include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if DMNSN_CXX -} -#endif +#include +#include -#endif /* DIMENSION_H */ +#endif /* DMNSN_H */ diff --git a/libdimension/dimension/array.h b/libdimension/dimension/array.h deleted file mode 100644 index 9261a0e..0000000 --- a/libdimension/dimension/array.h +++ /dev/null @@ -1,357 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Simple dynamic arrays. - */ - -#include /* For size_t */ -#include /* For qsort() */ -#include /* For memcpy() */ - -/** Dynamic array type. */ -typedef struct dmnsn_array { - void *ptr; /**< @internal The actual memory. */ - size_t obj_size; /**< @internal The size of each object. */ - size_t length; /**< @internal The current size of the array. */ - size_t capacity; /**< @internal The size of the allocated space. */ -} dmnsn_array; - -/** - * @internal - * Initialize a new array. - * @param[out] array The array to initialize. - * @param[in] obj_size The size of the objects to store in the array. - */ -DMNSN_INLINE void -dmnsn_init_array(dmnsn_array *array, size_t obj_size) -{ - array->obj_size = obj_size; - array->length = 0; - array->capacity = 2; /* Start with capacity of 2 */ - - /* Allocate the memory */ - array->ptr = dmnsn_malloc(array->capacity*array->obj_size); -} - -/** - * Allocate an array. - * @param[in] obj_size The size of the objects to store in the array. - * @return An empty array. - */ -DMNSN_INLINE dmnsn_array * -dmnsn_new_array(size_t obj_size) -{ - dmnsn_array *array = DMNSN_MALLOC(dmnsn_array); - dmnsn_init_array(array, obj_size); - return array; -} - -/** - * Allocate an array. - * @param[in] type Type type of element to store in the array. - * @return An empty array. - */ -#define DMNSN_NEW_ARRAY(type) (dmnsn_new_array(sizeof(type))) - -/** - * Delete an array. - * @param[in,out] array The array to delete. - */ -DMNSN_INLINE void -dmnsn_delete_array(dmnsn_array *array) -{ - if (array) { - dmnsn_free(array->ptr); - dmnsn_free(array); - } -} - -/** - * @internal - * Free a pool-allocated array. - * @param[in,out] ptr The array to clean up. - */ -void dmnsn_array_cleanup(void *ptr); - -/** - * Allocate an array from a pool. - * @param[in] pool The memory pool to allocate from. - * @param[in] obj_size The size of the objects to store in the array. - * @return An empty array. - */ -DMNSN_INLINE dmnsn_array * -dmnsn_palloc_array(dmnsn_pool *pool, size_t obj_size) -{ - dmnsn_array *array = DMNSN_PALLOC_TIDY(pool, dmnsn_array, dmnsn_array_cleanup); - dmnsn_init_array(array, obj_size); - return array; -} - -/** - * Allocate an array from a pool. - * @param[in] pool The memory pool to allocate from. - * @param[in] type Type type of element to store in the array. - * @return An empty array. - */ -#define DMNSN_PALLOC_ARRAY(pool, type) (dmnsn_palloc_array(pool, sizeof(type))) - -/** - * Get the size of the array. - * @param[in] array The array in question. - * @return The number of elements in the array. - */ -DMNSN_INLINE size_t -dmnsn_array_size(const dmnsn_array *array) -{ - return array->length; -} - -/** - * Set the size of the array. - * @param[in,out] array The array to resize. - * @param[in] length The new length of the array. - */ -DMNSN_INLINE void -dmnsn_array_resize(dmnsn_array *array, size_t length) -{ - if (length > array->capacity) { - /* Resize if we don't have enough capacity */ - array->capacity = length*2; /* We are greedy */ - array->ptr = dmnsn_realloc(array->ptr, array->obj_size*array->capacity); - } - - array->length = length; -} - -/** - * Copy an array. - * @param[in] array The array to copy. - * @return A copy of the array. - */ -DMNSN_INLINE dmnsn_array * -dmnsn_array_copy(const dmnsn_array *array) -{ - dmnsn_array *copy = dmnsn_new_array(array->obj_size); - dmnsn_array_resize(copy, dmnsn_array_size(array)); - memcpy(copy->ptr, array->ptr, dmnsn_array_size(array)*array->obj_size); - return copy; -} - -/** - * Split an array in half. - * @param[in,out] array The array to split. - * @return The second half of the array. - */ -DMNSN_INLINE dmnsn_array * -dmnsn_array_split(dmnsn_array *array) -{ - dmnsn_array *half = dmnsn_new_array(array->obj_size); - size_t new_size = dmnsn_array_size(array)/2; - size_t old_size = dmnsn_array_size(array) - new_size; - dmnsn_array_resize(half, new_size); - memcpy(half->ptr, (char *)array->ptr + old_size*array->obj_size, - new_size*array->obj_size); - dmnsn_array_resize(array, old_size); - return half; -} - -/** - * Get the i'th element. - * @param[in] array The array to access. - * @param[in] i The index of the element to extract. - * @param[out] obj The location to store the extracted object. - */ -DMNSN_INLINE void -dmnsn_array_get(const dmnsn_array *array, size_t i, void *obj) -{ - dmnsn_assert(i < dmnsn_array_size(array), "Array index out of bounds."); - memcpy(obj, (char *)array->ptr + array->obj_size*i, array->obj_size); -} - -/** - * Set the i'th object, expanding the array if necessary. - * @param[in,out] array The array to modify. - * @param[in] i The index to update. - * @param[in] obj The location of the object to copy into the array. - */ -DMNSN_INLINE void -dmnsn_array_set(dmnsn_array *array, size_t i, const void *obj) -{ - if (i >= dmnsn_array_size(array)) { - /* Resize if i is out of range */ - dmnsn_array_resize(array, i + 1); - } - memcpy((char *)array->ptr + array->obj_size*i, obj, array->obj_size); -} - -/** - * First element. - * @param[in] array The array to index. - * @return The address of the first element of the array. - */ -DMNSN_INLINE void * -dmnsn_array_first(const dmnsn_array *array) -{ - return array->ptr; -} - -/** - * Last element. - * @param[in] array The array to index. - * @return The address of the last element of the array. - */ -DMNSN_INLINE void * -dmnsn_array_last(const dmnsn_array *array) -{ - return (char *)array->ptr + array->obj_size*(array->length - 1); -} - -/** - * Arbitrary element access. - * @param[in] array The array to index. - * @param[in] i The index to access. - * @return The address of the i'th element of the array. - */ -DMNSN_INLINE void * -dmnsn_array_at(const dmnsn_array *array, size_t i) -{ - dmnsn_assert(i < dmnsn_array_size(array), "Array index out of bounds."); - return (char *)array->ptr + array->obj_size*i; -} - -/** - * Push an object to the end of the array. - * @param[in,out] array The array to append to. - * @param[in] obj The location of the object to push. - */ -DMNSN_INLINE void -dmnsn_array_push(dmnsn_array *array, const void *obj) -{ - dmnsn_array_set(array, dmnsn_array_size(array), obj); -} - -/** - * Pop an object from the end of the array. - * @param[in,out] array The array to pop from. - * @param[out] obj The location to store the extracted object. - */ -DMNSN_INLINE void -dmnsn_array_pop(dmnsn_array *array, void *obj) -{ - size_t size = dmnsn_array_size(array); - dmnsn_assert(size > 0, "Array is empty."); - dmnsn_array_get(array, size - 1, obj); /* Copy the object */ - dmnsn_array_resize(array, size - 1); /* Shrink the array */ -} - -/** - * Insert an item into the middle of the array. This is O(n). - * @param[in,out] array The array to insert to. - * @param[in] i The index at which to insert. - * @param[in] obj The object to insert. - */ -DMNSN_INLINE void -dmnsn_array_insert(dmnsn_array *array, size_t i, const void *obj) -{ - size_t size = dmnsn_array_size(array) + 1; - if (i >= size) - size = i + 1; - dmnsn_array_resize(array, size); - - /* Move the elements at and after `i' 1 to the right */ - memmove((char *)array->ptr + array->obj_size*(i + 1), - (char *)array->ptr + array->obj_size*i, - array->obj_size*(size - i - 1)); - /* Insert `obj' at `i' */ - memcpy((char *)array->ptr + array->obj_size*i, obj, array->obj_size); -} - -/** - * Remove an item from the middle of the array. This is O(n). - * @param[in,out] array The array to remove from. - * @param[in] i The index to remove. - */ -DMNSN_INLINE void -dmnsn_array_remove(dmnsn_array *array, size_t i) -{ - size_t size = dmnsn_array_size(array); - dmnsn_assert(i < size, "Array index out of bounds."); - /* Move the array elements after `i' 1 to the left */ - memmove((char *)array->ptr + array->obj_size*i, - (char *)array->ptr + array->obj_size*(i + 1), - array->obj_size*(size - i - 1)); - /* Decrease the size by 1 */ - dmnsn_array_resize(array, size - 1); -} - -/** - * Apply a callback to each element of an array. - * @param[in,out] array The array. - * @param[in] callback The callback to apply to the elements. - */ -DMNSN_INLINE void -dmnsn_array_apply(dmnsn_array *array, dmnsn_callback_fn *callback) -{ - char *i, *last = (char *)dmnsn_array_last(array); - for (i = (char *)dmnsn_array_first(array); i <= last; i += array->obj_size) { - callback((void *)i); - } -} - -/** - * Callback type for array sorting. - * @param[in] a The first element. - * @param[in] b The second element. - * @return A negative value iff a < b, zero iff a == b, and a positive value iff - * a > b. - */ -typedef int dmnsn_array_comparator_fn(const void *a, const void *b); - -/** - * Sort an array. - * @param[in,out] array The array to sort. - * @param[in] comparator The sorting comparator to use. - */ -DMNSN_INLINE void -dmnsn_array_sort(dmnsn_array *array, dmnsn_array_comparator_fn *comparator) -{ - qsort(array->ptr, dmnsn_array_size(array), array->obj_size, comparator); -} - -/* Macros to shorten array iteration */ - -/** - * Iterate over an array. For example, - * @code - * DMNSN_ARRAY_FOREACH (int *, i, array) { - * printf("%d\n", *i); - * } - * @endcode - * - * @param type The (pointer) type to use as an iterator. - * @param i The name of the iterator within the loop body. - * @param[in] array The array to loop over. - */ -#define DMNSN_ARRAY_FOREACH(type, i, array) \ - for (type i = dmnsn_array_first(array); \ - (size_t)(i - (type)dmnsn_array_first(array)) < dmnsn_array_size(array); \ - ++i) diff --git a/libdimension/dimension/base.h b/libdimension/dimension/base.h new file mode 100644 index 0000000..d9f063f --- /dev/null +++ b/libdimension/dimension/base.h @@ -0,0 +1,46 @@ +/************************************************************************* + * Copyright (C) 2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Basic functionality: compiler abstractions, error reporting, memory + * management, data structures, etc. + */ + +#ifndef DMNSN_BASE_H +#define DMNSN_BASE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +} +#endif + +#endif /* DMNSN_BASE_H */ diff --git a/libdimension/dimension/base/array.h b/libdimension/dimension/base/array.h new file mode 100644 index 0000000..a54d0e5 --- /dev/null +++ b/libdimension/dimension/base/array.h @@ -0,0 +1,361 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Simple dynamic arrays. + */ + +#ifndef DMNSN_BASE_H +#error "Please include instead of this header directly." +#endif + +#include /* For size_t */ +#include /* For qsort() */ +#include /* For memcpy() */ + +/** Dynamic array type. */ +typedef struct dmnsn_array { + void *ptr; /**< @internal The actual memory. */ + size_t obj_size; /**< @internal The size of each object. */ + size_t length; /**< @internal The current size of the array. */ + size_t capacity; /**< @internal The size of the allocated space. */ +} dmnsn_array; + +/** + * @internal + * Initialize a new array. + * @param[out] array The array to initialize. + * @param[in] obj_size The size of the objects to store in the array. + */ +DMNSN_INLINE void +dmnsn_init_array(dmnsn_array *array, size_t obj_size) +{ + array->obj_size = obj_size; + array->length = 0; + array->capacity = 2; /* Start with capacity of 2 */ + + /* Allocate the memory */ + array->ptr = dmnsn_malloc(array->capacity*array->obj_size); +} + +/** + * Allocate an array. + * @param[in] obj_size The size of the objects to store in the array. + * @return An empty array. + */ +DMNSN_INLINE dmnsn_array * +dmnsn_new_array(size_t obj_size) +{ + dmnsn_array *array = DMNSN_MALLOC(dmnsn_array); + dmnsn_init_array(array, obj_size); + return array; +} + +/** + * Allocate an array. + * @param[in] type Type type of element to store in the array. + * @return An empty array. + */ +#define DMNSN_NEW_ARRAY(type) (dmnsn_new_array(sizeof(type))) + +/** + * Delete an array. + * @param[in,out] array The array to delete. + */ +DMNSN_INLINE void +dmnsn_delete_array(dmnsn_array *array) +{ + if (array) { + dmnsn_free(array->ptr); + dmnsn_free(array); + } +} + +/** + * @internal + * Free a pool-allocated array. + * @param[in,out] ptr The array to clean up. + */ +void dmnsn_array_cleanup(void *ptr); + +/** + * Allocate an array from a pool. + * @param[in] pool The memory pool to allocate from. + * @param[in] obj_size The size of the objects to store in the array. + * @return An empty array. + */ +DMNSN_INLINE dmnsn_array * +dmnsn_palloc_array(dmnsn_pool *pool, size_t obj_size) +{ + dmnsn_array *array = DMNSN_PALLOC_TIDY(pool, dmnsn_array, dmnsn_array_cleanup); + dmnsn_init_array(array, obj_size); + return array; +} + +/** + * Allocate an array from a pool. + * @param[in] pool The memory pool to allocate from. + * @param[in] type Type type of element to store in the array. + * @return An empty array. + */ +#define DMNSN_PALLOC_ARRAY(pool, type) (dmnsn_palloc_array(pool, sizeof(type))) + +/** + * Get the size of the array. + * @param[in] array The array in question. + * @return The number of elements in the array. + */ +DMNSN_INLINE size_t +dmnsn_array_size(const dmnsn_array *array) +{ + return array->length; +} + +/** + * Set the size of the array. + * @param[in,out] array The array to resize. + * @param[in] length The new length of the array. + */ +DMNSN_INLINE void +dmnsn_array_resize(dmnsn_array *array, size_t length) +{ + if (length > array->capacity) { + /* Resize if we don't have enough capacity */ + array->capacity = length*2; /* We are greedy */ + array->ptr = dmnsn_realloc(array->ptr, array->obj_size*array->capacity); + } + + array->length = length; +} + +/** + * Copy an array. + * @param[in] array The array to copy. + * @return A copy of the array. + */ +DMNSN_INLINE dmnsn_array * +dmnsn_array_copy(const dmnsn_array *array) +{ + dmnsn_array *copy = dmnsn_new_array(array->obj_size); + dmnsn_array_resize(copy, dmnsn_array_size(array)); + memcpy(copy->ptr, array->ptr, dmnsn_array_size(array)*array->obj_size); + return copy; +} + +/** + * Split an array in half. + * @param[in,out] array The array to split. + * @return The second half of the array. + */ +DMNSN_INLINE dmnsn_array * +dmnsn_array_split(dmnsn_array *array) +{ + dmnsn_array *half = dmnsn_new_array(array->obj_size); + size_t new_size = dmnsn_array_size(array)/2; + size_t old_size = dmnsn_array_size(array) - new_size; + dmnsn_array_resize(half, new_size); + memcpy(half->ptr, (char *)array->ptr + old_size*array->obj_size, + new_size*array->obj_size); + dmnsn_array_resize(array, old_size); + return half; +} + +/** + * Get the i'th element. + * @param[in] array The array to access. + * @param[in] i The index of the element to extract. + * @param[out] obj The location to store the extracted object. + */ +DMNSN_INLINE void +dmnsn_array_get(const dmnsn_array *array, size_t i, void *obj) +{ + dmnsn_assert(i < dmnsn_array_size(array), "Array index out of bounds."); + memcpy(obj, (char *)array->ptr + array->obj_size*i, array->obj_size); +} + +/** + * Set the i'th object, expanding the array if necessary. + * @param[in,out] array The array to modify. + * @param[in] i The index to update. + * @param[in] obj The location of the object to copy into the array. + */ +DMNSN_INLINE void +dmnsn_array_set(dmnsn_array *array, size_t i, const void *obj) +{ + if (i >= dmnsn_array_size(array)) { + /* Resize if i is out of range */ + dmnsn_array_resize(array, i + 1); + } + memcpy((char *)array->ptr + array->obj_size*i, obj, array->obj_size); +} + +/** + * First element. + * @param[in] array The array to index. + * @return The address of the first element of the array. + */ +DMNSN_INLINE void * +dmnsn_array_first(const dmnsn_array *array) +{ + return array->ptr; +} + +/** + * Last element. + * @param[in] array The array to index. + * @return The address of the last element of the array. + */ +DMNSN_INLINE void * +dmnsn_array_last(const dmnsn_array *array) +{ + return (char *)array->ptr + array->obj_size*(array->length - 1); +} + +/** + * Arbitrary element access. + * @param[in] array The array to index. + * @param[in] i The index to access. + * @return The address of the i'th element of the array. + */ +DMNSN_INLINE void * +dmnsn_array_at(const dmnsn_array *array, size_t i) +{ + dmnsn_assert(i < dmnsn_array_size(array), "Array index out of bounds."); + return (char *)array->ptr + array->obj_size*i; +} + +/** + * Push an object to the end of the array. + * @param[in,out] array The array to append to. + * @param[in] obj The location of the object to push. + */ +DMNSN_INLINE void +dmnsn_array_push(dmnsn_array *array, const void *obj) +{ + dmnsn_array_set(array, dmnsn_array_size(array), obj); +} + +/** + * Pop an object from the end of the array. + * @param[in,out] array The array to pop from. + * @param[out] obj The location to store the extracted object. + */ +DMNSN_INLINE void +dmnsn_array_pop(dmnsn_array *array, void *obj) +{ + size_t size = dmnsn_array_size(array); + dmnsn_assert(size > 0, "Array is empty."); + dmnsn_array_get(array, size - 1, obj); /* Copy the object */ + dmnsn_array_resize(array, size - 1); /* Shrink the array */ +} + +/** + * Insert an item into the middle of the array. This is O(n). + * @param[in,out] array The array to insert to. + * @param[in] i The index at which to insert. + * @param[in] obj The object to insert. + */ +DMNSN_INLINE void +dmnsn_array_insert(dmnsn_array *array, size_t i, const void *obj) +{ + size_t size = dmnsn_array_size(array) + 1; + if (i >= size) + size = i + 1; + dmnsn_array_resize(array, size); + + /* Move the elements at and after `i' 1 to the right */ + memmove((char *)array->ptr + array->obj_size*(i + 1), + (char *)array->ptr + array->obj_size*i, + array->obj_size*(size - i - 1)); + /* Insert `obj' at `i' */ + memcpy((char *)array->ptr + array->obj_size*i, obj, array->obj_size); +} + +/** + * Remove an item from the middle of the array. This is O(n). + * @param[in,out] array The array to remove from. + * @param[in] i The index to remove. + */ +DMNSN_INLINE void +dmnsn_array_remove(dmnsn_array *array, size_t i) +{ + size_t size = dmnsn_array_size(array); + dmnsn_assert(i < size, "Array index out of bounds."); + /* Move the array elements after `i' 1 to the left */ + memmove((char *)array->ptr + array->obj_size*i, + (char *)array->ptr + array->obj_size*(i + 1), + array->obj_size*(size - i - 1)); + /* Decrease the size by 1 */ + dmnsn_array_resize(array, size - 1); +} + +/** + * Apply a callback to each element of an array. + * @param[in,out] array The array. + * @param[in] callback The callback to apply to the elements. + */ +DMNSN_INLINE void +dmnsn_array_apply(dmnsn_array *array, dmnsn_callback_fn *callback) +{ + char *i, *last = (char *)dmnsn_array_last(array); + for (i = (char *)dmnsn_array_first(array); i <= last; i += array->obj_size) { + callback((void *)i); + } +} + +/** + * Callback type for array sorting. + * @param[in] a The first element. + * @param[in] b The second element. + * @return A negative value iff a < b, zero iff a == b, and a positive value iff + * a > b. + */ +typedef int dmnsn_array_comparator_fn(const void *a, const void *b); + +/** + * Sort an array. + * @param[in,out] array The array to sort. + * @param[in] comparator The sorting comparator to use. + */ +DMNSN_INLINE void +dmnsn_array_sort(dmnsn_array *array, dmnsn_array_comparator_fn *comparator) +{ + qsort(array->ptr, dmnsn_array_size(array), array->obj_size, comparator); +} + +/* Macros to shorten array iteration */ + +/** + * Iterate over an array. For example, + * @code + * DMNSN_ARRAY_FOREACH (int *, i, array) { + * printf("%d\n", *i); + * } + * @endcode + * + * @param type The (pointer) type to use as an iterator. + * @param i The name of the iterator within the loop body. + * @param[in] array The array to loop over. + */ +#define DMNSN_ARRAY_FOREACH(type, i, array) \ + for (type i = dmnsn_array_first(array); \ + (size_t)(i - (type)dmnsn_array_first(array)) < dmnsn_array_size(array); \ + ++i) diff --git a/libdimension/dimension/base/common.h b/libdimension/dimension/base/common.h new file mode 100644 index 0000000..b947b4a --- /dev/null +++ b/libdimension/dimension/base/common.h @@ -0,0 +1,34 @@ +/************************************************************************* + * Copyright (C) 2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Common types and utilities. + */ + +#ifndef DMNSN_BASE_H +#error "Please include instead of this header directly." +#endif + +/** + * Generic callback type. + * @param[in,out] ptr A pointer to an object to act on. + */ +typedef void dmnsn_callback_fn(void *ptr); diff --git a/libdimension/dimension/base/compiler.h b/libdimension/dimension/base/compiler.h new file mode 100644 index 0000000..a83f1b9 --- /dev/null +++ b/libdimension/dimension/base/compiler.h @@ -0,0 +1,139 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Compiler abstractions. + */ + +#ifndef DMNSN_BASE_H +#error "Please include instead of this header directly." +#endif + +/** + * @internal + * @def DMNSN_C_VERSION + * The C version according to \p __STDC_VERSION__ if available, otherwise 0. + */ +#ifdef __STDC_VERSION__ + #define DMNSN_C_VERSION __STDC_VERSION__ +#else + #define DMNSN_C_VERSION 0L +#endif + +/** + * @internal + * @def DMNSN_CXX_VERSION + * The C++ version according to \p __cplusplus if available, otherwise 0. + */ +#ifdef __cplusplus + #define DMNSN_CXX_VERSION __cplusplus +#else + #define DMNSN_CXX_VERSION 0L +#endif + +/** + * @internal + * Whether we're being compiled as C++. + */ +#define DMNSN_CXX (DMNSN_CXX_VERSION > 0) + +/** + * @internal + * Whether C++11 features are supported. + */ +#define DMNSN_CXX11 (DMNSN_CXX_VERSION >= 201103L) + +/** + * @internal + * Whether C99 features are supported. + */ +#define DMNSN_C99 (DMNSN_C_VERSION >= 199901L || DMNSN_CXX11) + +/** + * @internal + * Whether C11 features are supported. + */ +#define DMNSN_C11 (DMNSN_C_VERSION >= 201112L) + +/** + * @internal + * Whether GNU C features are supported. + */ +#define DMNSN_GNUC defined(__GNUC__) + +/** + * @def DMNSN_INLINE + * A portable inline specifier. Expands to the correct method of declaring + * inline functions for the version of C you are using. + */ +#ifndef DMNSN_INLINE + #if DMNSN_CXX + /* C++ inline semantics */ + #define DMNSN_INLINE inline + #elif DMNSN_C99 + /* C99 inline semantics */ + #define DMNSN_INLINE inline + #elif DMNSN_GNUC + /* GCC inline semantics */ + #define DMNSN_INLINE __extension__ extern __inline__ + #else + /* Unknown C - mark functions static and hope the compiler is smart enough + to inline them */ + #define DMNSN_INLINE static + #endif +#endif + +/** + * @def DMNSN_NORETURN + * A portable noreturn attribute. + */ +#if DMNSN_CXX11 + #define DMNSN_NORETURN [[noreturn]] void +#elif DMNSN_C11 + #define DMNSN_NORETURN _Noreturn void +#elif DMNSN_GNUC + #define DMNSN_NORETURN __attribute__((noreturn)) void +#else + #define DMNSN_NORETURN void +#endif + +/** + * @internal + * @def DMNSN_FUNC + * @brief Expands to the name of the current function + */ +#if DMNSN_GNUC + #define DMNSN_FUNC __PRETTY_FUNCTION__ +#elif DMNSN_C99 + #define DMNSN_FUNC __func__ +#else + #define DMNSN_FUNC "" +#endif + +/** + * @internal + * An unreachable statement. + */ +#if DMNSN_GNUC + #define DMNSN_UNREACHABLE() __builtin_unreachable() +#else + #define DMNSN_UNREACHABLE() ((void)0) +#endif diff --git a/libdimension/dimension/base/dictionary.h b/libdimension/dimension/base/dictionary.h new file mode 100644 index 0000000..7360b45 --- /dev/null +++ b/libdimension/dimension/base/dictionary.h @@ -0,0 +1,87 @@ +/************************************************************************* + * Copyright (C) 2010-2011 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Simple associative arrays. + */ + +#ifndef DMNSN_BASE_H +#error "Please include instead of this header directly." +#endif + +/** A string-object associative array. */ +typedef struct dmnsn_dictionary dmnsn_dictionary; + +/** + * Allocate a dictionary. + * @param[in] obj_size The size of the objects to store in the dictionary. + * @return An empty dictionary. + */ +dmnsn_dictionary *dmnsn_new_dictionary(size_t obj_size); + +/** + * Delete a dictionary. + * @param[in,out] dict The dictionary to delete. + */ +void dmnsn_delete_dictionary(dmnsn_dictionary *dict); + +/** + * Find an element in a dictionary. + * @param[in] dict The dictionary to search. + * @param[in] key The key to search for. + * @param[out] obj The location to store the found object. + * @return Whether the element was found. + */ +bool dmnsn_dictionary_get(const dmnsn_dictionary *dict, const char *key, + void *obj); + +/** + * Access an element in a dictionary. + * @param[in] dict The dictionary to search. + * @param[in] key The key to search for. + * @return A pointer to the element if found, otherwise NULL. + */ +void *dmnsn_dictionary_at(const dmnsn_dictionary *dict, const char *key); + +/** + * Insert a (key, value) pair into a dictionary. + * @param[in,out] dict The dictionary to modify. + * @param[in] key The key to insert. + * @param[in] obj The object to insert. + */ +void dmnsn_dictionary_insert(dmnsn_dictionary *dict, const char *key, + const void *obj); + +/** + * Remove a (key, value) pair from a dictionary. + * @param[in,out] dict The dictionary to modify. + * @param[in] key The key to remove. + * @return Whether the key existed in the dictionary. + */ +bool dmnsn_dictionary_remove(dmnsn_dictionary *dict, const char *key); + +/** + * Apply a callback to all elements in a dictionary. + * @param[in,out] dict The dictionary. + * @param[in] callback The callback to apply to the elements. + */ +void dmnsn_dictionary_apply(dmnsn_dictionary *dict, + dmnsn_callback_fn *callback); diff --git a/libdimension/dimension/base/error.h b/libdimension/dimension/base/error.h new file mode 100644 index 0000000..d039081 --- /dev/null +++ b/libdimension/dimension/base/error.h @@ -0,0 +1,118 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Error reporting. + */ + +#ifndef DMNSN_BASE_H +#error "Please include instead of this header directly." +#endif + +#include + +/** + * Report a warning. + * @param[in] str A string to print explaining the warning. + */ +#define dmnsn_warning(str) \ + dmnsn_report_warning(DMNSN_FUNC, __FILE__, __LINE__, str) + +/** + * Report an error. + * @param[in] str A string to print explaining the error. + */ +#define dmnsn_error(str) \ + dmnsn_report_error(DMNSN_FUNC, __FILE__, __LINE__, str) + +/** + * @def dmnsn_assert + * Make an assertion. + * @param[in] expr The expression to assert. + * @param[in] str A string to print if the assertion fails. + */ +#if DMNSN_DEBUG + #define dmnsn_assert(expr, str) \ + do { \ + if (!(expr)) { \ + dmnsn_error((str)); \ + } \ + } while (0) +#else + #define dmnsn_assert(expr, str) ((void)0) +#endif + +/** + * @def dmnsn_unreachable + * Express that a line of code is unreachable. + * @param[in] str A string to print if the line is reached. + */ +#if DMNSN_DEBUG + #define dmnsn_unreachable(str) dmnsn_error((str)) +#else + #define dmnsn_unreachable(str) DMNSN_UNREACHABLE() +#endif + +/** + * @internal + * Called by dmnsn_warning(); don't call directly. + * @param[in] func The name of the function where the error originated. + * @param[in] file The file where the error originated. + * @param[in] line The line number where the error originated. + * @param[in] str A string describing the error. + */ +void dmnsn_report_warning(const char *func, const char *file, unsigned int line, const char *str); + +/** + * @internal + * Called by dmnsn_error(); don't call directly. + * @param[in] func The name of the function where the error originated. + * @param[in] file The file where the error originated. + * @param[in] line The line number where the error originated. + * @param[in] str A string describing the error. + */ +DMNSN_NORETURN dmnsn_report_error(const char *func, const char *file, unsigned int line, const char *str); + +/** + * Treat warnings as errors. + * @param[in] always_die Whether to die on warnings. + */ +void dmnsn_die_on_warnings(bool always_die); + +/** + * Fatal error callback type. This function should never return. + */ +typedef void dmnsn_fatal_error_fn(void); + +/** + * Get the libdimension fatal error handler, thread-safely. The default fatal + * error handler terminates the current thread, or the entire program if the + * current thread is the main thread. + * @return The current fatal error handler. + */ +dmnsn_fatal_error_fn *dmnsn_get_fatal_error_fn(void); + +/** + * Set the libdimension fatal error handler, thread-safely. + * @param[in] fatal The new fatal error handler. This function must never + * return. + */ +void dmnsn_set_fatal_error_fn(dmnsn_fatal_error_fn *fatal); diff --git a/libdimension/dimension/base/malloc.h b/libdimension/dimension/base/malloc.h new file mode 100644 index 0000000..fcc492e --- /dev/null +++ b/libdimension/dimension/base/malloc.h @@ -0,0 +1,70 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Dynamic memory. dmnsn_malloc() and friends behave like their + * non-dmnsn_-prefixed counterparts, but never return NULL. If allocation + * fails, they instead call dmnsn_error(). + */ + +#ifndef DMNSN_BASE_H +#error "Please include instead of this header directly." +#endif + +#include /* For size_t */ + +/** + * Allocate some memory. Always use dmnsn_free() to free this memory, never + * free(). + * @param[in] size The size of the memory block to allocate. + * @return The allocated memory area. + */ +void *dmnsn_malloc(size_t size); + +/** + * Allocate some memory. Always use dmnsn_free() to free this memory, never + * free(). + * @param[in] type The type of the memory block to allocate. + * @return The allocated memory area. + */ +#define DMNSN_MALLOC(type) ((type *)dmnsn_malloc(sizeof(type))) + +/** + * Expand or shrink an allocation created by dmnsn_malloc(). + * @param[in] ptr The block to resize. + * @param[in] size The new size. + * @return The resized memory area. + */ +void *dmnsn_realloc(void *ptr, size_t size); + +/** + * Duplicate a string. + * @param[in] s The string to duplicate. + * @return A string with the same contents as \p s, suitable for release by + * dmnsn_free(). + */ +char *dmnsn_strdup(const char *s); + +/** + * Free memory allocated by dmnsn_malloc() or dmnsn_strdup(). + * @param[in] ptr The memory block to free, or NULL. + */ +void dmnsn_free(void *ptr); diff --git a/libdimension/dimension/base/pool.h b/libdimension/dimension/base/pool.h new file mode 100644 index 0000000..9983f1d --- /dev/null +++ b/libdimension/dimension/base/pool.h @@ -0,0 +1,81 @@ +/************************************************************************* + * Copyright (C) 2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Memory pools. Rather than more complicated garbage collection methods like + * reference counting, objects are allocated out of pools which are freed all at + * once once a scene is rendered (for example). + */ + +#ifndef DMNSN_BASE_H +#error "Please include instead of this header directly." +#endif + +#include /* For size_t */ + +/* Forward-declare dmnsn_pool. */ +typedef struct dmnsn_pool dmnsn_pool; + +/** + * Create a new memory pool. + * @return The new pool. + */ +dmnsn_pool *dmnsn_new_pool(void); + +/** + * Allocate some memory from a pool. + * @param[in] pool The memory pool to allocate from. + * @param[in] size The size of the memory block to allocate. + * @return The allocated memory area. + */ +void *dmnsn_palloc(dmnsn_pool *pool, size_t size); + +/** + * Allocate some memory from a pool. + * @param[in] pool The memory pool to allocate from. + * @param[in] size The size of the memory block to allocate. + * @param[in] cleanup_fn A callback to invoke before the memory is freed. + * @return The allocated memory area. + */ +void *dmnsn_palloc_tidy(dmnsn_pool *pool, size_t size, dmnsn_callback_fn *cleanup_fn); + +/** + * Allocate some memory from a pool. + * @param[in] pool The memory pool to allocate from. + * @param[in] type The type of the memory block to allocate. + * @return The allocated memory area. + */ +#define DMNSN_PALLOC(pool, type) ((type *)dmnsn_palloc((pool), sizeof(type))) + +/** + * Allocate some memory from a pool. + * @param[in] pool The memory pool to allocate from. + * @param[in] type The type of the memory block to allocate. + * @param[in] cleanup_fn A callback to invoke before the memory is freed. + * @return The allocated memory area. + */ +#define DMNSN_PALLOC_TIDY(pool, type, cleanup_fn) ((type *)dmnsn_palloc_tidy((pool), sizeof(type), (cleanup_fn))) + +/** + * Free a memory pool and all associated allocations. + * @param[in] pool The memory pool to free. + */ +void dmnsn_delete_pool(dmnsn_pool *pool); diff --git a/libdimension/dimension/camera.h b/libdimension/dimension/camera.h deleted file mode 100644 index 3c6494c..0000000 --- a/libdimension/dimension/camera.h +++ /dev/null @@ -1,67 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Cameras. - */ - -/* Forward-declare dmnsn_camera */ -typedef struct dmnsn_camera dmnsn_camera; - -/** - * Camera ray callback. - * @param[in] camera The camera itself. - * @param[in] x The x coordinate of the pixel (in [0, 1]). - * @param[in] y The y coordinate of the pixel (in [0, 1]). - * @return The ray through (\p x, \p y). - */ -typedef dmnsn_line dmnsn_camera_ray_fn(const dmnsn_camera *camera, - double x, double y); - -/** A camera. */ -struct dmnsn_camera { - /* Callback functions */ - dmnsn_camera_ray_fn *ray_fn; /**< Camera ray callback. */ - - dmnsn_matrix trans; /**< Transformation matrix. */ -}; - -/** - * Create a dummy camera. - * @param[in] pool The memory pool to allocate from. - * @return The allocated camera. - */ -dmnsn_camera *dmnsn_new_camera(dmnsn_pool *pool); - -/** - * Initialize a dmnsn_camera field. - * @param[out] camera The camera to initialize. - */ -void dmnsn_init_camera(dmnsn_camera *camera); - -/** - * Invoke the camera ray callback, then correctly transform the ray. - * @param[in] camera The camera itself. - * @param[in] x The x coordinate of the pixel (in [0, 1]). - * @param[in] y The y coordinate of the pixel (in [0, 1]). - * @return The ray through (\p x, \p y). - */ -dmnsn_line dmnsn_camera_ray(const dmnsn_camera *camera, double x, double y); diff --git a/libdimension/dimension/cameras.h b/libdimension/dimension/cameras.h deleted file mode 100644 index 9ef2646..0000000 --- a/libdimension/dimension/cameras.h +++ /dev/null @@ -1,34 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Pre-defined camera types. - */ - -/** - * A perspective camera. The camera is located at the origin, looking at - * (0, 0, 1). The feild of view is the section of the plane z = 1 from - * (-0.5, -0.5) to (0.5, 0.5). Rays are transformed by the camera's - * transformation matrix. - * @param[in] pool The memory pool to allocate from. - * @return A perspective camera. - */ -dmnsn_camera *dmnsn_new_perspective_camera(dmnsn_pool *pool); diff --git a/libdimension/dimension/canvas.h b/libdimension/dimension/canvas.h index e325364..53bbda3 100644 --- a/libdimension/dimension/canvas.h +++ b/libdimension/dimension/canvas.h @@ -1,5 +1,5 @@ /************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * + * Copyright (C) 2014 Tavian Barnes * * * * This file is part of The Dimension Library. * * * @@ -20,108 +20,26 @@ /** * @file - * A canvas which is rendered to. + * Using, importing, and exporting canvases. */ -#include +#ifndef DMNSN_CANVAS_H +#define DMNSN_CANVAS_H -/** A canvas, or image. */ -typedef struct dmnsn_canvas { - size_t width; /**< Canvas width. */ - size_t height; /**< Canvas height. */ +#ifdef __cplusplus +extern "C" { +#endif - /** An array of dmnsn_canvas_optimizers. */ - dmnsn_array *optimizers; +#include +#include +#include - /** - * @internal - * Stored in first-quadrant representation (origin is bottom-left). The pixel - * at (a,b) is accessible as pixels[b*width + a]. - */ - dmnsn_tcolor *pixels; -} dmnsn_canvas; +#include +#include +#include -/* Forward-declare dmnsn_canvas_optimizer. */ -typedef struct dmnsn_canvas_optimizer dmnsn_canvas_optimizer; - -/** - * Canvas optimizer callback type. - * @param[in] optimizer The canvas optimizer. - * @param[in] canvas The canvas that was just updated. - * @param[in] x The x-coordinate that was just updated. - * @param[in] y The y-coordinate that was just updated. - */ -typedef void dmnsn_canvas_optimizer_fn(dmnsn_canvas_optimizer *optimizer, const dmnsn_canvas *canvas, size_t x, size_t y); - -/** Canvas optimizer. */ -struct dmnsn_canvas_optimizer { - dmnsn_canvas_optimizer_fn *optimizer_fn; /**< Optimizer callback. */ -}; - -/** - * Allocate a new canvas. - * @param[in] pool The memory pool to allocate from. - * @param[in] width The width of the canvas to allocate (in pixels). - * @param[in] height The height of the canvas to allocate (in pixels). - * @return The allocated canvas. - */ -dmnsn_canvas *dmnsn_new_canvas(dmnsn_pool *pool, size_t width, size_t height); - -/** - * Initialize a dmnsn_canvas_optimizer field - * @param[in] optimizer The optimizer to initialize. - */ -void dmnsn_init_canvas_optimizer(dmnsn_canvas_optimizer *optimizer); - -/** - * Set a canvas optimizer - * @param[in,out] canvas The canvas to optimize. - * @param[in] optimizer The optimizer to use. - */ -void dmnsn_canvas_optimize(dmnsn_canvas *canvas, - const dmnsn_canvas_optimizer *optimizer); - -/** - * Find a canvas optimizer by its callback. - * @param[in] canvas The canvas to check. - * @param[in] optimizer_fn The callback to search for for. - * @return A pointer to the canvas optimizer with the callback \p optimizer_fn, - * or NULL if none is found. - */ -dmnsn_canvas_optimizer * -dmnsn_canvas_find_optimizer(const dmnsn_canvas *canvas, - dmnsn_canvas_optimizer_fn *optimizer_fn); - -/* Pixel accessors */ - -/** - * Get the color of a pixel. - * @param[in] canvas The canvas to access. - * @param[in] x The x coordinate. - * @param[in] y The y coordinate. - * @return The color of the pixel at (\p x, \p y). - */ -DMNSN_INLINE dmnsn_tcolor -dmnsn_canvas_get_pixel(const dmnsn_canvas *canvas, size_t x, size_t y) -{ - dmnsn_assert(x < canvas->width && y < canvas->height, - "Canvas access out of bounds."); - return canvas->pixels[y*canvas->width + x]; +#ifdef __cplusplus } +#endif -/** - * Set the value of a pixel. - * @param[in,out] canvas The canvas to modify. - * @param[in] x The x coordinate of the pixel. - * @param[in] y The y coordinate of the pixel. - * @param[in] tcolor The value to set the pixel at (\p x, \p y) to. - */ -void dmnsn_canvas_set_pixel(dmnsn_canvas *canvas, size_t x, size_t y, - dmnsn_tcolor tcolor); - -/** - * Clear a canvas uniformly with a given color. - * @param[in,out] canvas The canvas to erase. - * @param[in] tcolor The color to paint it with. - */ -void dmnsn_canvas_clear(dmnsn_canvas *canvas, dmnsn_tcolor tcolor); +#endif /* DMNSN_CANVAS_H */ diff --git a/libdimension/dimension/canvas/canvas.h b/libdimension/dimension/canvas/canvas.h new file mode 100644 index 0000000..07d4fde --- /dev/null +++ b/libdimension/dimension/canvas/canvas.h @@ -0,0 +1,131 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * A canvas which is rendered to. + */ + +#ifndef DMNSN_CANVAS_H +#error "Please include instead of this header directly." +#endif + +#include + +/** A canvas, or image. */ +typedef struct dmnsn_canvas { + size_t width; /**< Canvas width. */ + size_t height; /**< Canvas height. */ + + /** An array of dmnsn_canvas_optimizers. */ + dmnsn_array *optimizers; + + /** + * @internal + * Stored in first-quadrant representation (origin is bottom-left). The pixel + * at (a,b) is accessible as pixels[b*width + a]. + */ + dmnsn_tcolor *pixels; +} dmnsn_canvas; + +/* Forward-declare dmnsn_canvas_optimizer. */ +typedef struct dmnsn_canvas_optimizer dmnsn_canvas_optimizer; + +/** + * Canvas optimizer callback type. + * @param[in] optimizer The canvas optimizer. + * @param[in] canvas The canvas that was just updated. + * @param[in] x The x-coordinate that was just updated. + * @param[in] y The y-coordinate that was just updated. + */ +typedef void dmnsn_canvas_optimizer_fn(dmnsn_canvas_optimizer *optimizer, const dmnsn_canvas *canvas, size_t x, size_t y); + +/** Canvas optimizer. */ +struct dmnsn_canvas_optimizer { + dmnsn_canvas_optimizer_fn *optimizer_fn; /**< Optimizer callback. */ +}; + +/** + * Allocate a new canvas. + * @param[in] pool The memory pool to allocate from. + * @param[in] width The width of the canvas to allocate (in pixels). + * @param[in] height The height of the canvas to allocate (in pixels). + * @return The allocated canvas. + */ +dmnsn_canvas *dmnsn_new_canvas(dmnsn_pool *pool, size_t width, size_t height); + +/** + * Initialize a dmnsn_canvas_optimizer field + * @param[in] optimizer The optimizer to initialize. + */ +void dmnsn_init_canvas_optimizer(dmnsn_canvas_optimizer *optimizer); + +/** + * Set a canvas optimizer + * @param[in,out] canvas The canvas to optimize. + * @param[in] optimizer The optimizer to use. + */ +void dmnsn_canvas_optimize(dmnsn_canvas *canvas, + const dmnsn_canvas_optimizer *optimizer); + +/** + * Find a canvas optimizer by its callback. + * @param[in] canvas The canvas to check. + * @param[in] optimizer_fn The callback to search for for. + * @return A pointer to the canvas optimizer with the callback \p optimizer_fn, + * or NULL if none is found. + */ +dmnsn_canvas_optimizer * +dmnsn_canvas_find_optimizer(const dmnsn_canvas *canvas, + dmnsn_canvas_optimizer_fn *optimizer_fn); + +/* Pixel accessors */ + +/** + * Get the color of a pixel. + * @param[in] canvas The canvas to access. + * @param[in] x The x coordinate. + * @param[in] y The y coordinate. + * @return The color of the pixel at (\p x, \p y). + */ +DMNSN_INLINE dmnsn_tcolor +dmnsn_canvas_get_pixel(const dmnsn_canvas *canvas, size_t x, size_t y) +{ + dmnsn_assert(x < canvas->width && y < canvas->height, + "Canvas access out of bounds."); + return canvas->pixels[y*canvas->width + x]; +} + +/** + * Set the value of a pixel. + * @param[in,out] canvas The canvas to modify. + * @param[in] x The x coordinate of the pixel. + * @param[in] y The y coordinate of the pixel. + * @param[in] tcolor The value to set the pixel at (\p x, \p y) to. + */ +void dmnsn_canvas_set_pixel(dmnsn_canvas *canvas, size_t x, size_t y, + dmnsn_tcolor tcolor); + +/** + * Clear a canvas uniformly with a given color. + * @param[in,out] canvas The canvas to erase. + * @param[in] tcolor The color to paint it with. + */ +void dmnsn_canvas_clear(dmnsn_canvas *canvas, dmnsn_tcolor tcolor); diff --git a/libdimension/dimension/canvas/gl.h b/libdimension/dimension/canvas/gl.h new file mode 100644 index 0000000..c2d77a1 --- /dev/null +++ b/libdimension/dimension/canvas/gl.h @@ -0,0 +1,52 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * OpenGL export/import of canvases. + */ + +#ifndef DMNSN_CANVAS_H +#error "Please include instead of this header directly." +#endif + +/** + * Optimize a canvas for GL drawing + * @param[in] pool The memory pool to allocate from. + * @param[in,out] canvas The canvas to optimize. + * @return Whether the canvas was successfully optimized. + */ +int dmnsn_gl_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas); + +/** + * Write canvas to GL framebuffer. + * @param[in] canvas The canvas to draw. + * @return 0 on success, non-zero on failure. + */ +int dmnsn_gl_write_canvas(const dmnsn_canvas *canvas); + +/** + * Read a canvas from a GL framebuffer. + * @param[in] canvas The canvas to write to. + * @param[in] x0 The \a x screen coordinate to start copying from. + * @param[in] y0 The \a y screen coordinate to start copying from. + * @return 0 on success, non-zero on failure. + */ +int dmnsn_gl_read_canvas(dmnsn_canvas *canvas, size_t x0, size_t y0); diff --git a/libdimension/dimension/canvas/png.h b/libdimension/dimension/canvas/png.h new file mode 100644 index 0000000..57a68c2 --- /dev/null +++ b/libdimension/dimension/canvas/png.h @@ -0,0 +1,75 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * PNG import/export of canvases. + */ + +#ifndef DMNSN_CANVAS_H +#error "Please include instead of this header directly." +#endif + +#include + +/** + * Optimize a canvas for PNG exporting + * @param[in] pool The memory pool to allocate from. + * @param[in,out] canvas The canvas to optimize. + * @return Whether the canvas was successfully optimized. + */ +int dmnsn_png_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas); + +/** + * Write a canvas to a file in PNG format. + * @param[in] canvas The canvas to write. + * @param[in,out] file The file to write to. + * @return 0 on success, non-zero on failure. + */ +int dmnsn_png_write_canvas(const dmnsn_canvas *canvas, FILE *file); + +/** + * Write a canvas to a PNG file in the background. + * @param[in] canvas The canvas to write. + * @param[in,out] file The file to write to. + * @return A \ref dmnsn_future object, or NULL on failure. + */ +dmnsn_future *dmnsn_png_write_canvas_async(const dmnsn_canvas *canvas, + FILE *file); + +/** + * Read a canvas from a PNG file. + * @param[in] pool The memory pool to allocate from. + * @param[in,out] file The PNG file to read. + * @return The new canvas, or NULL on failure. + */ +dmnsn_canvas *dmnsn_png_read_canvas(dmnsn_pool *pool, FILE *file); + +/** + * Read a canvas from a PNG file in the background. + * @param[out] canvas The address of a non-allocated canvas object. The canvas + * object will be allocated and filled with the contents of + * \p file. Do not read from this object until the + * background task has finished. + * @param[in] pool The memory pool to allocate from. + * @param[in,out] file The PNG file to read. + * @return A \ref dmnsn_future object, or NULL on failure. + */ +dmnsn_future *dmnsn_png_read_canvas_async(dmnsn_canvas **canvas, dmnsn_pool *pool, FILE *file); diff --git a/libdimension/dimension/color.h b/libdimension/dimension/color.h index adbd396..74bed59 100644 --- a/libdimension/dimension/color.h +++ b/libdimension/dimension/color.h @@ -1,5 +1,5 @@ /************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * + * Copyright (C) 2014 Tavian Barnes * * * * This file is part of The Dimension Library. * * * @@ -20,174 +20,23 @@ /** * @file - * Colors. + * Color handling. */ -#include +#ifndef DMNSN_COLOR_H +#define DMNSN_COLOR_H -/** A color value. */ -typedef struct { - double R; /**< Red component. */ - double G; /**< Green component. */ - double B; /**< Blue component. */ -} dmnsn_color; +#ifdef __cplusplus +extern "C" { +#endif -/** A standard format string for colors. */ -#define DMNSN_COLOR_FORMAT "Color<%g, %g, %g>" -/** The appropriate arguements to printf() a color. */ -#define DMNSN_COLOR_PRINTF(c) (c).R, (c).G, (c).B +#include -/** Construct a new color. */ -DMNSN_INLINE dmnsn_color -dmnsn_new_color(double R, double G, double B) -{ - dmnsn_color ret = { R, G, B }; - return ret; -} - -/** Apply sRGB gamma */ -DMNSN_INLINE double -dmnsn_sRGB_gamma(double Clinear) -{ - /* - * If C represents R, G, and B, then the sRGB values are now found as follows: - * - * { 12.92*Clinear, Clinear <= 0.0031308 - * Csrgb = { 1/2.4 - * { (1.055)*Clinear - 0.055, Clinear > 0.0031308 - */ - if (Clinear == 1.0) { - return 1.0; /* Map 1.0 to 1.0 instead of 0.9999999999999999 */ - } else if (Clinear > 0.0031308) { - return 1.055*pow(Clinear, 1.0/2.4) - 0.055; - } else { - return 12.92*Clinear; - } -} - -/** Convert to sRGB space. */ -DMNSN_INLINE dmnsn_color -dmnsn_color_to_sRGB(dmnsn_color color) -{ - return dmnsn_new_color( - dmnsn_sRGB_gamma(color.R), - dmnsn_sRGB_gamma(color.G), - dmnsn_sRGB_gamma(color.B) - ); -} - -/** Remove sRGB gamma */ -DMNSN_INLINE double -dmnsn_sRGB_inverse_gamma(double CsRGB) -{ - /* - * If C represents R, G, and B, then the Clinear values are now found as - * follows: - * - * { Csrgb/12.92, Csrgb <= 0.04045 - * Clinear = { 2.4 - * { ((Csrgb + 0.055)/1.055) , Csrgb > 0.04045 - */ - if (CsRGB == 1.0) { - return 1.0; /* Map 1.0 to 1.0 instead of 0.9999999999999999 */ - } else if (CsRGB <= 0.040449936) { - return CsRGB/12.92; - } else { - return pow((CsRGB + 0.055)/1.055, 2.4); - } -} - -/** Convert from sRGB space. */ -DMNSN_INLINE dmnsn_color -dmnsn_color_from_sRGB(dmnsn_color color) -{ - return dmnsn_new_color( - dmnsn_sRGB_inverse_gamma(color.R), - dmnsn_sRGB_inverse_gamma(color.G), - dmnsn_sRGB_inverse_gamma(color.B) - ); -} - -/** Greyscale color intensity. */ -DMNSN_INLINE double -dmnsn_color_intensity(dmnsn_color color) -{ - return 0.2126*color.R + 0.7152*color.G + 0.0722*color.B; -} - -/** Add two colors together. */ -DMNSN_INLINE dmnsn_color -dmnsn_color_add(dmnsn_color lhs, dmnsn_color rhs) -{ - return dmnsn_new_color(lhs.R + rhs.R, lhs.G + rhs.G, lhs.B + rhs.B); -} - -/** Subtract two colors. */ -DMNSN_INLINE dmnsn_color -dmnsn_color_sub(dmnsn_color lhs, dmnsn_color rhs) -{ - return dmnsn_new_color(lhs.R - rhs.R, lhs.G - rhs.G, lhs.B - rhs.B); -} - -/** Scale a color's intensity. */ -DMNSN_INLINE dmnsn_color -dmnsn_color_mul(double n, dmnsn_color color) -{ - return dmnsn_new_color(n*color.R, n*color.G, n*color.B); -} - -/** Return the color at \p n on a gradient from \p c1 at 0 to \p c2 at 1. */ -DMNSN_INLINE dmnsn_color -dmnsn_color_gradient(dmnsn_color c1, dmnsn_color c2, double n) -{ - return dmnsn_new_color( - n*(c2.R - c1.R) + c1.R, - n*(c2.G - c1.G) + c1.G, - n*(c2.B - c1.B) + c1.B - ); -} - -/** Illuminate \p color with \p light. */ -DMNSN_INLINE dmnsn_color -dmnsn_color_illuminate(dmnsn_color light, dmnsn_color color) -{ - return dmnsn_new_color(light.R*color.R, light.G*color.G, light.B*color.B); -} +#include +#include -/** Saturate the color components to [0.0, 1.0]. */ -DMNSN_INLINE dmnsn_color -dmnsn_color_clamp(dmnsn_color color) -{ - color.R = dmnsn_clamp(color.R, 0.0, 1.0); - color.G = dmnsn_clamp(color.G, 0.0, 1.0); - color.B = dmnsn_clamp(color.B, 0.0, 1.0); - return color; +#ifdef __cplusplus } +#endif -/** Return whether a color contains any NaN components. */ -DMNSN_INLINE bool -dmnsn_color_isnan(dmnsn_color color) -{ - return dmnsn_isnan(color.R) || dmnsn_isnan(color.G) || dmnsn_isnan(color.B); -} - -/* Standard colors */ - -/** Black. */ -#define dmnsn_black dmnsn_new_color(0.0, 0.0, 0.0) -/** White. */ -#define dmnsn_white dmnsn_new_color(1.0, 1.0, 1.0) -/** Red. */ -#define dmnsn_red dmnsn_new_color(1.0, 0.0, 0.0) -/** Green. */ -#define dmnsn_green dmnsn_new_color(0.0, 1.0, 0.0) -/** Blue. */ -#define dmnsn_blue dmnsn_new_color(0.0, 0.0, 1.0) -/** Magenta. */ -#define dmnsn_magenta dmnsn_new_color(1.0, 0.0, 1.0) -/** Orange. */ -#define dmnsn_orange dmnsn_new_color(1.0, 0.21404114048223255, 0.0) -/** Yellow. */ -#define dmnsn_yellow dmnsn_new_color(1.0, 1.0, 0.0) -/** Cyan. */ -#define dmnsn_cyan dmnsn_new_color(0.0, 1.0, 1.0) +#endif /* DMNSN_COLOR_H */ diff --git a/libdimension/dimension/color/color.h b/libdimension/dimension/color/color.h new file mode 100644 index 0000000..84e66ea --- /dev/null +++ b/libdimension/dimension/color/color.h @@ -0,0 +1,198 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Colors. + */ + +#ifndef DMNSN_COLOR_H +#error "Please include instead of this header directly." +#endif + +#include +#include + +/** A color value. */ +typedef struct { + double R; /**< Red component. */ + double G; /**< Green component. */ + double B; /**< Blue component. */ +} dmnsn_color; + +/** A standard format string for colors. */ +#define DMNSN_COLOR_FORMAT "Color<%g, %g, %g>" +/** The appropriate arguements to printf() a color. */ +#define DMNSN_COLOR_PRINTF(c) (c).R, (c).G, (c).B + +/** Construct a new color. */ +DMNSN_INLINE dmnsn_color +dmnsn_new_color(double R, double G, double B) +{ + dmnsn_color ret = { R, G, B }; + return ret; +} + +/** Apply sRGB gamma */ +DMNSN_INLINE double +dmnsn_sRGB_gamma(double Clinear) +{ + /* + * If C represents R, G, and B, then the sRGB values are now found as follows: + * + * { 12.92*Clinear, Clinear <= 0.0031308 + * Csrgb = { 1/2.4 + * { (1.055)*Clinear - 0.055, Clinear > 0.0031308 + */ + if (Clinear == 1.0) { + return 1.0; /* Map 1.0 to 1.0 instead of 0.9999999999999999 */ + } else if (Clinear > 0.0031308) { + return 1.055*pow(Clinear, 1.0/2.4) - 0.055; + } else { + return 12.92*Clinear; + } +} + +/** Convert to sRGB space. */ +DMNSN_INLINE dmnsn_color +dmnsn_color_to_sRGB(dmnsn_color color) +{ + return dmnsn_new_color( + dmnsn_sRGB_gamma(color.R), + dmnsn_sRGB_gamma(color.G), + dmnsn_sRGB_gamma(color.B) + ); +} + +/** Remove sRGB gamma */ +DMNSN_INLINE double +dmnsn_sRGB_inverse_gamma(double CsRGB) +{ + /* + * If C represents R, G, and B, then the Clinear values are now found as + * follows: + * + * { Csrgb/12.92, Csrgb <= 0.04045 + * Clinear = { 2.4 + * { ((Csrgb + 0.055)/1.055) , Csrgb > 0.04045 + */ + if (CsRGB == 1.0) { + return 1.0; /* Map 1.0 to 1.0 instead of 0.9999999999999999 */ + } else if (CsRGB <= 0.040449936) { + return CsRGB/12.92; + } else { + return pow((CsRGB + 0.055)/1.055, 2.4); + } +} + +/** Convert from sRGB space. */ +DMNSN_INLINE dmnsn_color +dmnsn_color_from_sRGB(dmnsn_color color) +{ + return dmnsn_new_color( + dmnsn_sRGB_inverse_gamma(color.R), + dmnsn_sRGB_inverse_gamma(color.G), + dmnsn_sRGB_inverse_gamma(color.B) + ); +} + +/** Greyscale color intensity. */ +DMNSN_INLINE double +dmnsn_color_intensity(dmnsn_color color) +{ + return 0.2126*color.R + 0.7152*color.G + 0.0722*color.B; +} + +/** Add two colors together. */ +DMNSN_INLINE dmnsn_color +dmnsn_color_add(dmnsn_color lhs, dmnsn_color rhs) +{ + return dmnsn_new_color(lhs.R + rhs.R, lhs.G + rhs.G, lhs.B + rhs.B); +} + +/** Subtract two colors. */ +DMNSN_INLINE dmnsn_color +dmnsn_color_sub(dmnsn_color lhs, dmnsn_color rhs) +{ + return dmnsn_new_color(lhs.R - rhs.R, lhs.G - rhs.G, lhs.B - rhs.B); +} + +/** Scale a color's intensity. */ +DMNSN_INLINE dmnsn_color +dmnsn_color_mul(double n, dmnsn_color color) +{ + return dmnsn_new_color(n*color.R, n*color.G, n*color.B); +} + +/** Return the color at \p n on a gradient from \p c1 at 0 to \p c2 at 1. */ +DMNSN_INLINE dmnsn_color +dmnsn_color_gradient(dmnsn_color c1, dmnsn_color c2, double n) +{ + return dmnsn_new_color( + n*(c2.R - c1.R) + c1.R, + n*(c2.G - c1.G) + c1.G, + n*(c2.B - c1.B) + c1.B + ); +} + +/** Illuminate \p color with \p light. */ +DMNSN_INLINE dmnsn_color +dmnsn_color_illuminate(dmnsn_color light, dmnsn_color color) +{ + return dmnsn_new_color(light.R*color.R, light.G*color.G, light.B*color.B); +} + +/** Saturate the color components to [0.0, 1.0]. */ +DMNSN_INLINE dmnsn_color +dmnsn_color_clamp(dmnsn_color color) +{ + color.R = dmnsn_clamp(color.R, 0.0, 1.0); + color.G = dmnsn_clamp(color.G, 0.0, 1.0); + color.B = dmnsn_clamp(color.B, 0.0, 1.0); + return color; +} + +/** Return whether a color contains any NaN components. */ +DMNSN_INLINE bool +dmnsn_color_isnan(dmnsn_color color) +{ + return dmnsn_isnan(color.R) || dmnsn_isnan(color.G) || dmnsn_isnan(color.B); +} + +/* Standard colors */ + +/** Black. */ +#define dmnsn_black dmnsn_new_color(0.0, 0.0, 0.0) +/** White. */ +#define dmnsn_white dmnsn_new_color(1.0, 1.0, 1.0) +/** Red. */ +#define dmnsn_red dmnsn_new_color(1.0, 0.0, 0.0) +/** Green. */ +#define dmnsn_green dmnsn_new_color(0.0, 1.0, 0.0) +/** Blue. */ +#define dmnsn_blue dmnsn_new_color(0.0, 0.0, 1.0) +/** Magenta. */ +#define dmnsn_magenta dmnsn_new_color(1.0, 0.0, 1.0) +/** Orange. */ +#define dmnsn_orange dmnsn_new_color(1.0, 0.21404114048223255, 0.0) +/** Yellow. */ +#define dmnsn_yellow dmnsn_new_color(1.0, 1.0, 0.0) +/** Cyan. */ +#define dmnsn_cyan dmnsn_new_color(0.0, 1.0, 1.0) diff --git a/libdimension/dimension/color/tcolor.h b/libdimension/dimension/color/tcolor.h new file mode 100644 index 0000000..b4b4167 --- /dev/null +++ b/libdimension/dimension/color/tcolor.h @@ -0,0 +1,116 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Colors with transparency information. + */ + +#ifndef DMNSN_COLOR_H +#error "Please include instead of this header directly." +#endif + +/** A transparent color. */ +typedef struct dmnsn_tcolor { + dmnsn_color c; /**< Color. */ + double T; /**< Transparency. */ + double F; /**< Proportion of filtered transparency. */ +} dmnsn_tcolor; + +/** A standard format string for colors. */ +#define DMNSN_TCOLOR_FORMAT "TColor<%g, %g, %g, %g, %g>" +/** The appropriate arguements to printf() a color. */ +#define DMNSN_TCOLOR_PRINTF(tc) (tc).c.R, (tc).c.G, (tc).c.B, (tc).T, (tc).F + +/** Create a tcolor. */ +DMNSN_INLINE dmnsn_tcolor +dmnsn_new_tcolor(dmnsn_color c, double T, double F) +{ + dmnsn_tcolor ret = { c, T, F }; + return ret; +} + +/** Convert a dmnsn_color into a dmnsn_tcolor. */ +#define DMNSN_TCOLOR(c) dmnsn_new_tcolor(c, 0.0, 0.0) + +/** Create a tcolor with individually-specified components. */ +DMNSN_INLINE dmnsn_tcolor +dmnsn_new_tcolor5(double R, double G, double B, double T, double F) +{ + dmnsn_tcolor ret = { dmnsn_new_color(R, G, B), T, F }; + return ret; +} + +/** Return the color at \p n on a gradient from \p c1 at 0 to \p c2 at 1. */ +DMNSN_INLINE dmnsn_tcolor +dmnsn_tcolor_gradient(dmnsn_tcolor c1, dmnsn_tcolor c2, double n) +{ + return dmnsn_new_tcolor( + dmnsn_color_gradient(c1.c, c2.c, n), + n*(c2.T - c1.T) + c1.T, + n*(c2.F - c1.F) + c1.F + ); +} + +/** Filter \p light through \p filter. */ +DMNSN_INLINE dmnsn_color +dmnsn_tcolor_filter(dmnsn_color light, dmnsn_tcolor filter) +{ + dmnsn_color filtered = + dmnsn_color_mul(filter.T*filter.F, dmnsn_color_illuminate(light, filter.c)); + dmnsn_color transmitted = dmnsn_color_mul(filter.T*(1.0 - filter.F), light); + return dmnsn_color_add(filtered, transmitted); +} + +/** Remove the filtered component of a tcolor. */ +DMNSN_INLINE dmnsn_tcolor +dmnsn_tcolor_remove_filter(dmnsn_tcolor tcolor) +{ + double intensity = dmnsn_color_intensity(tcolor.c); + double newtrans = (1.0 - (1.0 - intensity)*tcolor.F)*tcolor.T; + if (1.0 - newtrans >= dmnsn_epsilon) { + tcolor.c = dmnsn_color_mul((1.0 - tcolor.T)/(1.0 - newtrans), tcolor.c); + } + tcolor.T = newtrans; + tcolor.F = 0.0; + return tcolor; +} + +/** Saturate the tcolor components to [0.0, 1.0]. */ +DMNSN_INLINE dmnsn_tcolor +dmnsn_tcolor_clamp(dmnsn_tcolor tcolor) +{ + tcolor.c = dmnsn_color_clamp(tcolor.c); + tcolor.T = dmnsn_clamp(tcolor.T, 0.0, 1.0); + tcolor.F = dmnsn_clamp(tcolor.F, 0.0, 1.0); + return tcolor; +} + +/** Return whether a tcolor contains any NaN components. */ +DMNSN_INLINE bool +dmnsn_tcolor_isnan(dmnsn_tcolor tcolor) +{ + return dmnsn_color_isnan(tcolor.c) || dmnsn_isnan(tcolor.T) || dmnsn_isnan(tcolor.F); +} + +/* Standard tcolors */ + +/** Clear. */ +#define dmnsn_clear dmnsn_new_tcolor5(0.0, 0.0, 0.0, 1.0, 0.0) diff --git a/libdimension/dimension/common.h b/libdimension/dimension/common.h deleted file mode 100644 index 15bafd8..0000000 --- a/libdimension/dimension/common.h +++ /dev/null @@ -1,30 +0,0 @@ -/************************************************************************* - * Copyright (C) 2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Common types and utilities. - */ - -/** - * Generic callback type. - * @param[in,out] ptr A pointer to an object to act on. - */ -typedef void dmnsn_callback_fn(void *ptr); diff --git a/libdimension/dimension/compiler.h b/libdimension/dimension/compiler.h deleted file mode 100644 index af3f4c8..0000000 --- a/libdimension/dimension/compiler.h +++ /dev/null @@ -1,135 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Compiler abstractions. - */ - -/** - * @internal - * @def DMNSN_C_VERSION - * The C version according to \p __STDC_VERSION__ if available, otherwise 0. - */ -#ifdef __STDC_VERSION__ - #define DMNSN_C_VERSION __STDC_VERSION__ -#else - #define DMNSN_C_VERSION 0L -#endif - -/** - * @internal - * @def DMNSN_CXX_VERSION - * The C++ version according to \p __cplusplus if available, otherwise 0. - */ -#ifdef __cplusplus - #define DMNSN_CXX_VERSION __cplusplus -#else - #define DMNSN_CXX_VERSION 0L -#endif - -/** - * @internal - * Whether we're being compiled as C++. - */ -#define DMNSN_CXX (DMNSN_CXX_VERSION > 0) - -/** - * @internal - * Whether C++11 features are supported. - */ -#define DMNSN_CXX11 (DMNSN_CXX_VERSION >= 201103L) - -/** - * @internal - * Whether C99 features are supported. - */ -#define DMNSN_C99 (DMNSN_C_VERSION >= 199901L || DMNSN_CXX11) - -/** - * @internal - * Whether C11 features are supported. - */ -#define DMNSN_C11 (DMNSN_C_VERSION >= 201112L) - -/** - * @internal - * Whether GNU C features are supported. - */ -#define DMNSN_GNUC defined(__GNUC__) - -/** - * @def DMNSN_INLINE - * A portable inline specifier. Expands to the correct method of declaring - * inline functions for the version of C you are using. - */ -#ifndef DMNSN_INLINE - #if DMNSN_CXX - /* C++ inline semantics */ - #define DMNSN_INLINE inline - #elif DMNSN_C99 - /* C99 inline semantics */ - #define DMNSN_INLINE inline - #elif DMNSN_GNUC - /* GCC inline semantics */ - #define DMNSN_INLINE __extension__ extern __inline__ - #else - /* Unknown C - mark functions static and hope the compiler is smart enough - to inline them */ - #define DMNSN_INLINE static - #endif -#endif - -/** - * @def DMNSN_NORETURN - * A portable noreturn attribute. - */ -#if DMNSN_CXX11 - #define DMNSN_NORETURN [[noreturn]] void -#elif DMNSN_C11 - #define DMNSN_NORETURN _Noreturn void -#elif DMNSN_GNUC - #define DMNSN_NORETURN __attribute__((noreturn)) void -#else - #define DMNSN_NORETURN void -#endif - -/** - * @internal - * @def DMNSN_FUNC - * @brief Expands to the name of the current function - */ -#if DMNSN_GNUC - #define DMNSN_FUNC __PRETTY_FUNCTION__ -#elif DMNSN_C99 - #define DMNSN_FUNC __func__ -#else - #define DMNSN_FUNC "" -#endif - -/** - * @internal - * An unreachable statement. - */ -#if DMNSN_GNUC - #define DMNSN_UNREACHABLE() __builtin_unreachable() -#else - #define DMNSN_UNREACHABLE() ((void)0) -#endif diff --git a/libdimension/dimension/concurrency.h b/libdimension/dimension/concurrency.h new file mode 100644 index 0000000..6c8c29c --- /dev/null +++ b/libdimension/dimension/concurrency.h @@ -0,0 +1,39 @@ +/************************************************************************* + * Copyright (C) 2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Concurrency concerns. + */ + +#ifndef DMNSN_CONCURRENCY_H +#define DMNSN_CONCURRENCY_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifdef __cplusplus +} +#endif + +#endif /* DMNSN_CONCURRENCY_H */ diff --git a/libdimension/dimension/concurrency/future.h b/libdimension/dimension/concurrency/future.h new file mode 100644 index 0000000..24d5ee2 --- /dev/null +++ b/libdimension/dimension/concurrency/future.h @@ -0,0 +1,85 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * An interface for asynchronous tasks. *_async() versions of functions + * return a dmnsn_future* object which can indicate the progress of the + * background task, and wait for task completion. The task's return value + * is returned as an int from dmnsn_finish_progress(). + */ + +#ifndef DMNSN_CONCURRENCY_H +#error "Please include instead of this header directly." +#endif + +#include + +/** A future object. */ +typedef struct dmnsn_future dmnsn_future; + +/** + * Join the worker thread and return its integer return value in addition to + * deleting \p future. + * @param[in,out] future The background task to join. + * @return The return value of the background task. + */ +int dmnsn_future_join(dmnsn_future *future); + +/** + * Interrupt the execution of a background thread. + * @param[in,out] future The background task to cancel. + */ +void dmnsn_future_cancel(dmnsn_future *future); + +/** + * Get the progress of the background task. + * @param[in] future The background task to examine. + * @return The progress of the background task, in [0.0, 1.0]. + */ +double dmnsn_future_progress(const dmnsn_future *future); + +/** + * Find out if a background task is finished. + * @param[in] future The background task to examine. + * @return true if the task is done, false otherwise. + */ +bool dmnsn_future_is_done(const dmnsn_future *future); + +/** + * Wait for a certain amount of progress. Always use this rather than + * spinlocking. + * @param[in] future The background task to monitor. + * @param[in] progress The progress value to wait for. + */ +void dmnsn_future_wait(const dmnsn_future *future, double progress); + +/** + * Pause all threads working on the given future. Once this function returns, + * it is safe to examine the intermediate state of the asynchronous computation. + * @param[in,out] future The background task to pause. + */ +void dmnsn_future_pause(dmnsn_future *future); + +/** + * Resume a previously paused future object. + * @param[in,out] future The background task to resume. + */ +void dmnsn_future_resume(dmnsn_future *future); diff --git a/libdimension/dimension/csg.h b/libdimension/dimension/csg.h deleted file mode 100644 index b2ce83f..0000000 --- a/libdimension/dimension/csg.h +++ /dev/null @@ -1,59 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Constructive solid geometry - */ - -/** - * CSG union. - * @param[in] pool The memory pool to allocate from. - * @param[in] objects The objects from which to compose the union. - * @return A union of the objects in \p objects. - */ -dmnsn_object *dmnsn_new_csg_union(dmnsn_pool *pool, dmnsn_array *objects); - -/** - * CSG intersection. - * @param[in] pool The memory pool to allocate from. - * @param[in,out] A The first object. - * @param[in,out] B The second object. - * @return The intersection of \p A and \p B. - */ -dmnsn_object *dmnsn_new_csg_intersection(dmnsn_pool *pool, dmnsn_object *A, dmnsn_object *B); - -/** - * CSG intersection. - * @param[in] pool The memory pool to allocate from. - * @param[in,out] A The outer object. - * @param[in,out] B The inner object. - * @return The difference between \p A and \p B. - */ -dmnsn_object *dmnsn_new_csg_difference(dmnsn_pool *pool, dmnsn_object *A, dmnsn_object *B); - -/** - * CSG Merge. - * @param[in] pool The memory pool to allocate from. - * @param[in,out] A The first object. - * @param[in,out] B The second object. - * @return The merge of \p A and \p B. - */ -dmnsn_object *dmnsn_new_csg_merge(dmnsn_pool *pool, dmnsn_object *A, dmnsn_object *B); diff --git a/libdimension/dimension/dictionary.h b/libdimension/dimension/dictionary.h deleted file mode 100644 index 887b171..0000000 --- a/libdimension/dimension/dictionary.h +++ /dev/null @@ -1,83 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2011 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Simple associative arrays. - */ - -/** A string-object associative array. */ -typedef struct dmnsn_dictionary dmnsn_dictionary; - -/** - * Allocate a dictionary. - * @param[in] obj_size The size of the objects to store in the dictionary. - * @return An empty dictionary. - */ -dmnsn_dictionary *dmnsn_new_dictionary(size_t obj_size); - -/** - * Delete a dictionary. - * @param[in,out] dict The dictionary to delete. - */ -void dmnsn_delete_dictionary(dmnsn_dictionary *dict); - -/** - * Find an element in a dictionary. - * @param[in] dict The dictionary to search. - * @param[in] key The key to search for. - * @param[out] obj The location to store the found object. - * @return Whether the element was found. - */ -bool dmnsn_dictionary_get(const dmnsn_dictionary *dict, const char *key, - void *obj); - -/** - * Access an element in a dictionary. - * @param[in] dict The dictionary to search. - * @param[in] key The key to search for. - * @return A pointer to the element if found, otherwise NULL. - */ -void *dmnsn_dictionary_at(const dmnsn_dictionary *dict, const char *key); - -/** - * Insert a (key, value) pair into a dictionary. - * @param[in,out] dict The dictionary to modify. - * @param[in] key The key to insert. - * @param[in] obj The object to insert. - */ -void dmnsn_dictionary_insert(dmnsn_dictionary *dict, const char *key, - const void *obj); - -/** - * Remove a (key, value) pair from a dictionary. - * @param[in,out] dict The dictionary to modify. - * @param[in] key The key to remove. - * @return Whether the key existed in the dictionary. - */ -bool dmnsn_dictionary_remove(dmnsn_dictionary *dict, const char *key); - -/** - * Apply a callback to all elements in a dictionary. - * @param[in,out] dict The dictionary. - * @param[in] callback The callback to apply to the elements. - */ -void dmnsn_dictionary_apply(dmnsn_dictionary *dict, - dmnsn_callback_fn *callback); diff --git a/libdimension/dimension/error.h b/libdimension/dimension/error.h deleted file mode 100644 index 0561b8a..0000000 --- a/libdimension/dimension/error.h +++ /dev/null @@ -1,117 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Error reporting interface. Errors are reported at a given severity by the - * dmnsn_error() macro at a given severity, which prints a warning if it is - * below the set resilience, or prints an error and exits if it's at or above - * the set resilience. - */ - -#include - -/** - * Report a warning. - * @param[in] str A string to print explaining the warning. - */ -#define dmnsn_warning(str) \ - dmnsn_report_warning(DMNSN_FUNC, __FILE__, __LINE__, str) - -/** - * Report an error. - * @param[in] str A string to print explaining the error. - */ -#define dmnsn_error(str) \ - dmnsn_report_error(DMNSN_FUNC, __FILE__, __LINE__, str) - -/** - * @def dmnsn_assert - * Make an assertion. - * @param[in] expr The expression to assert. - * @param[in] str A string to print if the assertion fails. - */ -#if DMNSN_DEBUG - #define dmnsn_assert(expr, str) \ - do { \ - if (!(expr)) { \ - dmnsn_error((str)); \ - } \ - } while (0) -#else - #define dmnsn_assert(expr, str) ((void)0) -#endif - -/** - * @def dmnsn_unreachable - * Express that a line of code is unreachable. - * @param[in] str A string to print if the line is reached. - */ -#if DMNSN_DEBUG - #define dmnsn_unreachable(str) dmnsn_error((str)) -#else - #define dmnsn_unreachable(str) DMNSN_UNREACHABLE() -#endif - -/** - * @internal - * Called by dmnsn_warning(); don't call directly. - * @param[in] func The name of the function where the error originated. - * @param[in] file The file where the error originated. - * @param[in] line The line number where the error originated. - * @param[in] str A string describing the error. - */ -void dmnsn_report_warning(const char *func, const char *file, unsigned int line, const char *str); - -/** - * @internal - * Called by dmnsn_error(); don't call directly. - * @param[in] func The name of the function where the error originated. - * @param[in] file The file where the error originated. - * @param[in] line The line number where the error originated. - * @param[in] str A string describing the error. - */ -DMNSN_NORETURN dmnsn_report_error(const char *func, const char *file, unsigned int line, const char *str); - -/** - * Treat warnings as errors. - * @param[in] always_die Whether to die on warnings. - */ -void dmnsn_die_on_warnings(bool always_die); - -/** - * Fatal error callback type. This function should never return. - */ -typedef void dmnsn_fatal_error_fn(void); - -/** - * Get the libdimension fatal error handler, thread-safely. The default fatal - * error handler terminates the current thread, or the entire program if the - * current thread is the main thread. - * @return The current fatal error handler. - */ -dmnsn_fatal_error_fn *dmnsn_get_fatal_error_fn(void); - -/** - * Set the libdimension fatal error handler, thread-safely. - * @param[in] fatal The new fatal error handler. This function must never - * return. - */ -void dmnsn_set_fatal_error_fn(dmnsn_fatal_error_fn *fatal); diff --git a/libdimension/dimension/finish.h b/libdimension/dimension/finish.h deleted file mode 100644 index d975877..0000000 --- a/libdimension/dimension/finish.h +++ /dev/null @@ -1,141 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Object finishes. - */ - -/* Ambient component */ - -/** Ambient finish component. */ -typedef struct dmnsn_ambient { - dmnsn_color ambient; /**< Ambient light. */ -} dmnsn_ambient; - -/** Allocate an ambient component. */ -dmnsn_ambient *dmnsn_new_ambient(dmnsn_pool *pool, dmnsn_color ambient); - -/* Diffuse component */ - -typedef struct dmnsn_diffuse dmnsn_diffuse; - -/** - * Diffuse reflection callback. - * @param[in] diffuse The diffuse object itself. - * @param[in] light The color of the light illuminating the object. - * @param[in] color The pigment of the object. - * @param[in] ray The direction of the light source. - * @param[in] normal The normal vector of the surface. - * @return The diffuse reflection component of the object's color. - */ -typedef dmnsn_color dmnsn_diffuse_fn(const dmnsn_diffuse *diffuse, - dmnsn_color light, dmnsn_color color, - dmnsn_vector ray, dmnsn_vector normal); - -/** Diffuse finish component. */ -struct dmnsn_diffuse { - dmnsn_diffuse_fn *diffuse_fn; /**< Diffuse callback. */ -}; - -/** Allocate a dummy diffuse component. */ -dmnsn_diffuse *dmnsn_new_diffuse(dmnsn_pool *pool); -/** Initialize a dmnsn_diffuse field. */ -void dmnsn_init_diffuse(dmnsn_diffuse *diffuse); - -/* Specular component */ - -typedef struct dmnsn_specular dmnsn_specular; - -/** - * Specular highlight callback. - * @param[in] specular The specular object itself. - * @param[in] light The color of the light illuminating the object. - * @param[in] color The pigment of the object. - * @param[in] ray The direction of the light source. - * @param[in] normal The normal vector of the surface. - * @param[in] viewer The direction of the viewer. - * @return The specular reflection component of the object's color. - */ -typedef dmnsn_color dmnsn_specular_fn(const dmnsn_specular *specular, - dmnsn_color light, dmnsn_color color, - dmnsn_vector ray, dmnsn_vector normal, - dmnsn_vector viewer); - -/** Specular finish component. */ -struct dmnsn_specular { - dmnsn_specular_fn *specular_fn; /**< Specular callback. */ -}; - -/** Allocate a dummy specular component. */ -dmnsn_specular *dmnsn_new_specular(dmnsn_pool *pool); -/** Initialize a dmnsn_specular field. */ -void dmnsn_init_specular(dmnsn_specular *specular); - -/* Reflection component */ - -typedef struct dmnsn_reflection dmnsn_reflection; - -/** - * Reflected light callback. - * @param[in] reflection The reflection object itself. - * @param[in] reflect The color of the reflected ray. - * @param[in] color The pigment of the object. - * @param[in] ray The direction of the reflected ray. - * @param[in] normal The normal vector of the surface. - * @return The contribution of the reflected ray to the object's color. - */ -typedef dmnsn_color dmnsn_reflection_fn(const dmnsn_reflection *reflection, - dmnsn_color reflect, dmnsn_color color, - dmnsn_vector ray, dmnsn_vector normal); - -/** The reflection component. */ -struct dmnsn_reflection { - dmnsn_reflection_fn *reflection_fn; /**< Reflection callback. */ -}; - -/** Allocate a dummy reflection component. */ -dmnsn_reflection *dmnsn_new_reflection(dmnsn_pool *pool); -/** Initialize a dmnsn_reflection field. */ -void dmnsn_init_reflection(dmnsn_reflection *reflection); - -/* Entire finishes */ - -/** A finish. */ -typedef struct dmnsn_finish { - dmnsn_ambient *ambient; /**< Ambient component. */ - dmnsn_diffuse *diffuse; /**< Diffuse component. */ - dmnsn_specular *specular; /**< Specular component. */ - dmnsn_reflection *reflection; /**< Reflection component. */ -} dmnsn_finish; - -/** - * Create a new blank finish. - * @return The new finish. - */ -dmnsn_finish dmnsn_new_finish(void); - -/** - * Fill missing finish properties from a default finish. - * @param[in] default_finish The default finish. - * @param[in,out] finish The finish to fill. - */ -void dmnsn_finish_cascade(const dmnsn_finish *default_finish, - dmnsn_finish *finish); diff --git a/libdimension/dimension/finishes.h b/libdimension/dimension/finishes.h deleted file mode 100644 index e1f7b44..0000000 --- a/libdimension/dimension/finishes.h +++ /dev/null @@ -1,51 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Pre-defined finishes. - */ - -/** - * Regular diffuse finish. - * @param[in] pool The memory pool to allocate from. - * @param[in] diffuse The diffuse reflection coefficient. - * @return A diffuse finish component. - */ -dmnsn_diffuse *dmnsn_new_lambertian(dmnsn_pool *pool, double diffuse); - -/** - * A phong specular highlight. - * @param[in] pool The memory pool to allocate from. - * @param[in] specular The specular reflection coefficient. - * @param[in] exp The exponent (roughly the highlight size). - * @return A phong specular finish component. - */ -dmnsn_specular *dmnsn_new_phong(dmnsn_pool *pool, double specular, double exp); - -/** - * Specular (mirror) reflection. - * @param[in] pool The memory pool to allocate from. - * @param[in] min Reflection at paralell angles. - * @param[in] max Reflection at perpendicular angles (often == \p min). - * @param[in] falloff Degree of exponential falloff (usually 1). - * @return A reflective finish component. - */ -dmnsn_reflection *dmnsn_new_basic_reflection(dmnsn_pool *pool, dmnsn_color min, dmnsn_color max, double falloff); diff --git a/libdimension/dimension/future.h b/libdimension/dimension/future.h deleted file mode 100644 index 9ba28b1..0000000 --- a/libdimension/dimension/future.h +++ /dev/null @@ -1,79 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * An interface for asynchronous tasks. *_async() versions of functions - * return a dmnsn_future* object which can indicate the progress of the - * background task, and wait for task completion. The task's return value - * is returned as an int from dmnsn_finish_progress(). - */ - -/** A future object. */ -typedef struct dmnsn_future dmnsn_future; - -/** - * Join the worker thread and return its integer return value in addition to - * deleting \p future. - * @param[in,out] future The background task to join. - * @return The return value of the background task. - */ -int dmnsn_future_join(dmnsn_future *future); - -/** - * Interrupt the execution of a background thread. - * @param[in,out] future The background task to cancel. - */ -void dmnsn_future_cancel(dmnsn_future *future); - -/** - * Get the progress of the background task. - * @param[in] future The background task to examine. - * @return The progress of the background task, in [0.0, 1.0]. - */ -double dmnsn_future_progress(const dmnsn_future *future); - -/** - * Find out if a background task is finished. - * @param[in] future The background task to examine. - * @return true if the task is done, false otherwise. - */ -bool dmnsn_future_is_done(const dmnsn_future *future); - -/** - * Wait for a certain amount of progress. Always use this rather than - * spinlocking. - * @param[in] future The background task to monitor. - * @param[in] progress The progress value to wait for. - */ -void dmnsn_future_wait(const dmnsn_future *future, double progress); - -/** - * Pause all threads working on the given future. Once this function returns, - * it is safe to examine the intermediate state of the asynchronous computation. - * @param[in,out] future The background task to pause. - */ -void dmnsn_future_pause(dmnsn_future *future); - -/** - * Resume a previously paused future object. - * @param[in,out] future The background task to resume. - */ -void dmnsn_future_resume(dmnsn_future *future); diff --git a/libdimension/dimension/geometry.h b/libdimension/dimension/geometry.h deleted file mode 100644 index 2ea10ca..0000000 --- a/libdimension/dimension/geometry.h +++ /dev/null @@ -1,500 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Core geometric types like vectors, matricies, and rays. - */ - -#include -#include - -/** A vector in 3 dimensions. */ -typedef struct dmnsn_vector { - double x; /**< The x component. */ - double y; /**< The y component. */ - double z; /**< The z component. */ -} dmnsn_vector; - -/** A standard format string for vectors. */ -#define DMNSN_VECTOR_FORMAT "<%g, %g, %g>" -/** The appropriate arguements to printf() a vector. */ -#define DMNSN_VECTOR_PRINTF(v) (v).x, (v).y, (v).z - -/** A 4x4 affine transformation matrix, with implied [0 0 0 1] bottom row. */ -typedef struct dmnsn_matrix { - double n[3][4]; /**< The matrix elements in row-major order. */ -} dmnsn_matrix; - -/** A standard format string for matricies. */ -#define DMNSN_MATRIX_FORMAT \ - "[%g\t%g\t%g\t%g]\n" \ - "[%g\t%g\t%g\t%g]\n" \ - "[%g\t%g\t%g\t%g]\n" \ - "[%g\t%g\t%g\t%g]" -/** The appropriate arguements to printf() a matrix. */ -#define DMNSN_MATRIX_PRINTF(m) \ - (m).n[0][0], (m).n[0][1], (m).n[0][2], (m).n[0][3], \ - (m).n[1][0], (m).n[1][1], (m).n[1][2], (m).n[1][3], \ - (m).n[2][0], (m).n[2][1], (m).n[2][2], (m).n[2][3], \ - 0.0, 0.0, 0.0, 1.0 - -/** A line, or ray. */ -typedef struct dmnsn_line { - dmnsn_vector x0; /**< A point on the line. */ - dmnsn_vector n; /**< A normal vector; the direction of the line. */ -} dmnsn_line; - -/** A standard format string for lines. */ -#define DMNSN_LINE_FORMAT "(<%g, %g, %g> + t*<%g, %g, %g>)" -/** The appropriate arguements to printf() a line. */ -#define DMNSN_LINE_PRINTF(l) \ - DMNSN_VECTOR_PRINTF((l).x0), DMNSN_VECTOR_PRINTF((l).n) - -/** An axis-aligned bounding box (AABB). */ -typedef struct dmnsn_bounding_box { - dmnsn_vector min; /**< The coordinate-wise minimum extent of the box. */ - dmnsn_vector max; /**< The coordinate-wise maximum extent of the box. */ -} dmnsn_bounding_box; - -/** A standard format string for bounding boxes. */ -#define DMNSN_BOUNDING_BOX_FORMAT "(<%g, %g, %g> ==> <%g, %g, %g>)" -/** The appropriate arguements to printf() a bounding box. */ -#define DMNSN_BOUNDING_BOX_PRINTF(box) \ - DMNSN_VECTOR_PRINTF((box).min), DMNSN_VECTOR_PRINTF((box).max) - -/* Constants */ - -/** The zero vector. */ -static const dmnsn_vector dmnsn_zero = { 0.0, 0.0, 0.0 }; -/** The x vector. */ -static const dmnsn_vector dmnsn_x = { 1.0, 0.0, 0.0 }; -/** The y vector. */ -static const dmnsn_vector dmnsn_y = { 0.0, 1.0, 0.0 }; -/** The z vector. */ -static const dmnsn_vector dmnsn_z = { 0.0, 0.0, 1.0 }; - -/* Shorthand for vector/matrix construction */ - -/** Construct a new vector. */ -DMNSN_INLINE dmnsn_vector -dmnsn_new_vector(double x, double y, double z) -{ - dmnsn_vector v = { x, y, z }; - return v; -} - -/** Construct a new transformation matrix. */ -DMNSN_INLINE dmnsn_matrix -dmnsn_new_matrix(double a0, double a1, double a2, double a3, - double b0, double b1, double b2, double b3, - double c0, double c1, double c2, double c3) -{ - dmnsn_matrix m = { { { a0, a1, a2, a3 }, - { b0, b1, b2, b3 }, - { c0, c1, c2, c3 } } }; - return m; -} - -/** Construct a new transformation matrix from column vectors. */ -DMNSN_INLINE dmnsn_matrix -dmnsn_new_matrix4(dmnsn_vector a, dmnsn_vector b, dmnsn_vector c, - dmnsn_vector d) -{ - dmnsn_matrix m = { { { a.x, b.x, c.x, d.x }, - { a.y, b.y, c.y, d.y }, - { a.z, b.z, c.z, d.z } } }; - return m; -} - -/** Extract column vectors from a matrix. */ -DMNSN_INLINE dmnsn_vector -dmnsn_matrix_column(dmnsn_matrix M, unsigned int i) -{ - return dmnsn_new_vector(M.n[0][i], M.n[1][i], M.n[2][i]); -} - -/** Return the identity matrix. */ -dmnsn_matrix dmnsn_identity_matrix(void); - -/** - * A scale transformation. - * @param[in] s A vector with components representing the scaling factor in - * each axis. - * @return The transformation matrix. - */ -dmnsn_matrix dmnsn_scale_matrix(dmnsn_vector s); -/** - * A translation. - * @param[in] d The vector to translate by. - * @return The transformation matrix. - */ -dmnsn_matrix dmnsn_translation_matrix(dmnsn_vector d); -/** - * A left-handed rotation. - * @param[in] theta A vector representing an axis and angle. - * @f$ axis = \vec{\theta}/|\vec{\theta}| @f$, - * @f$ angle = |\vec{\theta}| @f$ - * @return The transformation matrix. - */ -dmnsn_matrix dmnsn_rotation_matrix(dmnsn_vector theta); -/** - * An alignment matrix. - * @param[in] from The initial vector. - * @param[in] to The desired direction. - * @param[in] axis1 The first axis about which to rotate. - * @param[in] axis2 The second axis about which to rotate. - * @return A transformation matrix that will rotate \p from to \p to. - */ -dmnsn_matrix dmnsn_alignment_matrix(dmnsn_vector from, dmnsn_vector to, - dmnsn_vector axis1, dmnsn_vector axis2); - -/** - * Construct a new line. - * @param[in] x0 A point on the line. - * @param[in] n The direction of the line. - * @return The new line. - */ -DMNSN_INLINE dmnsn_line -dmnsn_new_line(dmnsn_vector x0, dmnsn_vector n) -{ - dmnsn_line l = { x0, n }; - return l; -} - -/** - * Construct a new bounding box. - * @param[in] min The minimal extent of the bounding box. - * @param[in] max The maximal extent of the bounding box. - * @return The new bounding box. - */ -DMNSN_INLINE dmnsn_bounding_box -dmnsn_new_bounding_box(dmnsn_vector min, dmnsn_vector max) -{ - dmnsn_bounding_box box = { min, max }; - return box; -} - -/** Return the bounding box which contains nothing. */ -DMNSN_INLINE dmnsn_bounding_box -dmnsn_zero_bounding_box(void) -{ - dmnsn_bounding_box box = { - { DMNSN_INFINITY, DMNSN_INFINITY, DMNSN_INFINITY }, - { -DMNSN_INFINITY, -DMNSN_INFINITY, -DMNSN_INFINITY } - }; - return box; -} - -/** Return the bounding box which contains everything. */ -DMNSN_INLINE dmnsn_bounding_box -dmnsn_infinite_bounding_box(void) -{ - dmnsn_bounding_box box = { - { -DMNSN_INFINITY, -DMNSN_INFINITY, -DMNSN_INFINITY }, - { DMNSN_INFINITY, DMNSN_INFINITY, DMNSN_INFINITY } - }; - return box; -} - -/* Vector and matrix arithmetic */ - -/** Negate a vector. */ -DMNSN_INLINE dmnsn_vector -dmnsn_vector_negate(dmnsn_vector rhs) -{ - /* 3 negations */ - dmnsn_vector v = { -rhs.x, -rhs.y, -rhs.z }; - return v; -} - -/** Add two vectors. */ -DMNSN_INLINE dmnsn_vector -dmnsn_vector_add(dmnsn_vector lhs, dmnsn_vector rhs) -{ - /* 3 additions */ - dmnsn_vector v = { lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z }; - return v; -} - -/** Subtract two vectors. */ -DMNSN_INLINE dmnsn_vector -dmnsn_vector_sub(dmnsn_vector lhs, dmnsn_vector rhs) -{ - /* 3 additions */ - dmnsn_vector v = { lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z }; - return v; -} - -/** Multiply a vector by a scalar. */ -DMNSN_INLINE dmnsn_vector -dmnsn_vector_mul(double lhs, dmnsn_vector rhs) -{ - /* 3 multiplications */ - dmnsn_vector v = { lhs*rhs.x, lhs*rhs.y, lhs*rhs.z }; - return v; -} - -/** Divide a vector by a scalar. */ -DMNSN_INLINE dmnsn_vector -dmnsn_vector_div(dmnsn_vector lhs, double rhs) -{ - /* 3 divisions */ - dmnsn_vector v = { lhs.x/rhs, lhs.y/rhs, lhs.z/rhs }; - return v; -} - -/** Return the dot product of two vectors. */ -DMNSN_INLINE double -dmnsn_vector_dot(dmnsn_vector lhs, dmnsn_vector rhs) -{ - /* 3 multiplications, 2 additions */ - return lhs.x*rhs.x + lhs.y*rhs.y + lhs.z*rhs.z; -} - -/** Return the cross product of two vectors. */ -DMNSN_INLINE dmnsn_vector -dmnsn_vector_cross(dmnsn_vector lhs, dmnsn_vector rhs) -{ - /* 6 multiplications, 3 additions */ - dmnsn_vector v = { lhs.y*rhs.z - lhs.z*rhs.y, - lhs.z*rhs.x - lhs.x*rhs.z, - lhs.x*rhs.y - lhs.y*rhs.x }; - return v; -} - -/** Return the projection of \p u onto \p d. */ -DMNSN_INLINE dmnsn_vector -dmnsn_vector_proj(dmnsn_vector u, dmnsn_vector d) -{ - /* 1 division, 9 multiplications, 4 additions */ - return dmnsn_vector_mul(dmnsn_vector_dot(u, d)/dmnsn_vector_dot(d, d), d); -} - -/** Return the magnitude of a vector. */ -DMNSN_INLINE double -dmnsn_vector_norm(dmnsn_vector n) -{ - /* 1 sqrt, 3 multiplications, 2 additions */ - return sqrt(dmnsn_vector_dot(n, n)); -} - -/** Return the direction of a vector. */ -DMNSN_INLINE dmnsn_vector -dmnsn_vector_normalized(dmnsn_vector n) -{ - /* 1 sqrt, 3 divisions, 3 multiplications, 2 additions */ - return dmnsn_vector_div(n, dmnsn_vector_norm(n)); -} - -/** Return the component-wise minimum of two vectors. */ -DMNSN_INLINE dmnsn_vector -dmnsn_vector_min(dmnsn_vector a, dmnsn_vector b) -{ - return dmnsn_new_vector( - dmnsn_min(a.x, b.x), - dmnsn_min(a.y, b.y), - dmnsn_min(a.z, b.z) - ); -} - -/** Return the component-wise maximum of two vectors. */ -DMNSN_INLINE dmnsn_vector -dmnsn_vector_max(dmnsn_vector a, dmnsn_vector b) -{ - return dmnsn_new_vector( - dmnsn_max(a.x, b.x), - dmnsn_max(a.y, b.y), - dmnsn_max(a.z, b.z) - ); -} - -/** Invert a matrix. */ -dmnsn_matrix dmnsn_matrix_inverse(dmnsn_matrix A); - -/** Multiply two matricies. */ -dmnsn_matrix dmnsn_matrix_mul(dmnsn_matrix lhs, dmnsn_matrix rhs); - -/** Transform a point by a matrix. */ -DMNSN_INLINE dmnsn_vector -dmnsn_transform_point(dmnsn_matrix T, dmnsn_vector v) -{ - /* 9 multiplications, 9 additions */ - dmnsn_vector r; - r.x = T.n[0][0]*v.x + T.n[0][1]*v.y + T.n[0][2]*v.z + T.n[0][3]; - r.y = T.n[1][0]*v.x + T.n[1][1]*v.y + T.n[1][2]*v.z + T.n[1][3]; - r.z = T.n[2][0]*v.x + T.n[2][1]*v.y + T.n[2][2]*v.z + T.n[2][3]; - return r; -} - -/** Transform a direction by a matrix. */ -DMNSN_INLINE dmnsn_vector -dmnsn_transform_direction(dmnsn_matrix T, dmnsn_vector v) -{ - /* 9 multiplications, 6 additions */ - dmnsn_vector r; - r.x = T.n[0][0]*v.x + T.n[0][1]*v.y + T.n[0][2]*v.z; - r.y = T.n[1][0]*v.x + T.n[1][1]*v.y + T.n[1][2]*v.z; - r.z = T.n[2][0]*v.x + T.n[2][1]*v.y + T.n[2][2]*v.z; - return r; -} - -/** - * Transform a pseudovector by a matrix. - * @param[in] Tinv The inverse of the transformation matrix. - * @param[in] v The pseudovector to transform - * @return The transformed pseudovector. - */ -DMNSN_INLINE dmnsn_vector -dmnsn_transform_normal(dmnsn_matrix Tinv, dmnsn_vector v) -{ - /* Multiply by the transpose of the inverse - (9 multiplications, 6 additions) */ - dmnsn_vector r; - r.x = Tinv.n[0][0]*v.x + Tinv.n[1][0]*v.y + Tinv.n[2][0]*v.z; - r.y = Tinv.n[0][1]*v.x + Tinv.n[1][1]*v.y + Tinv.n[2][1]*v.z; - r.z = Tinv.n[0][2]*v.x + Tinv.n[1][2]*v.y + Tinv.n[2][2]*v.z; - return r; -} - -/** Transform a bounding box by a matrix. */ -dmnsn_bounding_box dmnsn_transform_bounding_box(dmnsn_matrix T, - dmnsn_bounding_box box); - -/** - * Transform a line by a matrix. - * \f$ n' = T(l.\vec{x_0} + l.\vec{n}) - T(l.\vec{x_0}) \f$, - * \f$ \vec{x_0}' = T(l.\vec{x_0}) \f$ - */ -DMNSN_INLINE dmnsn_line -dmnsn_transform_line(dmnsn_matrix T, dmnsn_line l) -{ - /* 18 multiplications, 15 additions */ - dmnsn_line ret; - ret.x0 = dmnsn_transform_point(T, l.x0); - ret.n = dmnsn_transform_direction(T, l.n); - return ret; -} - -/** - * Return the point at \p t on a line. - * The point is defined by \f$ l.\vec{x_0} + t \cdot l.\vec{n} \f$ - */ -DMNSN_INLINE dmnsn_vector -dmnsn_line_point(dmnsn_line l, double t) -{ - return dmnsn_vector_add(l.x0, dmnsn_vector_mul(t, l.n)); -} - -/** Add epsilon*l.n to l.x0, to avoid self-intersections. */ -DMNSN_INLINE dmnsn_line -dmnsn_line_add_epsilon(dmnsn_line l) -{ - return dmnsn_new_line( - dmnsn_vector_add( - l.x0, - dmnsn_vector_mul(1.0e3*dmnsn_epsilon, l.n) - ), - l.n - ); -} - -/** - * Construct a new symmetric bounding box. - * @param[in] r The extent of the bounding box from the origin. - * @return The new bounding box. - */ -DMNSN_INLINE dmnsn_bounding_box -dmnsn_symmetric_bounding_box(dmnsn_vector r) -{ - dmnsn_vector minus_r = dmnsn_vector_negate(r); - dmnsn_bounding_box box = { - dmnsn_vector_min(r, minus_r), - dmnsn_vector_max(r, minus_r) - }; - return box; -} - -/** Return whether \p p is within the axis-aligned bounding box. */ -DMNSN_INLINE bool -dmnsn_bounding_box_contains(dmnsn_bounding_box box, dmnsn_vector p) -{ - return (p.x >= box.min.x && p.y >= box.min.y && p.z >= box.min.z) - && (p.x <= box.max.x && p.y <= box.max.y && p.z <= box.max.z); -} - -/** Return whether a bounding box is infinite. */ -DMNSN_INLINE bool -dmnsn_bounding_box_is_infinite(dmnsn_bounding_box box) -{ - return box.min.x == -DMNSN_INFINITY; -} - -/** - * Expand a bounding box to contain a point - * @param[in] box The bounding box to expand. - * @param[in] point The point to swallow. - * @return The expanded bounding box. - */ -DMNSN_INLINE dmnsn_bounding_box -dmnsn_bounding_box_swallow(dmnsn_bounding_box box, dmnsn_vector point) -{ - dmnsn_bounding_box ret = { - dmnsn_vector_min(box.min, point), - dmnsn_vector_max(box.max, point) - }; - return ret; -} - -/** Return whether a vector contains any NaN components. */ -DMNSN_INLINE bool -dmnsn_vector_isnan(dmnsn_vector v) -{ - return dmnsn_isnan(v.x) || dmnsn_isnan(v.y) || dmnsn_isnan(v.z); -} - -/** Return whether a matrix contains any NaN components. */ -DMNSN_INLINE bool -dmnsn_matrix_isnan(dmnsn_matrix m) -{ - size_t i, j; - for (i = 0; i < 3; ++i) { - for (j = 0; j < 4; ++j) { - if (dmnsn_isnan(m.n[i][j])) { - return true; - } - } - } - return false; -} - -/** Return whether a line contains any NaN entries. */ -DMNSN_INLINE bool -dmnsn_line_isnan(dmnsn_line l) -{ - return dmnsn_vector_isnan(l.x0) || dmnsn_vector_isnan(l.n); -} - -/** Return whether a bounding box has any NaN components. */ -DMNSN_INLINE bool -dmnsn_bounding_box_isnan(dmnsn_bounding_box box) -{ - return dmnsn_vector_isnan(box.min) || dmnsn_vector_isnan(box.max); -} diff --git a/libdimension/dimension/gl.h b/libdimension/dimension/gl.h deleted file mode 100644 index 284a248..0000000 --- a/libdimension/dimension/gl.h +++ /dev/null @@ -1,48 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * OpenGL export/import of canvases. - */ - -/** - * Optimize a canvas for GL drawing - * @param[in] pool The memory pool to allocate from. - * @param[in,out] canvas The canvas to optimize. - * @return Whether the canvas was successfully optimized. - */ -int dmnsn_gl_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas); - -/** - * Write canvas to GL framebuffer. - * @param[in] canvas The canvas to draw. - * @return 0 on success, non-zero on failure. - */ -int dmnsn_gl_write_canvas(const dmnsn_canvas *canvas); - -/** - * Read a canvas from a GL framebuffer. - * @param[in] canvas The canvas to write to. - * @param[in] x0 The \a x screen coordinate to start copying from. - * @param[in] y0 The \a y screen coordinate to start copying from. - * @return 0 on success, non-zero on failure. - */ -int dmnsn_gl_read_canvas(dmnsn_canvas *canvas, size_t x0, size_t y0); diff --git a/libdimension/dimension/interior.h b/libdimension/dimension/interior.h deleted file mode 100644 index 0ff697d..0000000 --- a/libdimension/dimension/interior.h +++ /dev/null @@ -1,44 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Object interiors. - */ - -/** An interior. */ -typedef struct dmnsn_interior { - double ior; /**< Refractive index. */ -} dmnsn_interior; - -/** - * Create an interior object. - * @param[in] pool The memory pool to allocate from. - * @return The new interior. - */ -dmnsn_interior *dmnsn_new_interior(dmnsn_pool *pool); - -/** - * Fill missing interior properties from a default interior. - * @param[in] default_interior The default interior. - * @param[in,out] interiorp A pointer to the interior to fill. - */ -void dmnsn_interior_cascade(dmnsn_interior *default_interior, - dmnsn_interior **interiorp); diff --git a/libdimension/dimension/light.h b/libdimension/dimension/light.h deleted file mode 100644 index 218611d..0000000 --- a/libdimension/dimension/light.h +++ /dev/null @@ -1,76 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Lights. - */ - -#include - -/* Forward-declar dmnsn_light */ -typedef struct dmnsn_light dmnsn_light; - -/** - * Light direction callback. - * @param[in] light The light itself. - * @param[in] v The point to illuminate. - * @return The direction of light rays pointing from \p v - */ -typedef dmnsn_vector dmnsn_light_direction_fn(const dmnsn_light *light, - dmnsn_vector v); - -/** - * Light illumination callback. - * @param[in] light The light itself. - * @param[in] v The point to illuminate. - * @return The color of the light at \p v. - */ -typedef dmnsn_color dmnsn_light_illumination_fn(const dmnsn_light *light, - dmnsn_vector v); - -/** - * Light shadow callback. - * @param[in] light The light itself. - * @param[in] t The line index of the closest shadow ray intersection. - * @return Whether the point is in shadow. - */ -typedef bool dmnsn_light_shadow_fn(const dmnsn_light *light, double t); - -/** A light. */ -struct dmnsn_light { - /* Callbacks */ - dmnsn_light_direction_fn *direction_fn; /**< Direction callback. */ - dmnsn_light_illumination_fn *illumination_fn; /**< Illumination callback. */ - dmnsn_light_shadow_fn *shadow_fn; /**< Shadow callback. */ -}; - -/** - * Create a dummy light. - * @param[in] pool The memory pool to allocate from. - * @return The allocated light. - */ -dmnsn_light *dmnsn_new_light(dmnsn_pool *pool); - -/** - * Initialize a dmnsn_light field. - * @param[out] light The light to initialize. - */ -void dmnsn_init_light(dmnsn_light *light); diff --git a/libdimension/dimension/lights.h b/libdimension/dimension/lights.h deleted file mode 100644 index e7de4cc..0000000 --- a/libdimension/dimension/lights.h +++ /dev/null @@ -1,33 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Pre-defined light types. - */ - -/** - * A point light. - * @param[in] pool The memory pool to allocate from. - * @param[in] x0 The origin of the light. - * @param[in] color The color of the light. - * @return A point light. - */ -dmnsn_light *dmnsn_new_point_light(dmnsn_pool *pool, dmnsn_vector x0, dmnsn_color color); diff --git a/libdimension/dimension/malloc.h b/libdimension/dimension/malloc.h deleted file mode 100644 index 742e3a2..0000000 --- a/libdimension/dimension/malloc.h +++ /dev/null @@ -1,66 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Dynamic memory. dmnsn_malloc() and friends behave like their - * non-dmnsn_-prefixed counterparts, but never return NULL. If allocation - * fails, they instead call dmnsn_error(). - */ - -#include /* For size_t */ - -/** - * Allocate some memory. Always use dmnsn_free() to free this memory, never - * free(). - * @param[in] size The size of the memory block to allocate. - * @return The allocated memory area. - */ -void *dmnsn_malloc(size_t size); - -/** - * Allocate some memory. Always use dmnsn_free() to free this memory, never - * free(). - * @param[in] type The type of the memory block to allocate. - * @return The allocated memory area. - */ -#define DMNSN_MALLOC(type) ((type *)dmnsn_malloc(sizeof(type))) - -/** - * Expand or shrink an allocation created by dmnsn_malloc(). - * @param[in] ptr The block to resize. - * @param[in] size The new size. - * @return The resized memory area. - */ -void *dmnsn_realloc(void *ptr, size_t size); - -/** - * Duplicate a string. - * @param[in] s The string to duplicate. - * @return A string with the same contents as \p s, suitable for release by - * dmnsn_free(). - */ -char *dmnsn_strdup(const char *s); - -/** - * Free memory allocated by dmnsn_malloc() or dmnsn_strdup(). - * @param[in] ptr The memory block to free, or NULL. - */ -void dmnsn_free(void *ptr); diff --git a/libdimension/dimension/map.h b/libdimension/dimension/map.h deleted file mode 100644 index 7229a24..0000000 --- a/libdimension/dimension/map.h +++ /dev/null @@ -1,68 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Generic maps (backend for pigment_maps, etc.). - */ - -/** A map. */ -typedef struct dmnsn_map dmnsn_map; - -/** - * Create an empty map. - * @param[in] pool The memory pool to allocate from. - * @param[in] size The size of the objects to store in the map. - * @return A map with no entries. - */ -dmnsn_map *dmnsn_new_map(dmnsn_pool *pool, size_t size); - -/** - * Add an entry (a scalar-object pair) to a map. - * @param[in,out] map The map to add to. - * @param[in] n The index of the entry. - * @param[in] obj The value of the entry. - */ -void dmnsn_map_add_entry(dmnsn_map *map, double n, const void *obj); - -/** - * Return the number of entries in a map. - * @param[in] map The map to measure. - * @return The size of \p map. - */ -size_t dmnsn_map_size(const dmnsn_map *map); - -/** - * Evaluate a map. - * @param[in] map The map to evaluate. - * @param[in] n The index to evaluate. - * @param[out] val The normalized distance of \p n from \p obj1. - * @param[out] obj1 The first object. - * @param[out] obj2 The second object. - */ -void dmnsn_map_evaluate(const dmnsn_map *map, double n, - double *val, void *obj1, void *obj2); - -/** - * Apply a callback to each element of a map. - * @param[in,out] map The map. - * @param[in] callback The callback to apply to the elements. - */ -void dmnsn_map_apply(dmnsn_map *map, dmnsn_callback_fn *callback); diff --git a/libdimension/dimension/math.h b/libdimension/dimension/math.h index 597be36..603373f 100644 --- a/libdimension/dimension/math.h +++ b/libdimension/dimension/math.h @@ -20,80 +20,26 @@ /** * @file - * Useful math functions. + * Mathematical functions and types. */ -#include -#include +#ifndef DMNSN_MATH_H +#define DMNSN_MATH_H -/** The smallest value considered non-zero by some numerical algorithms. */ -#define dmnsn_epsilon 1.0e-10 - -/** - * @def DMNSN_INFINITY - * Expands to floating-point infinity. - */ -#if defined(INFINITY) || DMNSN_C99 - #define DMNSN_INFINITY INFINITY -#else - #define DMNSN_INFINITY HUGE_VAL +#ifdef __cplusplus +extern "C" { #endif -/** Find the minimum of two values. */ -DMNSN_INLINE double -dmnsn_min(double a, double b) -{ - return a < b ? a : b; -} - -/** Find the maximum of two values. */ -DMNSN_INLINE double -dmnsn_max(double a, double b) -{ - return a > b ? a : b; -} - -/** Clamp a value to an interval. */ -DMNSN_INLINE double -dmnsn_clamp(double n, double min, double max) -{ - return dmnsn_min(dmnsn_max(n, min), max); -} - -/** Convert degrees to radians. */ -DMNSN_INLINE double -dmnsn_radians(double degrees) -{ - return degrees*(atan(1.0)/45.0); -} +#include -/** Convert radians to degrees. */ -DMNSN_INLINE double -dmnsn_degrees(double radians) -{ - return radians*(45.0/atan(1.0)); -} +#include +#include +#include +#include +#include -/** Signum function: return the sign of a value. */ -DMNSN_INLINE int -dmnsn_sgn(double n) -{ - if (n > 0.0) { - return 1; - } else if (n < 0.0) { - return -1; - } else { - return 0; - } +#ifdef __cplusplus } - -/** Return whether a value is NaN. */ -DMNSN_INLINE bool -dmnsn_isnan(double n) -{ -#if DMNSN_C99 - return isnan(n); -#else - return n != n; #endif -} + +#endif /* DMNSN_MATH_H */ diff --git a/libdimension/dimension/math/aabb.h b/libdimension/dimension/math/aabb.h new file mode 100644 index 0000000..14cc575 --- /dev/null +++ b/libdimension/dimension/math/aabb.h @@ -0,0 +1,129 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Axis-aligned bounding boxes. + */ + +#ifndef DMNSN_MATH_H +#error "Please include instead of this header directly." +#endif + +/** An axis-aligned bounding box. */ +typedef struct dmnsn_aabb { + dmnsn_vector min; /**< The coordinate-wise minimum extent of the box. */ + dmnsn_vector max; /**< The coordinate-wise maximum extent of the box. */ +} dmnsn_aabb; + +/** A standard format string for bounding boxes. */ +#define DMNSN_AABB_FORMAT "(<%g, %g, %g> ==> <%g, %g, %g>)" +/** The appropriate arguements to printf() a bounding box. */ +#define DMNSN_AABB_PRINTF(box) \ + DMNSN_VECTOR_PRINTF((box).min), DMNSN_VECTOR_PRINTF((box).max) + +/** + * Construct a new bounding box. + * @param[in] min The minimal extent of the bounding box. + * @param[in] max The maximal extent of the bounding box. + * @return The new bounding box. + */ +DMNSN_INLINE dmnsn_aabb +dmnsn_new_aabb(dmnsn_vector min, dmnsn_vector max) +{ + dmnsn_aabb box = { min, max }; + return box; +} + +/** Return the bounding box which contains nothing. */ +DMNSN_INLINE dmnsn_aabb +dmnsn_zero_aabb(void) +{ + dmnsn_aabb box = { + { DMNSN_INFINITY, DMNSN_INFINITY, DMNSN_INFINITY }, + { -DMNSN_INFINITY, -DMNSN_INFINITY, -DMNSN_INFINITY } + }; + return box; +} + +/** Return the bounding box which contains everything. */ +DMNSN_INLINE dmnsn_aabb +dmnsn_infinite_aabb(void) +{ + dmnsn_aabb box = { + { -DMNSN_INFINITY, -DMNSN_INFINITY, -DMNSN_INFINITY }, + { DMNSN_INFINITY, DMNSN_INFINITY, DMNSN_INFINITY } + }; + return box; +} + +/** + * Construct a new symmetric bounding box. + * @param[in] r The extent of the bounding box from the origin. + * @return The new bounding box. + */ +DMNSN_INLINE dmnsn_aabb +dmnsn_symmetric_aabb(dmnsn_vector r) +{ + dmnsn_vector minus_r = dmnsn_vector_negate(r); + dmnsn_aabb box = { + dmnsn_vector_min(r, minus_r), + dmnsn_vector_max(r, minus_r) + }; + return box; +} + +/** Return whether \p p is within the axis-aligned bounding box. */ +DMNSN_INLINE bool +dmnsn_aabb_contains(dmnsn_aabb box, dmnsn_vector p) +{ + return (p.x >= box.min.x && p.y >= box.min.y && p.z >= box.min.z) + && (p.x <= box.max.x && p.y <= box.max.y && p.z <= box.max.z); +} + +/** Return whether a bounding box is infinite. */ +DMNSN_INLINE bool +dmnsn_aabb_is_infinite(dmnsn_aabb box) +{ + return box.min.x == -DMNSN_INFINITY; +} + +/** + * Expand a bounding box to contain a point + * @param[in] box The bounding box to expand. + * @param[in] point The point to swallow. + * @return The expanded bounding box. + */ +DMNSN_INLINE dmnsn_aabb +dmnsn_aabb_swallow(dmnsn_aabb box, dmnsn_vector point) +{ + dmnsn_aabb ret = { + dmnsn_vector_min(box.min, point), + dmnsn_vector_max(box.max, point) + }; + return ret; +} + +/** Return whether a bounding box has any NaN components. */ +DMNSN_INLINE bool +dmnsn_aabb_isnan(dmnsn_aabb box) +{ + return dmnsn_vector_isnan(box.min) || dmnsn_vector_isnan(box.max); +} diff --git a/libdimension/dimension/math/matrix.h b/libdimension/dimension/math/matrix.h new file mode 100644 index 0000000..7471bf5 --- /dev/null +++ b/libdimension/dimension/math/matrix.h @@ -0,0 +1,193 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Affine transformation matrices. + */ + +#ifndef DMNSN_MATH_H +#error "Please include instead of this header directly." +#endif + +/** A 4x4 affine transformation matrix, with implied [0 0 0 1] bottom row. */ +typedef struct dmnsn_matrix { + double n[3][4]; /**< The matrix elements in row-major order. */ +} dmnsn_matrix; + +/** A standard format string for matricies. */ +#define DMNSN_MATRIX_FORMAT \ + "[%g\t%g\t%g\t%g]\n" \ + "[%g\t%g\t%g\t%g]\n" \ + "[%g\t%g\t%g\t%g]\n" \ + "[%g\t%g\t%g\t%g]" +/** The appropriate arguements to printf() a matrix. */ +#define DMNSN_MATRIX_PRINTF(m) \ + (m).n[0][0], (m).n[0][1], (m).n[0][2], (m).n[0][3], \ + (m).n[1][0], (m).n[1][1], (m).n[1][2], (m).n[1][3], \ + (m).n[2][0], (m).n[2][1], (m).n[2][2], (m).n[2][3], \ + 0.0, 0.0, 0.0, 1.0 + +/** Construct a new transformation matrix. */ +DMNSN_INLINE dmnsn_matrix +dmnsn_new_matrix(double a0, double a1, double a2, double a3, + double b0, double b1, double b2, double b3, + double c0, double c1, double c2, double c3) +{ + dmnsn_matrix m = { { { a0, a1, a2, a3 }, + { b0, b1, b2, b3 }, + { c0, c1, c2, c3 } } }; + return m; +} + +/** Construct a new transformation matrix from column vectors. */ +DMNSN_INLINE dmnsn_matrix +dmnsn_new_matrix4(dmnsn_vector a, dmnsn_vector b, dmnsn_vector c, + dmnsn_vector d) +{ + dmnsn_matrix m = { { { a.x, b.x, c.x, d.x }, + { a.y, b.y, c.y, d.y }, + { a.z, b.z, c.z, d.z } } }; + return m; +} + +/** Extract column vectors from a matrix. */ +DMNSN_INLINE dmnsn_vector +dmnsn_matrix_column(dmnsn_matrix M, unsigned int i) +{ + return dmnsn_new_vector(M.n[0][i], M.n[1][i], M.n[2][i]); +} + +/** Return the identity matrix. */ +dmnsn_matrix dmnsn_identity_matrix(void); + +/** + * A scale transformation. + * @param[in] s A vector with components representing the scaling factor in + * each axis. + * @return The transformation matrix. + */ +dmnsn_matrix dmnsn_scale_matrix(dmnsn_vector s); +/** + * A translation. + * @param[in] d The vector to translate by. + * @return The transformation matrix. + */ +dmnsn_matrix dmnsn_translation_matrix(dmnsn_vector d); +/** + * A left-handed rotation. + * @param[in] theta A vector representing an axis and angle. + * @f$ axis = \vec{\theta}/|\vec{\theta}| @f$, + * @f$ angle = |\vec{\theta}| @f$ + * @return The transformation matrix. + */ +dmnsn_matrix dmnsn_rotation_matrix(dmnsn_vector theta); + +/** + * An alignment matrix. + * @param[in] from The initial vector. + * @param[in] to The desired direction. + * @param[in] axis1 The first axis about which to rotate. + * @param[in] axis2 The second axis about which to rotate. + * @return A transformation matrix that will rotate \p from to \p to. + */ +dmnsn_matrix dmnsn_alignment_matrix(dmnsn_vector from, dmnsn_vector to, + dmnsn_vector axis1, dmnsn_vector axis2); + +/** Invert a matrix. */ +dmnsn_matrix dmnsn_matrix_inverse(dmnsn_matrix A); + +/** Multiply two matricies. */ +dmnsn_matrix dmnsn_matrix_mul(dmnsn_matrix lhs, dmnsn_matrix rhs); + +/** Transform a point by a matrix. */ +DMNSN_INLINE dmnsn_vector +dmnsn_transform_point(dmnsn_matrix T, dmnsn_vector v) +{ + /* 9 multiplications, 9 additions */ + dmnsn_vector r; + r.x = T.n[0][0]*v.x + T.n[0][1]*v.y + T.n[0][2]*v.z + T.n[0][3]; + r.y = T.n[1][0]*v.x + T.n[1][1]*v.y + T.n[1][2]*v.z + T.n[1][3]; + r.z = T.n[2][0]*v.x + T.n[2][1]*v.y + T.n[2][2]*v.z + T.n[2][3]; + return r; +} + +/** Transform a direction by a matrix. */ +DMNSN_INLINE dmnsn_vector +dmnsn_transform_direction(dmnsn_matrix T, dmnsn_vector v) +{ + /* 9 multiplications, 6 additions */ + dmnsn_vector r; + r.x = T.n[0][0]*v.x + T.n[0][1]*v.y + T.n[0][2]*v.z; + r.y = T.n[1][0]*v.x + T.n[1][1]*v.y + T.n[1][2]*v.z; + r.z = T.n[2][0]*v.x + T.n[2][1]*v.y + T.n[2][2]*v.z; + return r; +} + +/** + * Transform a pseudovector by a matrix. + * @param[in] Tinv The inverse of the transformation matrix. + * @param[in] v The pseudovector to transform + * @return The transformed pseudovector. + */ +DMNSN_INLINE dmnsn_vector +dmnsn_transform_normal(dmnsn_matrix Tinv, dmnsn_vector v) +{ + /* Multiply by the transpose of the inverse + (9 multiplications, 6 additions) */ + dmnsn_vector r; + r.x = Tinv.n[0][0]*v.x + Tinv.n[1][0]*v.y + Tinv.n[2][0]*v.z; + r.y = Tinv.n[0][1]*v.x + Tinv.n[1][1]*v.y + Tinv.n[2][1]*v.z; + r.z = Tinv.n[0][2]*v.x + Tinv.n[1][2]*v.y + Tinv.n[2][2]*v.z; + return r; +} + +/** + * Transform a ray by a matrix. + * \f$ n' = T(l.\vec{x_0} + l.\vec{n}) - T(l.\vec{x_0}) \f$, + * \f$ \vec{x_0}' = T(l.\vec{x_0}) \f$ + */ +DMNSN_INLINE dmnsn_ray +dmnsn_transform_ray(dmnsn_matrix T, dmnsn_ray l) +{ + /* 18 multiplications, 15 additions */ + dmnsn_ray ret; + ret.x0 = dmnsn_transform_point(T, l.x0); + ret.n = dmnsn_transform_direction(T, l.n); + return ret; +} + +/** Transform a bounding box by a matrix. */ +dmnsn_aabb dmnsn_transform_aabb(dmnsn_matrix T, dmnsn_aabb box); + +/** Return whether a matrix contains any NaN components. */ +DMNSN_INLINE bool +dmnsn_matrix_isnan(dmnsn_matrix m) +{ + size_t i, j; + for (i = 0; i < 3; ++i) { + for (j = 0; j < 4; ++j) { + if (dmnsn_isnan(m.n[i][j])) { + return true; + } + } + } + return false; +} diff --git a/libdimension/dimension/math/ray.h b/libdimension/dimension/math/ray.h new file mode 100644 index 0000000..1dd98f7 --- /dev/null +++ b/libdimension/dimension/math/ray.h @@ -0,0 +1,83 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Lines in 3-D space. + */ + +#ifndef DMNSN_MATH_H +#error "Please include instead of this header directly." +#endif + +/** A line, or ray. */ +typedef struct dmnsn_ray { + dmnsn_vector x0; /**< The origin of the ray. */ + dmnsn_vector n; /**< The direction of the ray. */ +} dmnsn_ray; + +/** A standard format string for rays. */ +#define DMNSN_RAY_FORMAT "(<%g, %g, %g> + t*<%g, %g, %g>)" +/** The appropriate arguements to printf() a ray. */ +#define DMNSN_RAY_PRINTF(l) \ + DMNSN_VECTOR_PRINTF((l).x0), DMNSN_VECTOR_PRINTF((l).n) + +/** + * Construct a new ray. + * @param[in] x0 The origin of the ray. + * @param[in] n The direction of the ray. + * @return The new ray. + */ +DMNSN_INLINE dmnsn_ray +dmnsn_new_ray(dmnsn_vector x0, dmnsn_vector n) +{ + dmnsn_ray l = { x0, n }; + return l; +} + +/** + * Return the point at \p t on a ray. + * The point is defined by \f$ l.\vec{x_0} + t \cdot l.\vec{n} \f$ + */ +DMNSN_INLINE dmnsn_vector +dmnsn_ray_point(dmnsn_ray l, double t) +{ + return dmnsn_vector_add(l.x0, dmnsn_vector_mul(t, l.n)); +} + +/** Add epsilon*l.n to l.x0, to avoid self-intersections. */ +DMNSN_INLINE dmnsn_ray +dmnsn_ray_add_epsilon(dmnsn_ray l) +{ + return dmnsn_new_ray( + dmnsn_vector_add( + l.x0, + dmnsn_vector_mul(1.0e3*dmnsn_epsilon, l.n) + ), + l.n + ); +} + +/** Return whether a ray contains any NaN entries. */ +DMNSN_INLINE bool +dmnsn_ray_isnan(dmnsn_ray l) +{ + return dmnsn_vector_isnan(l.x0) || dmnsn_vector_isnan(l.n); +} diff --git a/libdimension/dimension/math/scalar.h b/libdimension/dimension/math/scalar.h new file mode 100644 index 0000000..3887c14 --- /dev/null +++ b/libdimension/dimension/math/scalar.h @@ -0,0 +1,103 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Mathematical functions of one variable. + */ + +#ifndef DMNSN_MATH_H +#error "Please include instead of this header directly." +#endif + +#include +#include + +/** The smallest value considered non-zero by some numerical algorithms. */ +#define dmnsn_epsilon 1.0e-10 + +/** + * @def DMNSN_INFINITY + * Expands to floating-point infinity. + */ +#if defined(INFINITY) || DMNSN_C99 + #define DMNSN_INFINITY INFINITY +#else + #define DMNSN_INFINITY HUGE_VAL +#endif + +/** Find the minimum of two values. */ +DMNSN_INLINE double +dmnsn_min(double a, double b) +{ + return a < b ? a : b; +} + +/** Find the maximum of two values. */ +DMNSN_INLINE double +dmnsn_max(double a, double b) +{ + return a > b ? a : b; +} + +/** Clamp a value to an interval. */ +DMNSN_INLINE double +dmnsn_clamp(double n, double min, double max) +{ + return dmnsn_min(dmnsn_max(n, min), max); +} + +/** Convert degrees to radians. */ +DMNSN_INLINE double +dmnsn_radians(double degrees) +{ + return degrees*(atan(1.0)/45.0); +} + +/** Convert radians to degrees. */ +DMNSN_INLINE double +dmnsn_degrees(double radians) +{ + return radians*(45.0/atan(1.0)); +} + +/** Signum function: return the sign of a value. */ +DMNSN_INLINE int +dmnsn_sgn(double n) +{ + if (n > 0.0) { + return 1; + } else if (n < 0.0) { + return -1; + } else { + return 0; + } +} + +/** Return whether a value is NaN. */ +DMNSN_INLINE bool +dmnsn_isnan(double n) +{ +#if DMNSN_C99 + return isnan(n); +#else + return n != n; +#endif +} diff --git a/libdimension/dimension/math/vector.h b/libdimension/dimension/math/vector.h new file mode 100644 index 0000000..8eacee9 --- /dev/null +++ b/libdimension/dimension/math/vector.h @@ -0,0 +1,183 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Vectors in 3-D space. + */ + +#ifndef DMNSN_MATH_H +#error "Please include instead of this header directly." +#endif + +#include +#include + +/** A vector in 3 dimensions. */ +typedef struct dmnsn_vector { + double x; /**< The x component. */ + double y; /**< The y component. */ + double z; /**< The z component. */ +} dmnsn_vector; + +/** A standard format string for vectors. */ +#define DMNSN_VECTOR_FORMAT "<%g, %g, %g>" +/** The appropriate arguements to printf() a vector. */ +#define DMNSN_VECTOR_PRINTF(v) (v).x, (v).y, (v).z + +/* Constants */ + +/** The zero vector. */ +static const dmnsn_vector dmnsn_zero = { 0.0, 0.0, 0.0 }; +/** The x vector. */ +static const dmnsn_vector dmnsn_x = { 1.0, 0.0, 0.0 }; +/** The y vector. */ +static const dmnsn_vector dmnsn_y = { 0.0, 1.0, 0.0 }; +/** The z vector. */ +static const dmnsn_vector dmnsn_z = { 0.0, 0.0, 1.0 }; + +/* Shorthand for vector construction */ + +/** Construct a new vector. */ +DMNSN_INLINE dmnsn_vector +dmnsn_new_vector(double x, double y, double z) +{ + dmnsn_vector v = { x, y, z }; + return v; +} + +/* Vector arithmetic */ + +/** Negate a vector. */ +DMNSN_INLINE dmnsn_vector +dmnsn_vector_negate(dmnsn_vector rhs) +{ + /* 3 negations */ + dmnsn_vector v = { -rhs.x, -rhs.y, -rhs.z }; + return v; +} + +/** Add two vectors. */ +DMNSN_INLINE dmnsn_vector +dmnsn_vector_add(dmnsn_vector lhs, dmnsn_vector rhs) +{ + /* 3 additions */ + dmnsn_vector v = { lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z }; + return v; +} + +/** Subtract two vectors. */ +DMNSN_INLINE dmnsn_vector +dmnsn_vector_sub(dmnsn_vector lhs, dmnsn_vector rhs) +{ + /* 3 additions */ + dmnsn_vector v = { lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z }; + return v; +} + +/** Multiply a vector by a scalar. */ +DMNSN_INLINE dmnsn_vector +dmnsn_vector_mul(double lhs, dmnsn_vector rhs) +{ + /* 3 multiplications */ + dmnsn_vector v = { lhs*rhs.x, lhs*rhs.y, lhs*rhs.z }; + return v; +} + +/** Divide a vector by a scalar. */ +DMNSN_INLINE dmnsn_vector +dmnsn_vector_div(dmnsn_vector lhs, double rhs) +{ + /* 3 divisions */ + dmnsn_vector v = { lhs.x/rhs, lhs.y/rhs, lhs.z/rhs }; + return v; +} + +/** Return the dot product of two vectors. */ +DMNSN_INLINE double +dmnsn_vector_dot(dmnsn_vector lhs, dmnsn_vector rhs) +{ + /* 3 multiplications, 2 additions */ + return lhs.x*rhs.x + lhs.y*rhs.y + lhs.z*rhs.z; +} + +/** Return the cross product of two vectors. */ +DMNSN_INLINE dmnsn_vector +dmnsn_vector_cross(dmnsn_vector lhs, dmnsn_vector rhs) +{ + /* 6 multiplications, 3 additions */ + dmnsn_vector v = { lhs.y*rhs.z - lhs.z*rhs.y, + lhs.z*rhs.x - lhs.x*rhs.z, + lhs.x*rhs.y - lhs.y*rhs.x }; + return v; +} + +/** Return the projection of \p u onto \p d. */ +DMNSN_INLINE dmnsn_vector +dmnsn_vector_proj(dmnsn_vector u, dmnsn_vector d) +{ + /* 1 division, 9 multiplications, 4 additions */ + return dmnsn_vector_mul(dmnsn_vector_dot(u, d)/dmnsn_vector_dot(d, d), d); +} + +/** Return the magnitude of a vector. */ +DMNSN_INLINE double +dmnsn_vector_norm(dmnsn_vector n) +{ + /* 1 sqrt, 3 multiplications, 2 additions */ + return sqrt(dmnsn_vector_dot(n, n)); +} + +/** Return the direction of a vector. */ +DMNSN_INLINE dmnsn_vector +dmnsn_vector_normalized(dmnsn_vector n) +{ + /* 1 sqrt, 3 divisions, 3 multiplications, 2 additions */ + return dmnsn_vector_div(n, dmnsn_vector_norm(n)); +} + +/** Return the component-wise minimum of two vectors. */ +DMNSN_INLINE dmnsn_vector +dmnsn_vector_min(dmnsn_vector a, dmnsn_vector b) +{ + return dmnsn_new_vector( + dmnsn_min(a.x, b.x), + dmnsn_min(a.y, b.y), + dmnsn_min(a.z, b.z) + ); +} + +/** Return the component-wise maximum of two vectors. */ +DMNSN_INLINE dmnsn_vector +dmnsn_vector_max(dmnsn_vector a, dmnsn_vector b) +{ + return dmnsn_new_vector( + dmnsn_max(a.x, b.x), + dmnsn_max(a.y, b.y), + dmnsn_max(a.z, b.z) + ); +} + +/** Return whether a vector contains any NaN components. */ +DMNSN_INLINE bool +dmnsn_vector_isnan(dmnsn_vector v) +{ + return dmnsn_isnan(v.x) || dmnsn_isnan(v.y) || dmnsn_isnan(v.z); +} diff --git a/libdimension/dimension/model.h b/libdimension/dimension/model.h new file mode 100644 index 0000000..5367729 --- /dev/null +++ b/libdimension/dimension/model.h @@ -0,0 +1,59 @@ +/************************************************************************* + * Copyright (C) 2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Scene modeling. + */ + +#ifndef DMNSN_MODEL_H +#define DMNSN_MODEL_H + +#include "dimension/base.h" +#include "dimension/color.h" +#include "dimension/canvas.h" +#include "dimension/pattern.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +} +#endif + +#endif /* DMNSN_MODEL_H */ diff --git a/libdimension/dimension/model/camera.h b/libdimension/dimension/model/camera.h new file mode 100644 index 0000000..37f80b9 --- /dev/null +++ b/libdimension/dimension/model/camera.h @@ -0,0 +1,66 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Cameras. + */ + +/* Forward-declare dmnsn_camera */ +typedef struct dmnsn_camera dmnsn_camera; + +/** + * Camera ray callback. + * @param[in] camera The camera itself. + * @param[in] x The x coordinate of the pixel (in [0, 1]). + * @param[in] y The y coordinate of the pixel (in [0, 1]). + * @return The ray through (\p x, \p y). + */ +typedef dmnsn_ray dmnsn_camera_ray_fn(const dmnsn_camera *camera, double x, double y); + +/** A camera. */ +struct dmnsn_camera { + /* Callback functions */ + dmnsn_camera_ray_fn *ray_fn; /**< Camera ray callback. */ + + dmnsn_matrix trans; /**< Transformation matrix. */ +}; + +/** + * Create a dummy camera. + * @param[in] pool The memory pool to allocate from. + * @return The allocated camera. + */ +dmnsn_camera *dmnsn_new_camera(dmnsn_pool *pool); + +/** + * Initialize a dmnsn_camera field. + * @param[out] camera The camera to initialize. + */ +void dmnsn_init_camera(dmnsn_camera *camera); + +/** + * Invoke the camera ray callback, then correctly transform the ray. + * @param[in] camera The camera itself. + * @param[in] x The x coordinate of the pixel (in [0, 1]). + * @param[in] y The y coordinate of the pixel (in [0, 1]). + * @return The ray through (\p x, \p y). + */ +dmnsn_ray dmnsn_camera_ray(const dmnsn_camera *camera, double x, double y); diff --git a/libdimension/dimension/model/cameras.h b/libdimension/dimension/model/cameras.h new file mode 100644 index 0000000..9ef2646 --- /dev/null +++ b/libdimension/dimension/model/cameras.h @@ -0,0 +1,34 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Pre-defined camera types. + */ + +/** + * A perspective camera. The camera is located at the origin, looking at + * (0, 0, 1). The feild of view is the section of the plane z = 1 from + * (-0.5, -0.5) to (0.5, 0.5). Rays are transformed by the camera's + * transformation matrix. + * @param[in] pool The memory pool to allocate from. + * @return A perspective camera. + */ +dmnsn_camera *dmnsn_new_perspective_camera(dmnsn_pool *pool); diff --git a/libdimension/dimension/model/csg.h b/libdimension/dimension/model/csg.h new file mode 100644 index 0000000..b2ce83f --- /dev/null +++ b/libdimension/dimension/model/csg.h @@ -0,0 +1,59 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Constructive solid geometry + */ + +/** + * CSG union. + * @param[in] pool The memory pool to allocate from. + * @param[in] objects The objects from which to compose the union. + * @return A union of the objects in \p objects. + */ +dmnsn_object *dmnsn_new_csg_union(dmnsn_pool *pool, dmnsn_array *objects); + +/** + * CSG intersection. + * @param[in] pool The memory pool to allocate from. + * @param[in,out] A The first object. + * @param[in,out] B The second object. + * @return The intersection of \p A and \p B. + */ +dmnsn_object *dmnsn_new_csg_intersection(dmnsn_pool *pool, dmnsn_object *A, dmnsn_object *B); + +/** + * CSG intersection. + * @param[in] pool The memory pool to allocate from. + * @param[in,out] A The outer object. + * @param[in,out] B The inner object. + * @return The difference between \p A and \p B. + */ +dmnsn_object *dmnsn_new_csg_difference(dmnsn_pool *pool, dmnsn_object *A, dmnsn_object *B); + +/** + * CSG Merge. + * @param[in] pool The memory pool to allocate from. + * @param[in,out] A The first object. + * @param[in,out] B The second object. + * @return The merge of \p A and \p B. + */ +dmnsn_object *dmnsn_new_csg_merge(dmnsn_pool *pool, dmnsn_object *A, dmnsn_object *B); diff --git a/libdimension/dimension/model/finish.h b/libdimension/dimension/model/finish.h new file mode 100644 index 0000000..d975877 --- /dev/null +++ b/libdimension/dimension/model/finish.h @@ -0,0 +1,141 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Object finishes. + */ + +/* Ambient component */ + +/** Ambient finish component. */ +typedef struct dmnsn_ambient { + dmnsn_color ambient; /**< Ambient light. */ +} dmnsn_ambient; + +/** Allocate an ambient component. */ +dmnsn_ambient *dmnsn_new_ambient(dmnsn_pool *pool, dmnsn_color ambient); + +/* Diffuse component */ + +typedef struct dmnsn_diffuse dmnsn_diffuse; + +/** + * Diffuse reflection callback. + * @param[in] diffuse The diffuse object itself. + * @param[in] light The color of the light illuminating the object. + * @param[in] color The pigment of the object. + * @param[in] ray The direction of the light source. + * @param[in] normal The normal vector of the surface. + * @return The diffuse reflection component of the object's color. + */ +typedef dmnsn_color dmnsn_diffuse_fn(const dmnsn_diffuse *diffuse, + dmnsn_color light, dmnsn_color color, + dmnsn_vector ray, dmnsn_vector normal); + +/** Diffuse finish component. */ +struct dmnsn_diffuse { + dmnsn_diffuse_fn *diffuse_fn; /**< Diffuse callback. */ +}; + +/** Allocate a dummy diffuse component. */ +dmnsn_diffuse *dmnsn_new_diffuse(dmnsn_pool *pool); +/** Initialize a dmnsn_diffuse field. */ +void dmnsn_init_diffuse(dmnsn_diffuse *diffuse); + +/* Specular component */ + +typedef struct dmnsn_specular dmnsn_specular; + +/** + * Specular highlight callback. + * @param[in] specular The specular object itself. + * @param[in] light The color of the light illuminating the object. + * @param[in] color The pigment of the object. + * @param[in] ray The direction of the light source. + * @param[in] normal The normal vector of the surface. + * @param[in] viewer The direction of the viewer. + * @return The specular reflection component of the object's color. + */ +typedef dmnsn_color dmnsn_specular_fn(const dmnsn_specular *specular, + dmnsn_color light, dmnsn_color color, + dmnsn_vector ray, dmnsn_vector normal, + dmnsn_vector viewer); + +/** Specular finish component. */ +struct dmnsn_specular { + dmnsn_specular_fn *specular_fn; /**< Specular callback. */ +}; + +/** Allocate a dummy specular component. */ +dmnsn_specular *dmnsn_new_specular(dmnsn_pool *pool); +/** Initialize a dmnsn_specular field. */ +void dmnsn_init_specular(dmnsn_specular *specular); + +/* Reflection component */ + +typedef struct dmnsn_reflection dmnsn_reflection; + +/** + * Reflected light callback. + * @param[in] reflection The reflection object itself. + * @param[in] reflect The color of the reflected ray. + * @param[in] color The pigment of the object. + * @param[in] ray The direction of the reflected ray. + * @param[in] normal The normal vector of the surface. + * @return The contribution of the reflected ray to the object's color. + */ +typedef dmnsn_color dmnsn_reflection_fn(const dmnsn_reflection *reflection, + dmnsn_color reflect, dmnsn_color color, + dmnsn_vector ray, dmnsn_vector normal); + +/** The reflection component. */ +struct dmnsn_reflection { + dmnsn_reflection_fn *reflection_fn; /**< Reflection callback. */ +}; + +/** Allocate a dummy reflection component. */ +dmnsn_reflection *dmnsn_new_reflection(dmnsn_pool *pool); +/** Initialize a dmnsn_reflection field. */ +void dmnsn_init_reflection(dmnsn_reflection *reflection); + +/* Entire finishes */ + +/** A finish. */ +typedef struct dmnsn_finish { + dmnsn_ambient *ambient; /**< Ambient component. */ + dmnsn_diffuse *diffuse; /**< Diffuse component. */ + dmnsn_specular *specular; /**< Specular component. */ + dmnsn_reflection *reflection; /**< Reflection component. */ +} dmnsn_finish; + +/** + * Create a new blank finish. + * @return The new finish. + */ +dmnsn_finish dmnsn_new_finish(void); + +/** + * Fill missing finish properties from a default finish. + * @param[in] default_finish The default finish. + * @param[in,out] finish The finish to fill. + */ +void dmnsn_finish_cascade(const dmnsn_finish *default_finish, + dmnsn_finish *finish); diff --git a/libdimension/dimension/model/finishes.h b/libdimension/dimension/model/finishes.h new file mode 100644 index 0000000..e1f7b44 --- /dev/null +++ b/libdimension/dimension/model/finishes.h @@ -0,0 +1,51 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Pre-defined finishes. + */ + +/** + * Regular diffuse finish. + * @param[in] pool The memory pool to allocate from. + * @param[in] diffuse The diffuse reflection coefficient. + * @return A diffuse finish component. + */ +dmnsn_diffuse *dmnsn_new_lambertian(dmnsn_pool *pool, double diffuse); + +/** + * A phong specular highlight. + * @param[in] pool The memory pool to allocate from. + * @param[in] specular The specular reflection coefficient. + * @param[in] exp The exponent (roughly the highlight size). + * @return A phong specular finish component. + */ +dmnsn_specular *dmnsn_new_phong(dmnsn_pool *pool, double specular, double exp); + +/** + * Specular (mirror) reflection. + * @param[in] pool The memory pool to allocate from. + * @param[in] min Reflection at paralell angles. + * @param[in] max Reflection at perpendicular angles (often == \p min). + * @param[in] falloff Degree of exponential falloff (usually 1). + * @return A reflective finish component. + */ +dmnsn_reflection *dmnsn_new_basic_reflection(dmnsn_pool *pool, dmnsn_color min, dmnsn_color max, double falloff); diff --git a/libdimension/dimension/model/interior.h b/libdimension/dimension/model/interior.h new file mode 100644 index 0000000..0ff697d --- /dev/null +++ b/libdimension/dimension/model/interior.h @@ -0,0 +1,44 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Object interiors. + */ + +/** An interior. */ +typedef struct dmnsn_interior { + double ior; /**< Refractive index. */ +} dmnsn_interior; + +/** + * Create an interior object. + * @param[in] pool The memory pool to allocate from. + * @return The new interior. + */ +dmnsn_interior *dmnsn_new_interior(dmnsn_pool *pool); + +/** + * Fill missing interior properties from a default interior. + * @param[in] default_interior The default interior. + * @param[in,out] interiorp A pointer to the interior to fill. + */ +void dmnsn_interior_cascade(dmnsn_interior *default_interior, + dmnsn_interior **interiorp); diff --git a/libdimension/dimension/model/light.h b/libdimension/dimension/model/light.h new file mode 100644 index 0000000..218611d --- /dev/null +++ b/libdimension/dimension/model/light.h @@ -0,0 +1,76 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Lights. + */ + +#include + +/* Forward-declar dmnsn_light */ +typedef struct dmnsn_light dmnsn_light; + +/** + * Light direction callback. + * @param[in] light The light itself. + * @param[in] v The point to illuminate. + * @return The direction of light rays pointing from \p v + */ +typedef dmnsn_vector dmnsn_light_direction_fn(const dmnsn_light *light, + dmnsn_vector v); + +/** + * Light illumination callback. + * @param[in] light The light itself. + * @param[in] v The point to illuminate. + * @return The color of the light at \p v. + */ +typedef dmnsn_color dmnsn_light_illumination_fn(const dmnsn_light *light, + dmnsn_vector v); + +/** + * Light shadow callback. + * @param[in] light The light itself. + * @param[in] t The line index of the closest shadow ray intersection. + * @return Whether the point is in shadow. + */ +typedef bool dmnsn_light_shadow_fn(const dmnsn_light *light, double t); + +/** A light. */ +struct dmnsn_light { + /* Callbacks */ + dmnsn_light_direction_fn *direction_fn; /**< Direction callback. */ + dmnsn_light_illumination_fn *illumination_fn; /**< Illumination callback. */ + dmnsn_light_shadow_fn *shadow_fn; /**< Shadow callback. */ +}; + +/** + * Create a dummy light. + * @param[in] pool The memory pool to allocate from. + * @return The allocated light. + */ +dmnsn_light *dmnsn_new_light(dmnsn_pool *pool); + +/** + * Initialize a dmnsn_light field. + * @param[out] light The light to initialize. + */ +void dmnsn_init_light(dmnsn_light *light); diff --git a/libdimension/dimension/model/lights.h b/libdimension/dimension/model/lights.h new file mode 100644 index 0000000..e7de4cc --- /dev/null +++ b/libdimension/dimension/model/lights.h @@ -0,0 +1,33 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Pre-defined light types. + */ + +/** + * A point light. + * @param[in] pool The memory pool to allocate from. + * @param[in] x0 The origin of the light. + * @param[in] color The color of the light. + * @return A point light. + */ +dmnsn_light *dmnsn_new_point_light(dmnsn_pool *pool, dmnsn_vector x0, dmnsn_color color); diff --git a/libdimension/dimension/model/object.h b/libdimension/dimension/model/object.h new file mode 100644 index 0000000..287d838 --- /dev/null +++ b/libdimension/dimension/model/object.h @@ -0,0 +1,165 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Objects. + */ + +#include + +/* Forward-declare dmnsn_object */ +typedef struct dmnsn_object dmnsn_object; + +/** A type to represent a ray-object intersection. */ +typedef struct dmnsn_intersection { + dmnsn_ray ray; /**< The ray that intersected. */ + double t; /**< The ray index that intersected. */ + + /** The surface normal at the intersection point. */ + dmnsn_vector normal; + + /** The object of intersection. */ + const dmnsn_object *object; +} dmnsn_intersection; + +/** + * Ray-object intersection callback. + * @param[in] object The object to test. + * @param[in] ray The ray to test. + * @param[out] intersection Where to store the intersection details of the + * closest (if any) intersection. + * @return Whether \p ray intersected \p object. + */ +typedef bool dmnsn_object_intersection_fn(const dmnsn_object *object, dmnsn_ray ray, dmnsn_intersection *intersection); + +/** + * Object inside callback. + * @param[in] object The object to test. + * @param[in] point The point to test. + * @return Whether \p point is inside \p object. + */ +typedef bool dmnsn_object_inside_fn(const dmnsn_object *object, dmnsn_vector point); + +/** + * Object bounding callback. + * @param[in,out] object The object to bound. + * @param[in,out] trans The effective transformation for the object. + * @return A world-coordinate bounding box computed for the object. + */ +typedef dmnsn_aabb dmnsn_object_bounding_fn(const dmnsn_object *object, dmnsn_matrix trans); + +/** + * Object precomputation callback. + * @param[in,out] object The object to precompute. + */ +typedef void dmnsn_object_precompute_fn(dmnsn_object *object); + +/** Object callbacks. */ +typedef struct dmnsn_object_vtable { + dmnsn_object_intersection_fn *intersection_fn; /**< Intersection callback. */ + dmnsn_object_inside_fn *inside_fn; /**< Inside callback. */ + dmnsn_object_bounding_fn *bounding_fn; /**< Bounding callback. */ + dmnsn_object_precompute_fn *precompute_fn; /**< Precomputation callback. */ +} dmnsn_object_vtable; + +/** An object. */ +struct dmnsn_object { + const dmnsn_object_vtable *vtable; /**< Callbacks. */ + + dmnsn_texture *texture; /**< Surface properties. */ + dmnsn_interior *interior; /**< Interior properties. */ + + dmnsn_matrix trans; /**< Transformation matrix. */ + dmnsn_matrix intrinsic_trans; /**< Transformations intrinsic to the object. */ + + dmnsn_array *children; /**< Child objects. */ + bool split_children; /**< Whether the child objects can be split. */ + + /* Precomputed values */ + bool precomputed; /**< @internal Whether the object is precomputed yet. */ + dmnsn_matrix trans_inv; /**< Inverse of the transformation matrix. */ + dmnsn_matrix pigment_trans; /**< Inverse transformation for the texture. */ + dmnsn_aabb aabb; /**< Bounding box in world coordinates. */ +}; + +/** + * Allocate a dummy object. + * @param[in] pool The memory pool to allocate from. + * @return The allocated object. + */ +dmnsn_object *dmnsn_new_object(dmnsn_pool *pool); + +/** + * Initialize a dmnsn_object field. + * @param[out] object The object to initialize. + */ +void dmnsn_init_object(dmnsn_object *object); + +/** + * Precompute values for an object and its children. + * @param[in,out] object The object to precompute. + */ +void dmnsn_object_precompute(dmnsn_object *object); + +/** + * Appropriately transform a ray, then test for an intersection. + * @param[in] object The object to test. + * @param[in] ray The ray to test. + * @param[out] intersection Where to store the intersection details. + * @return Whether there was an intersection. + */ +DMNSN_INLINE bool +dmnsn_object_intersection(const dmnsn_object *object, dmnsn_ray ray, + dmnsn_intersection *intersection) +{ + dmnsn_ray ray_trans = dmnsn_transform_ray(object->trans_inv, ray); + intersection->object = NULL; + if (object->vtable->intersection_fn(object, ray_trans, intersection)) { + /* Get us back into world coordinates */ + intersection->ray = ray; + intersection->normal = dmnsn_vector_normalized( + dmnsn_transform_normal(object->trans_inv, intersection->normal) + ); + if (!intersection->object) { + intersection->object = object; + } + + dmnsn_assert(!dmnsn_isnan(intersection->t), "Intersection point is NaN."); + dmnsn_assert(!dmnsn_vector_isnan(intersection->normal), "Intersection normal is NaN."); + + return true; + } else { + return false; + } +} + +/** + * Appropriately transform a point, then test for containment. + * @param[in] object The object to test. + * @param[in] point The point to test. + * @return Whether \p point was inside \p object. + */ +DMNSN_INLINE bool +dmnsn_object_inside(const dmnsn_object *object, dmnsn_vector point) +{ + point = dmnsn_transform_point(object->trans_inv, point); + return object->vtable->inside_fn(object, point); +} diff --git a/libdimension/dimension/model/objects.h b/libdimension/dimension/model/objects.h new file mode 100644 index 0000000..2865d82 --- /dev/null +++ b/libdimension/dimension/model/objects.h @@ -0,0 +1,103 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Pre-defined objects. + */ + +#include + +/** + * A flat triangle. + * @param[in] pool The memory pool to allocate from. + * @param[in] vertices The corners of the triangle. + * @return A triangle. + */ +dmnsn_object *dmnsn_new_triangle(dmnsn_pool *pool, dmnsn_vector vertices[3]); + +/** + * A triangle, with normals interpolated between the points. + * @param[in] pool The memory pool to allocate from. + * @param[in] vertices The corners of the triangle. + * @param[in] normals The normals at each corner. + * @return A smooth triangle. + */ +dmnsn_object *dmnsn_new_smooth_triangle(dmnsn_pool *pool, dmnsn_vector vertices[3], dmnsn_vector normals[3]); + +/** + * A triangle fan. + * @param[in] pool The memory pool to allocate from. + * @param[in] vertices The vertices of the fan, starting in the center. + * @param[in] nvertices The number of vertices. + * @return A triangle fan. + */ +dmnsn_object *dmnsn_new_triangle_fan(dmnsn_pool *pool, dmnsn_vector vertices[], size_t nvertices); + +/** + * A smooth triangle fan. + * @param[in] pool The memory pool to allocate from. + * @param[in] vertices The vertices of the fan, starting in the center. + * @param[in] normals The normal vector for each vertex. + * @param[in] nvertices The number of vertices. + * @return A triangle fan. + */ +dmnsn_object *dmnsn_new_smooth_triangle_fan(dmnsn_pool *pool, dmnsn_vector vertices[], dmnsn_vector normals[], size_t nvertices); + +/** + * A plane. + * @param[in] pool The memory pool to allocate from. + * @param[in] normal The normal vector of the plane. + * @return A plane through the origin, with the given normal. + */ +dmnsn_object *dmnsn_new_plane(dmnsn_pool *pool, dmnsn_vector normal); + +/** + * A sphere. + * @param[in] pool The memory pool to allocate from. + * @return A sphere of radius 1, centered at the origin. + */ +dmnsn_object *dmnsn_new_sphere(dmnsn_pool *pool); + +/** + * A cube. + * @param[in] pool The memory pool to allocate from. + * @return An axis-aligned cube, from (-1, -1, -1) to (1, 1, 1). + */ +dmnsn_object *dmnsn_new_cube(dmnsn_pool *pool); + +/** + * A cylinder/cone. + * @param[in] pool The memory pool to allocate from. + * @param[in] r1 The bottom radius. + * @param[in] r2 The top radius. + * @param[in] open Whether to render caps. + * @return A cone slice, from r = \p r1 at y = -1, to r = \p r2 at y = 1 + */ +dmnsn_object *dmnsn_new_cone(dmnsn_pool *pool, double r1, double r2, bool open); + +/** + * A torus. + * @param[in] pool The memory pool to allocate from. + * @param[in] major The major radius. + * @param[in] minor The minor radius. + * @return A torus, centered at the origin and lying in the x-z plane. + */ +dmnsn_object *dmnsn_new_torus(dmnsn_pool *pool, double major, double minor); diff --git a/libdimension/dimension/model/pigment.h b/libdimension/dimension/model/pigment.h new file mode 100644 index 0000000..14d8bae --- /dev/null +++ b/libdimension/dimension/model/pigment.h @@ -0,0 +1,86 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Object pigments. + */ + +/* Forward-declare dmnsn_pigment */ +typedef struct dmnsn_pigment dmnsn_pigment; + +/** + * Pigment callback. + * @param[in] pigment The pigment itself. + * @param[in] v The point to color. + * @return The color of the pigment at \p v. + */ +typedef dmnsn_tcolor dmnsn_pigment_fn(const dmnsn_pigment *pigment, + dmnsn_vector v); + +/** + * Pigment initializer callback. + * @param[in,out] pigment The pigment to initialize. + */ +typedef void dmnsn_pigment_initialize_fn(dmnsn_pigment *pigment); + +/** A pigment. */ +struct dmnsn_pigment { + dmnsn_pigment_fn *pigment_fn; /**< The pigment callback. */ + dmnsn_pigment_initialize_fn *initialize_fn; /**< The initializer callback. */ + + dmnsn_matrix trans; /**< Transformation matrix. */ + dmnsn_matrix trans_inv; /**< The inverse of the transformation matrix. */ + + /** Quick color -- used for low-quality renders. */ + dmnsn_tcolor quick_color; + + bool initialized; /** @internal Whether the pigment is initialized. */ +}; + +/** + * Allocate a new dummy pigment. + * @param[in] pool The memory pool to allocate from. + * @return The allocated pigment. + */ +dmnsn_pigment *dmnsn_new_pigment(dmnsn_pool *pool); + +/** + * Initialize a dmnsn_pigment field. + * @param[out] pigment The pigment to initialize. + */ +void dmnsn_init_pigment(dmnsn_pigment *pigment); + +/** + * Initialize a pigment. Pigments should not be used before being initialized, + * but should not be modified after being initialized. Pigments are generally + * initialized for you. + * @param[in,out] pigment The pigment to initialize. + */ +void dmnsn_pigment_initialize(dmnsn_pigment *pigment); + +/** + * Evaluate the color of a pigment at a point. + * @param[in] pigment The pigment to evaluate. + * @param[in] v The point to color. + * @return The color at \p v. + */ +dmnsn_tcolor dmnsn_pigment_evaluate(const dmnsn_pigment *pigment, + dmnsn_vector v); diff --git a/libdimension/dimension/model/pigments.h b/libdimension/dimension/model/pigments.h new file mode 100644 index 0000000..100016d --- /dev/null +++ b/libdimension/dimension/model/pigments.h @@ -0,0 +1,66 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Pre-defined pigments. + */ + +/** + * A solid color. + * @param[in] pool The memory pool to allocate from. + * @param[in] color The color of the pigment. + * @return A pigment with the color \p color everywhere. + */ +dmnsn_pigment *dmnsn_new_solid_pigment(dmnsn_pool *pool, dmnsn_tcolor color); + +/** + * An image map. The image (regardless of its real dimensions) is projected + * on the x-y plane in tesselating unit squares. + * @param[in] pool The memory pool to allocate from. + * @param[in] canvas The canvas holding the image. + * @return An image-mapped pigment. + */ +dmnsn_pigment *dmnsn_new_canvas_pigment(dmnsn_pool *pool, dmnsn_canvas *canvas); + +/** + * Pigment map flags. + */ +typedef enum dmnsn_pigment_map_flags { + DMNSN_PIGMENT_MAP_REGULAR, /**< Calculate linear color gradients. */ + DMNSN_PIGMENT_MAP_SRGB /**< Calculate sRGB color gradients. */ +} dmnsn_pigment_map_flags; + +/** + * Construct a pigment map. + * @param[in] pool The memory pool to allocate from. + * @return An empty pigment map. + */ +dmnsn_map *dmnsn_new_pigment_map(dmnsn_pool *pool); + +/** + * A pigment-mapped pigment. + * @param[in] pool The memory pool to allocate from. + * @param[in,out] pattern The pattern of the pigment. + * @param[in,out] map The pigment map to apply to the pattern. + * @param[in] flags Gradient flags + * @return A pigment mapping the pattern to other pigments. + */ +dmnsn_pigment *dmnsn_new_pigment_map_pigment(dmnsn_pool *pool, dmnsn_pattern *pattern, dmnsn_map *map, dmnsn_pigment_map_flags flags); diff --git a/libdimension/dimension/model/scene.h b/libdimension/dimension/model/scene.h new file mode 100644 index 0000000..dd0c1ba --- /dev/null +++ b/libdimension/dimension/model/scene.h @@ -0,0 +1,95 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Entire scenes. + */ + +/** Render quality flags. */ +enum { + DMNSN_RENDER_NONE = 0, /**< Render nothing. */ + DMNSN_RENDER_PIGMENT = 1 << 0, /**< Render pigments. */ + DMNSN_RENDER_LIGHTS = 1 << 1, /**< Render lights and shadows. */ + DMNSN_RENDER_FINISH = 1 << 2, /**< Render object finishes. */ + DMNSN_RENDER_TRANSPARENCY = 1 << 3, /**< Render transparency/refraction. */ + DMNSN_RENDER_REFLECTION = 1 << 4, /**< Render specular reflection. */ + DMNSN_RENDER_FULL = ~DMNSN_RENDER_NONE /**< Render everything. */ +}; + +/** Render quality. */ +typedef unsigned int dmnsn_quality; + +/** An entire scene. */ +typedef struct dmnsn_scene { + /* World attributes */ + dmnsn_pigment *background; /**< Background pigment. */ + dmnsn_texture *default_texture; /**< Default object texture. */ + dmnsn_interior *default_interior; /**< Default object interior. */ + + /** Canvas. */ + dmnsn_canvas *canvas; + + /* Support for rendering image subregions. */ + size_t region_x; /**< The x position of the canvas in the broader image. */ + size_t region_y; /**< The y position of the canvas in the broader image. */ + size_t outer_width; /**< Width of the broader image. */ + size_t outer_height; /**< Height of the broader image. */ + + /** Objects. */ + dmnsn_array *objects; + + /** Lights. */ + dmnsn_array *lights; + + /** Camera. */ + dmnsn_camera *camera; + + /** Render quality. */ + dmnsn_quality quality; + + /** Recursion limit. */ + unsigned int reclimit; + + /** Adaptive depth control bailout. */ + double adc_bailout; + + /** Number of parallel threads. */ + unsigned int nthreads; + + /** Timers. */ + dmnsn_timer bounding_timer; + dmnsn_timer render_timer; + + bool initialized; /**< @internal Whether the scene is initialized. */ +} dmnsn_scene; + +/** + * Create a scene. + * @param[in] pool The memory pool to allocate from. + * @return A new empty scene. + */ +dmnsn_scene *dmnsn_new_scene(dmnsn_pool *pool); + +/** + * Initialize a scene. + * @param[in,out] scene The scene to initalize. + */ +void dmnsn_scene_initialize(dmnsn_scene *scene); diff --git a/libdimension/dimension/model/texture.h b/libdimension/dimension/model/texture.h new file mode 100644 index 0000000..df08a4a --- /dev/null +++ b/libdimension/dimension/model/texture.h @@ -0,0 +1,57 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Object textures. + */ + +/** A complete texture. */ +typedef struct { + dmnsn_pigment *pigment; /**< Pigment. */ + dmnsn_finish finish; /**< Finish. */ + + dmnsn_matrix trans; /**< Transformation matrix. */ + dmnsn_matrix trans_inv; /**< The inverse of the transformation matrix. */ + + bool initialized; /**< @internal Whether the texture is initialized yet. */ +} dmnsn_texture; + +/** + * Create a blank texture. + * @param[in] pool The memory pool to allocate from. + * @return The new texture. + */ +dmnsn_texture *dmnsn_new_texture(dmnsn_pool *pool); + +/** + * Initialize a texture. Textures should not be used before being initialized, + * but should not be modified after being initialized. Textures are generally + * initialized for you. + * @param[in,out] texture The texture to initialize. + */ +void dmnsn_texture_initialize(dmnsn_texture *texture); + +/** + * Fill missing texture properties from a default texture. + * @param[in] default_texture The default texture. + * @param[in,out] texturep A pointer to the texture to fill. + */ +void dmnsn_texture_cascade(dmnsn_texture *default_texture, dmnsn_texture **texturep); diff --git a/libdimension/dimension/object.h b/libdimension/dimension/object.h deleted file mode 100644 index c8e9d86..0000000 --- a/libdimension/dimension/object.h +++ /dev/null @@ -1,164 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Objects. - */ - -#include - -/* Forward-declare dmnsn_object */ -typedef struct dmnsn_object dmnsn_object; - -/** A type to represent a ray-object intersection. */ -typedef struct dmnsn_intersection { - dmnsn_line ray; /**< The ray that intersected. */ - double t; /**< The line index that intersected. */ - - /** The surface normal at the intersection point. */ - dmnsn_vector normal; - - /** The object of intersection. */ - const dmnsn_object *object; -} dmnsn_intersection; - -/** - * Ray-object intersection callback. - * @param[in] object The object to test. - * @param[in] line The line to test. - * @param[out] intersection Where to store the intersection details of the - * closest (if any) intersection. - * @return Whether \p line intersected \p object. - */ -typedef bool dmnsn_object_intersection_fn(const dmnsn_object *object, dmnsn_line line, dmnsn_intersection *intersection); - -/** - * Object inside callback. - * @param[in] object The object to test. - * @param[in] point The point to test. - * @return Whether \p point is inside \p object. - */ -typedef bool dmnsn_object_inside_fn(const dmnsn_object *object, dmnsn_vector point); - -/** - * Object bounding callback. - * @param[in,out] object The object to bound. - * @param[in,out] trans The effective transformation for the object. - */ -typedef dmnsn_bounding_box dmnsn_object_bounding_fn(const dmnsn_object *object, dmnsn_matrix trans); - -/** - * Object precomputation callback. - * @param[in,out] object The object to precompute. - */ -typedef void dmnsn_object_precompute_fn(dmnsn_object *object); - -/** Object callbacks. */ -typedef struct dmnsn_object_vtable { - dmnsn_object_intersection_fn *intersection_fn; /**< Intersection callback. */ - dmnsn_object_inside_fn *inside_fn; /**< Inside callback. */ - dmnsn_object_bounding_fn *bounding_fn; /**< Bounding callback. */ - dmnsn_object_precompute_fn *precompute_fn; /**< Precomputation callback. */ -} dmnsn_object_vtable; - -/** An object. */ -struct dmnsn_object { - const dmnsn_object_vtable *vtable; /**< Callbacks. */ - - dmnsn_texture *texture; /**< Surface properties. */ - dmnsn_interior *interior; /**< Interior properties. */ - - dmnsn_matrix trans; /**< Transformation matrix. */ - dmnsn_matrix intrinsic_trans; /**< Transformations intrinsic to the object. */ - - dmnsn_array *children; /**< Child objects. */ - bool split_children; /**< Whether the child objects can be split. */ - - /* Precomputed values */ - bool precomputed; /**< @internal Whether the object is precomputed yet. */ - dmnsn_matrix trans_inv; /**< Inverse of the transformation matrix. */ - dmnsn_matrix pigment_trans; /**< Inverse transformation for the texture. */ - dmnsn_bounding_box bounding_box; /**< Bounding box in world coordinates. */ -}; - -/** - * Allocate a dummy object. - * @param[in] pool The memory pool to allocate from. - * @return The allocated object. - */ -dmnsn_object *dmnsn_new_object(dmnsn_pool *pool); - -/** - * Initialize a dmnsn_object field. - * @param[out] object The object to initialize. - */ -void dmnsn_init_object(dmnsn_object *object); - -/** - * Precompute values for an object and its children. - * @param[in,out] object The object to precompute. - */ -void dmnsn_object_precompute(dmnsn_object *object); - -/** - * Appropriately transform a ray, then test for an intersection. - * @param[in] object The object to test. - * @param[in] line The ray to test. - * @param[out] intersection Where to store the intersection details. - * @return Whether there was an intersection. - */ -DMNSN_INLINE bool -dmnsn_object_intersection(const dmnsn_object *object, dmnsn_line line, - dmnsn_intersection *intersection) -{ - dmnsn_line line_trans = dmnsn_transform_line(object->trans_inv, line); - intersection->object = NULL; - if (object->vtable->intersection_fn(object, line_trans, intersection)) { - /* Get us back into world coordinates */ - intersection->ray = line; - intersection->normal = dmnsn_vector_normalized( - dmnsn_transform_normal(object->trans_inv, intersection->normal) - ); - if (!intersection->object) { - intersection->object = object; - } - - dmnsn_assert(!dmnsn_isnan(intersection->t), "Intersection point is NaN."); - dmnsn_assert(!dmnsn_vector_isnan(intersection->normal), "Intersection normal is NaN."); - - return true; - } else { - return false; - } -} - -/** - * Appropriately transform a point, then test for containment. - * @param[in] object The object to test. - * @param[in] point The point to test. - * @return Whether \p point was inside \p object. - */ -DMNSN_INLINE bool -dmnsn_object_inside(const dmnsn_object *object, dmnsn_vector point) -{ - point = dmnsn_transform_point(object->trans_inv, point); - return object->vtable->inside_fn(object, point); -} diff --git a/libdimension/dimension/objects.h b/libdimension/dimension/objects.h deleted file mode 100644 index 2865d82..0000000 --- a/libdimension/dimension/objects.h +++ /dev/null @@ -1,103 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Pre-defined objects. - */ - -#include - -/** - * A flat triangle. - * @param[in] pool The memory pool to allocate from. - * @param[in] vertices The corners of the triangle. - * @return A triangle. - */ -dmnsn_object *dmnsn_new_triangle(dmnsn_pool *pool, dmnsn_vector vertices[3]); - -/** - * A triangle, with normals interpolated between the points. - * @param[in] pool The memory pool to allocate from. - * @param[in] vertices The corners of the triangle. - * @param[in] normals The normals at each corner. - * @return A smooth triangle. - */ -dmnsn_object *dmnsn_new_smooth_triangle(dmnsn_pool *pool, dmnsn_vector vertices[3], dmnsn_vector normals[3]); - -/** - * A triangle fan. - * @param[in] pool The memory pool to allocate from. - * @param[in] vertices The vertices of the fan, starting in the center. - * @param[in] nvertices The number of vertices. - * @return A triangle fan. - */ -dmnsn_object *dmnsn_new_triangle_fan(dmnsn_pool *pool, dmnsn_vector vertices[], size_t nvertices); - -/** - * A smooth triangle fan. - * @param[in] pool The memory pool to allocate from. - * @param[in] vertices The vertices of the fan, starting in the center. - * @param[in] normals The normal vector for each vertex. - * @param[in] nvertices The number of vertices. - * @return A triangle fan. - */ -dmnsn_object *dmnsn_new_smooth_triangle_fan(dmnsn_pool *pool, dmnsn_vector vertices[], dmnsn_vector normals[], size_t nvertices); - -/** - * A plane. - * @param[in] pool The memory pool to allocate from. - * @param[in] normal The normal vector of the plane. - * @return A plane through the origin, with the given normal. - */ -dmnsn_object *dmnsn_new_plane(dmnsn_pool *pool, dmnsn_vector normal); - -/** - * A sphere. - * @param[in] pool The memory pool to allocate from. - * @return A sphere of radius 1, centered at the origin. - */ -dmnsn_object *dmnsn_new_sphere(dmnsn_pool *pool); - -/** - * A cube. - * @param[in] pool The memory pool to allocate from. - * @return An axis-aligned cube, from (-1, -1, -1) to (1, 1, 1). - */ -dmnsn_object *dmnsn_new_cube(dmnsn_pool *pool); - -/** - * A cylinder/cone. - * @param[in] pool The memory pool to allocate from. - * @param[in] r1 The bottom radius. - * @param[in] r2 The top radius. - * @param[in] open Whether to render caps. - * @return A cone slice, from r = \p r1 at y = -1, to r = \p r2 at y = 1 - */ -dmnsn_object *dmnsn_new_cone(dmnsn_pool *pool, double r1, double r2, bool open); - -/** - * A torus. - * @param[in] pool The memory pool to allocate from. - * @param[in] major The major radius. - * @param[in] minor The minor radius. - * @return A torus, centered at the origin and lying in the x-z plane. - */ -dmnsn_object *dmnsn_new_torus(dmnsn_pool *pool, double major, double minor); diff --git a/libdimension/dimension/pattern.h b/libdimension/dimension/pattern.h index 2caed07..17a43a9 100644 --- a/libdimension/dimension/pattern.h +++ b/libdimension/dimension/pattern.h @@ -1,5 +1,5 @@ /************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * + * Copyright (C) 2014 Tavian Barnes * * * * This file is part of The Dimension Library. * * * @@ -20,43 +20,25 @@ /** * @file - * Patterns. Patterns are functions which map vectors to scalars, which are - * used for pigments and normals. + * Patterns. */ -/* Forward-declare dmnsn_pattern */ -typedef struct dmnsn_pattern dmnsn_pattern; +#ifndef DMNSN_PATTERN_H +#define DMNSN_PATTERN_H -/** - * Pattern callback. - * @param[in] pattern The pattern itself. - * @param[in] v The point at which to evaluate the pattern. - * @return The value of the pattern at \p v. - */ -typedef double dmnsn_pattern_fn(const dmnsn_pattern *pattern, dmnsn_vector v); +#ifdef __cplusplus +extern "C" { +#endif -/** A pattern. */ -struct dmnsn_pattern { - dmnsn_pattern_fn *pattern_fn; /**< The pattern callback. */ -}; +#include +#include -/** - * Allocate a dummy pattern. - * @param[in] pool The memory pool to allocate from. - * @return A pattern with no callbacks set. - */ -dmnsn_pattern *dmnsn_new_pattern(dmnsn_pool *pool); +#include +#include +#include -/** - * Initialize a dmnsn_pattern field. - * @param[out] pattern The pattern to initialize. - */ -void dmnsn_init_pattern(dmnsn_pattern *pattern); +#ifdef __cplusplus +} +#endif -/** - * Invoke the pattern callback. - * @param[in] pattern The pattern to evaluate. - * @param[in] v The point to get the pattern value for. - * @return The value of the pattern at \p v. - */ -double dmnsn_pattern_value(const dmnsn_pattern *pattern, dmnsn_vector v); +#endif /* DMNSN_PATTERN_H */ diff --git a/libdimension/dimension/pattern/map.h b/libdimension/dimension/pattern/map.h new file mode 100644 index 0000000..7229a24 --- /dev/null +++ b/libdimension/dimension/pattern/map.h @@ -0,0 +1,68 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Generic maps (backend for pigment_maps, etc.). + */ + +/** A map. */ +typedef struct dmnsn_map dmnsn_map; + +/** + * Create an empty map. + * @param[in] pool The memory pool to allocate from. + * @param[in] size The size of the objects to store in the map. + * @return A map with no entries. + */ +dmnsn_map *dmnsn_new_map(dmnsn_pool *pool, size_t size); + +/** + * Add an entry (a scalar-object pair) to a map. + * @param[in,out] map The map to add to. + * @param[in] n The index of the entry. + * @param[in] obj The value of the entry. + */ +void dmnsn_map_add_entry(dmnsn_map *map, double n, const void *obj); + +/** + * Return the number of entries in a map. + * @param[in] map The map to measure. + * @return The size of \p map. + */ +size_t dmnsn_map_size(const dmnsn_map *map); + +/** + * Evaluate a map. + * @param[in] map The map to evaluate. + * @param[in] n The index to evaluate. + * @param[out] val The normalized distance of \p n from \p obj1. + * @param[out] obj1 The first object. + * @param[out] obj2 The second object. + */ +void dmnsn_map_evaluate(const dmnsn_map *map, double n, + double *val, void *obj1, void *obj2); + +/** + * Apply a callback to each element of a map. + * @param[in,out] map The map. + * @param[in] callback The callback to apply to the elements. + */ +void dmnsn_map_apply(dmnsn_map *map, dmnsn_callback_fn *callback); diff --git a/libdimension/dimension/pattern/pattern.h b/libdimension/dimension/pattern/pattern.h new file mode 100644 index 0000000..2caed07 --- /dev/null +++ b/libdimension/dimension/pattern/pattern.h @@ -0,0 +1,62 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Patterns. Patterns are functions which map vectors to scalars, which are + * used for pigments and normals. + */ + +/* Forward-declare dmnsn_pattern */ +typedef struct dmnsn_pattern dmnsn_pattern; + +/** + * Pattern callback. + * @param[in] pattern The pattern itself. + * @param[in] v The point at which to evaluate the pattern. + * @return The value of the pattern at \p v. + */ +typedef double dmnsn_pattern_fn(const dmnsn_pattern *pattern, dmnsn_vector v); + +/** A pattern. */ +struct dmnsn_pattern { + dmnsn_pattern_fn *pattern_fn; /**< The pattern callback. */ +}; + +/** + * Allocate a dummy pattern. + * @param[in] pool The memory pool to allocate from. + * @return A pattern with no callbacks set. + */ +dmnsn_pattern *dmnsn_new_pattern(dmnsn_pool *pool); + +/** + * Initialize a dmnsn_pattern field. + * @param[out] pattern The pattern to initialize. + */ +void dmnsn_init_pattern(dmnsn_pattern *pattern); + +/** + * Invoke the pattern callback. + * @param[in] pattern The pattern to evaluate. + * @param[in] v The point to get the pattern value for. + * @return The value of the pattern at \p v. + */ +double dmnsn_pattern_value(const dmnsn_pattern *pattern, dmnsn_vector v); diff --git a/libdimension/dimension/pattern/patterns.h b/libdimension/dimension/pattern/patterns.h new file mode 100644 index 0000000..59b7fec --- /dev/null +++ b/libdimension/dimension/pattern/patterns.h @@ -0,0 +1,48 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Pre-defined patterns. + */ + +/** + * A checker pattern. The pattern is composed of tesselating unit cubes + * alternating between 0 and 1. + * @param[in] pool The memory pool to allocate from. + * @return A checker pattern. + */ +dmnsn_pattern *dmnsn_new_checker_pattern(dmnsn_pool *pool); + +/** + * A gradient. The value starts at 0 at the origin, and goes linearly to 1 in + * the direction of \p orientation, then repeats after a distance of 1. + * @param[in] pool The memory pool to allocate from. + * @param[in] orientation The direction of the gradient. + * @return A gradient pattern. + */ +dmnsn_pattern *dmnsn_new_gradient_pattern(dmnsn_pool *pool, dmnsn_vector orientation); + +/** + * A leopard pattern. + * @param[in] pool The memory pool to allocate from. + * @return A leopard pattern. + */ +dmnsn_pattern *dmnsn_new_leopard_pattern(dmnsn_pool *pool); diff --git a/libdimension/dimension/patterns.h b/libdimension/dimension/patterns.h deleted file mode 100644 index 59b7fec..0000000 --- a/libdimension/dimension/patterns.h +++ /dev/null @@ -1,48 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Pre-defined patterns. - */ - -/** - * A checker pattern. The pattern is composed of tesselating unit cubes - * alternating between 0 and 1. - * @param[in] pool The memory pool to allocate from. - * @return A checker pattern. - */ -dmnsn_pattern *dmnsn_new_checker_pattern(dmnsn_pool *pool); - -/** - * A gradient. The value starts at 0 at the origin, and goes linearly to 1 in - * the direction of \p orientation, then repeats after a distance of 1. - * @param[in] pool The memory pool to allocate from. - * @param[in] orientation The direction of the gradient. - * @return A gradient pattern. - */ -dmnsn_pattern *dmnsn_new_gradient_pattern(dmnsn_pool *pool, dmnsn_vector orientation); - -/** - * A leopard pattern. - * @param[in] pool The memory pool to allocate from. - * @return A leopard pattern. - */ -dmnsn_pattern *dmnsn_new_leopard_pattern(dmnsn_pool *pool); diff --git a/libdimension/dimension/pigment.h b/libdimension/dimension/pigment.h deleted file mode 100644 index 14d8bae..0000000 --- a/libdimension/dimension/pigment.h +++ /dev/null @@ -1,86 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Object pigments. - */ - -/* Forward-declare dmnsn_pigment */ -typedef struct dmnsn_pigment dmnsn_pigment; - -/** - * Pigment callback. - * @param[in] pigment The pigment itself. - * @param[in] v The point to color. - * @return The color of the pigment at \p v. - */ -typedef dmnsn_tcolor dmnsn_pigment_fn(const dmnsn_pigment *pigment, - dmnsn_vector v); - -/** - * Pigment initializer callback. - * @param[in,out] pigment The pigment to initialize. - */ -typedef void dmnsn_pigment_initialize_fn(dmnsn_pigment *pigment); - -/** A pigment. */ -struct dmnsn_pigment { - dmnsn_pigment_fn *pigment_fn; /**< The pigment callback. */ - dmnsn_pigment_initialize_fn *initialize_fn; /**< The initializer callback. */ - - dmnsn_matrix trans; /**< Transformation matrix. */ - dmnsn_matrix trans_inv; /**< The inverse of the transformation matrix. */ - - /** Quick color -- used for low-quality renders. */ - dmnsn_tcolor quick_color; - - bool initialized; /** @internal Whether the pigment is initialized. */ -}; - -/** - * Allocate a new dummy pigment. - * @param[in] pool The memory pool to allocate from. - * @return The allocated pigment. - */ -dmnsn_pigment *dmnsn_new_pigment(dmnsn_pool *pool); - -/** - * Initialize a dmnsn_pigment field. - * @param[out] pigment The pigment to initialize. - */ -void dmnsn_init_pigment(dmnsn_pigment *pigment); - -/** - * Initialize a pigment. Pigments should not be used before being initialized, - * but should not be modified after being initialized. Pigments are generally - * initialized for you. - * @param[in,out] pigment The pigment to initialize. - */ -void dmnsn_pigment_initialize(dmnsn_pigment *pigment); - -/** - * Evaluate the color of a pigment at a point. - * @param[in] pigment The pigment to evaluate. - * @param[in] v The point to color. - * @return The color at \p v. - */ -dmnsn_tcolor dmnsn_pigment_evaluate(const dmnsn_pigment *pigment, - dmnsn_vector v); diff --git a/libdimension/dimension/pigments.h b/libdimension/dimension/pigments.h deleted file mode 100644 index 100016d..0000000 --- a/libdimension/dimension/pigments.h +++ /dev/null @@ -1,66 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Pre-defined pigments. - */ - -/** - * A solid color. - * @param[in] pool The memory pool to allocate from. - * @param[in] color The color of the pigment. - * @return A pigment with the color \p color everywhere. - */ -dmnsn_pigment *dmnsn_new_solid_pigment(dmnsn_pool *pool, dmnsn_tcolor color); - -/** - * An image map. The image (regardless of its real dimensions) is projected - * on the x-y plane in tesselating unit squares. - * @param[in] pool The memory pool to allocate from. - * @param[in] canvas The canvas holding the image. - * @return An image-mapped pigment. - */ -dmnsn_pigment *dmnsn_new_canvas_pigment(dmnsn_pool *pool, dmnsn_canvas *canvas); - -/** - * Pigment map flags. - */ -typedef enum dmnsn_pigment_map_flags { - DMNSN_PIGMENT_MAP_REGULAR, /**< Calculate linear color gradients. */ - DMNSN_PIGMENT_MAP_SRGB /**< Calculate sRGB color gradients. */ -} dmnsn_pigment_map_flags; - -/** - * Construct a pigment map. - * @param[in] pool The memory pool to allocate from. - * @return An empty pigment map. - */ -dmnsn_map *dmnsn_new_pigment_map(dmnsn_pool *pool); - -/** - * A pigment-mapped pigment. - * @param[in] pool The memory pool to allocate from. - * @param[in,out] pattern The pattern of the pigment. - * @param[in,out] map The pigment map to apply to the pattern. - * @param[in] flags Gradient flags - * @return A pigment mapping the pattern to other pigments. - */ -dmnsn_pigment *dmnsn_new_pigment_map_pigment(dmnsn_pool *pool, dmnsn_pattern *pattern, dmnsn_map *map, dmnsn_pigment_map_flags flags); diff --git a/libdimension/dimension/platform.h b/libdimension/dimension/platform.h new file mode 100644 index 0000000..4c01709 --- /dev/null +++ b/libdimension/dimension/platform.h @@ -0,0 +1,39 @@ +/************************************************************************* + * Copyright (C) 2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Platform abstractions. + */ + +#ifndef DMNSN_PLATFORM_H +#define DMNSN_PLATFORM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifdef __cplusplus +} +#endif + +#endif /* DMNSN_PLATFORM_H */ diff --git a/libdimension/dimension/platform/timer.h b/libdimension/dimension/platform/timer.h new file mode 100644 index 0000000..bcf4a9d --- /dev/null +++ b/libdimension/dimension/platform/timer.h @@ -0,0 +1,58 @@ +/************************************************************************* + * Copyright (C) 2010-2011 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * A platform-agnostic timer abstraction. + */ + +#ifndef DMNSN_PLATFORM_H +#error "Please include instead of this header directly." +#endif + +/** A platform-agnotic timer. */ +typedef struct dmnsn_timer { + double real; /**< Wall-clock time. */ + double user; /**< Time spent executing. */ + double system; /**< Time spent waiting for the system. */ +} dmnsn_timer; + +/** A standard format string for timers. */ +#define DMNSN_TIMER_FORMAT "%.2fs (user: %.2fs; system: %.2fs)" +/** + * The appropriate arguments to printf() a timer. For example: + * @code + * printf(DMNSN_TIMER_FORMAT "\n", DMNSN_TIMER_PRINTF(timer)); + * @endcode + * will print something like "1.00s (user: 0.99s; system: 0.01s)". + */ +#define DMNSN_TIMER_PRINTF(t) (t).real, (t).user, (t).system + +/** + * Start a timer. The values of an unfinished timer are undefined. + * @param[in,out] timer The timer to start. + */ +void dmnsn_timer_start(dmnsn_timer *timer); + +/** + * Finish timing. The members of the timer struct will now contain timing data. + * @param[in,out] timer The timer to stop. + */ +void dmnsn_timer_stop(dmnsn_timer *timer); diff --git a/libdimension/dimension/png.h b/libdimension/dimension/png.h deleted file mode 100644 index f4d2c1e..0000000 --- a/libdimension/dimension/png.h +++ /dev/null @@ -1,71 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * PNG import/export of canvases. - */ - -#include - -/** - * Optimize a canvas for PNG exporting - * @param[in] pool The memory pool to allocate from. - * @param[in,out] canvas The canvas to optimize. - * @return Whether the canvas was successfully optimized. - */ -int dmnsn_png_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas); - -/** - * Write a canvas to a file in PNG format. - * @param[in] canvas The canvas to write. - * @param[in,out] file The file to write to. - * @return 0 on success, non-zero on failure. - */ -int dmnsn_png_write_canvas(const dmnsn_canvas *canvas, FILE *file); - -/** - * Write a canvas to a PNG file in the background. - * @param[in] canvas The canvas to write. - * @param[in,out] file The file to write to. - * @return A \ref dmnsn_future object, or NULL on failure. - */ -dmnsn_future *dmnsn_png_write_canvas_async(const dmnsn_canvas *canvas, - FILE *file); - -/** - * Read a canvas from a PNG file. - * @param[in] pool The memory pool to allocate from. - * @param[in,out] file The PNG file to read. - * @return The new canvas, or NULL on failure. - */ -dmnsn_canvas *dmnsn_png_read_canvas(dmnsn_pool *pool, FILE *file); - -/** - * Read a canvas from a PNG file in the background. - * @param[out] canvas The address of a non-allocated canvas object. The canvas - * object will be allocated and filled with the contents of - * \p file. Do not read from this object until the - * background task has finished. - * @param[in] pool The memory pool to allocate from. - * @param[in,out] file The PNG file to read. - * @return A \ref dmnsn_future object, or NULL on failure. - */ -dmnsn_future *dmnsn_png_read_canvas_async(dmnsn_canvas **canvas, dmnsn_pool *pool, FILE *file); diff --git a/libdimension/dimension/polynomial.h b/libdimension/dimension/polynomial.h deleted file mode 100644 index ab95d50..0000000 --- a/libdimension/dimension/polynomial.h +++ /dev/null @@ -1,85 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2011 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Utility functions for working with and numerically solving polynomials. - * Polynomials are represented as simple arrays where the ith element is the - * coefficient on x^i. In general, we are only interested in positive roots. - */ - -#include -#include - -/** - * Evaluate a polynomial at \p x. - * @param[in] poly The coefficients of the polynomial to evaluate, in order - * from lowest degree to highest degree. The array should - * have dimension degree + 1. - * @param[in] degree The degree of the polynomial. - * @param[in] x The value of the variable at which to evaluate. - */ -DMNSN_INLINE double -dmnsn_polynomial_evaluate(const double poly[], size_t degree, double x) -{ - double ret = poly[degree]; - size_t i; - for (i = degree; i-- > 0;) { - ret = ret*x + poly[i]; - } - return ret; -} - -/** - * Evaluate the derivative of a polynomial at \p x. - * @param[in] poly The coefficients of the polynomial to evaluate. - * @param[in] degree The degree of the polynomial. - * @param[in] x The value of the variable at which to evaluate. - */ -DMNSN_INLINE double -dmnsn_polynomial_evaluate_derivative(const double poly[], size_t degree, - double x) -{ - double ret = poly[degree]*degree; - size_t i; - for (i = degree - 1; i >= 1; --i) { - ret = ret*x + poly[i]*i; - } - return ret; -} - -/** - * Find the positive roots of a polynomial. - * @param[in] poly The coefficients of the polynomial to solve. - * @param[in] degree The degree of the polynomial. - * @param[out] x An array in which to store the roots. It should have - * dimension \p degree. - * @return The number of positive roots stored in \c x[]. - */ -size_t dmnsn_polynomial_solve(const double poly[], size_t degree, double x[]); - -/** - * Output a polynomial. The polynomial is printed as a function of x suitable - * for input into a CAS, and without a trailing newline. - * @param[in,out] file The file to write to. - * @param[in] poly The coefficients of the polynomial to print. - * @param[in] degree The degree of the polynomial. - */ -void dmnsn_polynomial_print(FILE *file, const double poly[], size_t degree); diff --git a/libdimension/dimension/pool.h b/libdimension/dimension/pool.h deleted file mode 100644 index 164bbbc..0000000 --- a/libdimension/dimension/pool.h +++ /dev/null @@ -1,77 +0,0 @@ -/************************************************************************* - * Copyright (C) 2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Memory pools. Rather than more complicated garbage collection methods like - * reference counting, objects are allocated out of pools which are freed all at - * once once a scene is rendered (for example). - */ - -#include /* For size_t */ - -/* Forward-declare dmnsn_pool. */ -typedef struct dmnsn_pool dmnsn_pool; - -/** - * Create a new memory pool. - * @return The new pool. - */ -dmnsn_pool *dmnsn_new_pool(void); - -/** - * Allocate some memory from a pool. - * @param[in] pool The memory pool to allocate from. - * @param[in] size The size of the memory block to allocate. - * @return The allocated memory area. - */ -void *dmnsn_palloc(dmnsn_pool *pool, size_t size); - -/** - * Allocate some memory from a pool. - * @param[in] pool The memory pool to allocate from. - * @param[in] size The size of the memory block to allocate. - * @param[in] cleanup_fn A callback to invoke before the memory is freed. - * @return The allocated memory area. - */ -void *dmnsn_palloc_tidy(dmnsn_pool *pool, size_t size, dmnsn_callback_fn *cleanup_fn); - -/** - * Allocate some memory from a pool. - * @param[in] pool The memory pool to allocate from. - * @param[in] type The type of the memory block to allocate. - * @return The allocated memory area. - */ -#define DMNSN_PALLOC(pool, type) ((type *)dmnsn_palloc((pool), sizeof(type))) - -/** - * Allocate some memory from a pool. - * @param[in] pool The memory pool to allocate from. - * @param[in] type The type of the memory block to allocate. - * @param[in] cleanup_fn A callback to invoke before the memory is freed. - * @return The allocated memory area. - */ -#define DMNSN_PALLOC_TIDY(pool, type, cleanup_fn) ((type *)dmnsn_palloc_tidy((pool), sizeof(type), (cleanup_fn))) - -/** - * Free a memory pool and all associated allocations. - * @param[in] pool The memory pool to free. - */ -void dmnsn_delete_pool(dmnsn_pool *pool); diff --git a/libdimension/dimension/ray_trace.h b/libdimension/dimension/ray_trace.h deleted file mode 100644 index 82fb759..0000000 --- a/libdimension/dimension/ray_trace.h +++ /dev/null @@ -1,37 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2011 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Ray-trace a scene. - */ - -/** - * Render a scene by ray tracing. - * @param[in,out] scene The scene to render. - */ -void dmnsn_ray_trace(dmnsn_scene *scene); - -/** - * Render a scene in the background. - * @param[in,out] scene The scene to render. - * @return A \p dmnsn_future object. - */ -dmnsn_future *dmnsn_ray_trace_async(dmnsn_scene *scene); diff --git a/libdimension/dimension/render.h b/libdimension/dimension/render.h new file mode 100644 index 0000000..296d7a3 --- /dev/null +++ b/libdimension/dimension/render.h @@ -0,0 +1,41 @@ +/************************************************************************* + * Copyright (C) 2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Rendering. + */ + +#ifndef DMNSN_RENDER_H +#define DMNSN_RENDER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +#ifdef __cplusplus +} +#endif + +#endif /* DMNSN_RENDER_H */ diff --git a/libdimension/dimension/render/render.h b/libdimension/dimension/render/render.h new file mode 100644 index 0000000..69f52c2 --- /dev/null +++ b/libdimension/dimension/render/render.h @@ -0,0 +1,41 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Rendering. + */ + +#ifndef DMNSN_RENDER_H +#error "Please include instead of this header directly." +#endif + +/** + * Render a scene. + * @param[in,out] scene The scene to render. + */ +void dmnsn_render(dmnsn_scene *scene); + +/** + * Render a scene in the background. + * @param[in,out] scene The scene to render. + * @return A \p dmnsn_future object. + */ +dmnsn_future *dmnsn_render_async(dmnsn_scene *scene); diff --git a/libdimension/dimension/scene.h b/libdimension/dimension/scene.h deleted file mode 100644 index dd0c1ba..0000000 --- a/libdimension/dimension/scene.h +++ /dev/null @@ -1,95 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Entire scenes. - */ - -/** Render quality flags. */ -enum { - DMNSN_RENDER_NONE = 0, /**< Render nothing. */ - DMNSN_RENDER_PIGMENT = 1 << 0, /**< Render pigments. */ - DMNSN_RENDER_LIGHTS = 1 << 1, /**< Render lights and shadows. */ - DMNSN_RENDER_FINISH = 1 << 2, /**< Render object finishes. */ - DMNSN_RENDER_TRANSPARENCY = 1 << 3, /**< Render transparency/refraction. */ - DMNSN_RENDER_REFLECTION = 1 << 4, /**< Render specular reflection. */ - DMNSN_RENDER_FULL = ~DMNSN_RENDER_NONE /**< Render everything. */ -}; - -/** Render quality. */ -typedef unsigned int dmnsn_quality; - -/** An entire scene. */ -typedef struct dmnsn_scene { - /* World attributes */ - dmnsn_pigment *background; /**< Background pigment. */ - dmnsn_texture *default_texture; /**< Default object texture. */ - dmnsn_interior *default_interior; /**< Default object interior. */ - - /** Canvas. */ - dmnsn_canvas *canvas; - - /* Support for rendering image subregions. */ - size_t region_x; /**< The x position of the canvas in the broader image. */ - size_t region_y; /**< The y position of the canvas in the broader image. */ - size_t outer_width; /**< Width of the broader image. */ - size_t outer_height; /**< Height of the broader image. */ - - /** Objects. */ - dmnsn_array *objects; - - /** Lights. */ - dmnsn_array *lights; - - /** Camera. */ - dmnsn_camera *camera; - - /** Render quality. */ - dmnsn_quality quality; - - /** Recursion limit. */ - unsigned int reclimit; - - /** Adaptive depth control bailout. */ - double adc_bailout; - - /** Number of parallel threads. */ - unsigned int nthreads; - - /** Timers. */ - dmnsn_timer bounding_timer; - dmnsn_timer render_timer; - - bool initialized; /**< @internal Whether the scene is initialized. */ -} dmnsn_scene; - -/** - * Create a scene. - * @param[in] pool The memory pool to allocate from. - * @return A new empty scene. - */ -dmnsn_scene *dmnsn_new_scene(dmnsn_pool *pool); - -/** - * Initialize a scene. - * @param[in,out] scene The scene to initalize. - */ -void dmnsn_scene_initialize(dmnsn_scene *scene); diff --git a/libdimension/dimension/tcolor.h b/libdimension/dimension/tcolor.h deleted file mode 100644 index 8c48800..0000000 --- a/libdimension/dimension/tcolor.h +++ /dev/null @@ -1,112 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Colors with transparency information. - */ - -/** A transparent color. */ -typedef struct dmnsn_tcolor { - dmnsn_color c; /**< Color. */ - double T; /**< Transparency. */ - double F; /**< Proportion of filtered transparency. */ -} dmnsn_tcolor; - -/** A standard format string for colors. */ -#define DMNSN_TCOLOR_FORMAT "TColor<%g, %g, %g, %g, %g>" -/** The appropriate arguements to printf() a color. */ -#define DMNSN_TCOLOR_PRINTF(tc) (tc).c.R, (tc).c.G, (tc).c.B, (tc).T, (tc).F - -/** Create a tcolor. */ -DMNSN_INLINE dmnsn_tcolor -dmnsn_new_tcolor(dmnsn_color c, double T, double F) -{ - dmnsn_tcolor ret = { c, T, F }; - return ret; -} - -/** Convert a dmnsn_color into a dmnsn_tcolor. */ -#define DMNSN_TCOLOR(c) dmnsn_new_tcolor(c, 0.0, 0.0) - -/** Create a tcolor with individually-specified components. */ -DMNSN_INLINE dmnsn_tcolor -dmnsn_new_tcolor5(double R, double G, double B, double T, double F) -{ - dmnsn_tcolor ret = { dmnsn_new_color(R, G, B), T, F }; - return ret; -} - -/** Return the color at \p n on a gradient from \p c1 at 0 to \p c2 at 1. */ -DMNSN_INLINE dmnsn_tcolor -dmnsn_tcolor_gradient(dmnsn_tcolor c1, dmnsn_tcolor c2, double n) -{ - return dmnsn_new_tcolor( - dmnsn_color_gradient(c1.c, c2.c, n), - n*(c2.T - c1.T) + c1.T, - n*(c2.F - c1.F) + c1.F - ); -} - -/** Filter \p light through \p filter. */ -DMNSN_INLINE dmnsn_color -dmnsn_tcolor_filter(dmnsn_color light, dmnsn_tcolor filter) -{ - dmnsn_color filtered = - dmnsn_color_mul(filter.T*filter.F, dmnsn_color_illuminate(light, filter.c)); - dmnsn_color transmitted = dmnsn_color_mul(filter.T*(1.0 - filter.F), light); - return dmnsn_color_add(filtered, transmitted); -} - -/** Remove the filtered component of a tcolor. */ -DMNSN_INLINE dmnsn_tcolor -dmnsn_tcolor_remove_filter(dmnsn_tcolor tcolor) -{ - double intensity = dmnsn_color_intensity(tcolor.c); - double newtrans = (1.0 - (1.0 - intensity)*tcolor.F)*tcolor.T; - if (1.0 - newtrans >= dmnsn_epsilon) { - tcolor.c = dmnsn_color_mul((1.0 - tcolor.T)/(1.0 - newtrans), tcolor.c); - } - tcolor.T = newtrans; - tcolor.F = 0.0; - return tcolor; -} - -/** Saturate the tcolor components to [0.0, 1.0]. */ -DMNSN_INLINE dmnsn_tcolor -dmnsn_tcolor_clamp(dmnsn_tcolor tcolor) -{ - tcolor.c = dmnsn_color_clamp(tcolor.c); - tcolor.T = dmnsn_clamp(tcolor.T, 0.0, 1.0); - tcolor.F = dmnsn_clamp(tcolor.F, 0.0, 1.0); - return tcolor; -} - -/** Return whether a tcolor contains any NaN components. */ -DMNSN_INLINE bool -dmnsn_tcolor_isnan(dmnsn_tcolor tcolor) -{ - return dmnsn_color_isnan(tcolor.c) || dmnsn_isnan(tcolor.T) || dmnsn_isnan(tcolor.F); -} - -/* Standard tcolors */ - -/** Clear. */ -#define dmnsn_clear dmnsn_new_tcolor5(0.0, 0.0, 0.0, 1.0, 0.0) diff --git a/libdimension/dimension/texture.h b/libdimension/dimension/texture.h deleted file mode 100644 index df08a4a..0000000 --- a/libdimension/dimension/texture.h +++ /dev/null @@ -1,57 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Object textures. - */ - -/** A complete texture. */ -typedef struct { - dmnsn_pigment *pigment; /**< Pigment. */ - dmnsn_finish finish; /**< Finish. */ - - dmnsn_matrix trans; /**< Transformation matrix. */ - dmnsn_matrix trans_inv; /**< The inverse of the transformation matrix. */ - - bool initialized; /**< @internal Whether the texture is initialized yet. */ -} dmnsn_texture; - -/** - * Create a blank texture. - * @param[in] pool The memory pool to allocate from. - * @return The new texture. - */ -dmnsn_texture *dmnsn_new_texture(dmnsn_pool *pool); - -/** - * Initialize a texture. Textures should not be used before being initialized, - * but should not be modified after being initialized. Textures are generally - * initialized for you. - * @param[in,out] texture The texture to initialize. - */ -void dmnsn_texture_initialize(dmnsn_texture *texture); - -/** - * Fill missing texture properties from a default texture. - * @param[in] default_texture The default texture. - * @param[in,out] texturep A pointer to the texture to fill. - */ -void dmnsn_texture_cascade(dmnsn_texture *default_texture, dmnsn_texture **texturep); diff --git a/libdimension/dimension/timer.h b/libdimension/dimension/timer.h deleted file mode 100644 index 55d66ff..0000000 --- a/libdimension/dimension/timer.h +++ /dev/null @@ -1,54 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2011 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * A platform-agnostic timer abstraction. - */ - -/** A platform-agnotic timer. */ -typedef struct dmnsn_timer { - double real; /**< Wall-clock time. */ - double user; /**< Time spent executing. */ - double system; /**< Time spent waiting for the system. */ -} dmnsn_timer; - -/** A standard format string for timers. */ -#define DMNSN_TIMER_FORMAT "%.2fs (user: %.2fs; system: %.2fs)" -/** - * The appropriate arguments to printf() a timer. For example: - * @code - * printf(DMNSN_TIMER_FORMAT "\n", DMNSN_TIMER_PRINTF(timer)); - * @endcode - * will print something like "1.00s (user: 0.99s; system: 0.01s)". - */ -#define DMNSN_TIMER_PRINTF(t) (t).real, (t).user, (t).system - -/** - * Start a timer. The values of an unfinished timer are undefined. - * @param[in,out] timer The timer to start. - */ -void dmnsn_timer_start(dmnsn_timer *timer); - -/** - * Finish timing. The members of the timer struct will now contain timing data. - * @param[in,out] timer The timer to stop. - */ -void dmnsn_timer_stop(dmnsn_timer *timer); diff --git a/libdimension/error.c b/libdimension/error.c deleted file mode 100644 index e9e7cf1..0000000 --- a/libdimension/error.c +++ /dev/null @@ -1,143 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Error handling. - */ - -#include "dimension-internal.h" -#include -#include -#include -#include -#include -#include -#include - -/// Report internal errors in this file. -#define DMNSN_LOCAL_ERROR(str) \ - do { \ - fprintf(stderr, "Dimension ERROR: %s, %s:%u: %s\n", \ - DMNSN_FUNC, __FILE__, __LINE__, (str)); \ - abort(); \ - } while (0) - -/// The default fatal error handler. -static void dmnsn_default_fatal_error_fn(void); - -/// The current fatal error handler. -static atomic(dmnsn_fatal_error_fn *) dmnsn_fatal = ATOMIC_VAR_INIT(dmnsn_default_fatal_error_fn); - -/// The current resilience. -static atomic_bool dmnsn_always_die = ATOMIC_VAR_INIT(false); - -void -dmnsn_report_impl(bool die, const char *func, const char *file, unsigned int line, const char *str) -{ - // Save the value of errno - int err = errno; - - bool always_die = atomic_load(&dmnsn_always_die); - - // Print the diagnostic string - fprintf(stderr, "Dimension %s: %s, %s:%u: %s\n", - die ? "ERROR" : "WARNING", func, file, line, str); - - // Print the value of errno - if (err != 0) { - fprintf(stderr, "Last error: %d", err); -#if DMNSN_STRERROR_R - char errbuf[256]; - if (strerror_r(err, errbuf, 256) == 0) { - fprintf(stderr, " (%s)", errbuf); - } -#elif DMNSN_SYS_ERRLIST - if (err >= 0 && err < sys_nerr) { - fprintf(stderr, " (%s)", sys_errlist[err]); - } -#endif - fprintf(stderr, "\n"); - } - - // Print a stack trace to standard error - dmnsn_backtrace(stderr); - - // Call the fatal error handler if needed - if (die || always_die) { - static __thread bool thread_exiting = false; - - if (thread_exiting) { - if (die) { - // Prevent infinite recursion if the fatal error function itself calls - // dmnsn_error() (not dmnsn_warning()) - DMNSN_LOCAL_ERROR("Error raised while in error handler, aborting."); - } - } else { - thread_exiting = true; - - dmnsn_fatal_error_fn *fatal = dmnsn_get_fatal_error_fn(); - fatal(); - DMNSN_LOCAL_ERROR("Fatal error handler didn't exit."); - } - } -} - -void -dmnsn_report_warning(const char *func, const char *file, unsigned int line, const char *str) -{ - dmnsn_report_impl(false, func, file, line, str); -} - -DMNSN_NORETURN -dmnsn_report_error(const char *func, const char *file, unsigned int line, const char *str) -{ - dmnsn_report_impl(true, func, file, line, str); - DMNSN_UNREACHABLE(); -} - -void -dmnsn_die_on_warnings(bool always_die) -{ - atomic_store(&dmnsn_always_die, always_die); -} - -dmnsn_fatal_error_fn * -dmnsn_get_fatal_error_fn(void) -{ - dmnsn_fatal_error_fn *fatal = atomic_load(&dmnsn_fatal); - return fatal; -} - -void -dmnsn_set_fatal_error_fn(dmnsn_fatal_error_fn *fatal) -{ - atomic_store(&dmnsn_fatal, fatal); -} - -static void -dmnsn_default_fatal_error_fn(void) -{ - if (dmnsn_is_main_thread()) { - exit(EXIT_FAILURE); - } else { - pthread_exit(NULL); - } -} diff --git a/libdimension/finish.c b/libdimension/finish.c deleted file mode 100644 index 13adefb..0000000 --- a/libdimension/finish.c +++ /dev/null @@ -1,102 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Finishes. - */ - -#include "dimension-internal.h" - -dmnsn_ambient * -dmnsn_new_ambient(dmnsn_pool *pool, dmnsn_color ambient_light) -{ - dmnsn_ambient *ambient = DMNSN_PALLOC(pool, dmnsn_ambient); - ambient->ambient = ambient_light; - return ambient; -} - -dmnsn_diffuse * -dmnsn_new_diffuse(dmnsn_pool *pool) -{ - dmnsn_diffuse *diffuse = DMNSN_PALLOC(pool, dmnsn_diffuse); - dmnsn_init_diffuse(diffuse); - return diffuse; -} - -void -dmnsn_init_diffuse(dmnsn_diffuse *diffuse) -{ -} - -dmnsn_specular * -dmnsn_new_specular(dmnsn_pool *pool) -{ - dmnsn_specular *specular = DMNSN_PALLOC(pool, dmnsn_specular); - dmnsn_init_specular(specular); - return specular; -} - -void -dmnsn_init_specular(dmnsn_specular *specular) -{ -} - -dmnsn_reflection * -dmnsn_new_reflection(dmnsn_pool *pool) -{ - dmnsn_reflection *reflection = DMNSN_PALLOC(pool, dmnsn_reflection); - dmnsn_init_reflection(reflection); - return reflection; -} - -void -dmnsn_init_reflection(dmnsn_reflection *reflection) -{ -} - -dmnsn_finish -dmnsn_new_finish(void) -{ - dmnsn_finish finish = { - .ambient = NULL, - .diffuse = NULL, - .specular = NULL, - .reflection = NULL, - }; - return finish; -} - -void -dmnsn_finish_cascade(const dmnsn_finish *default_finish, dmnsn_finish *finish) -{ - if (!finish->ambient) { - finish->ambient = default_finish->ambient; - } - if (!finish->diffuse) { - finish->diffuse = default_finish->diffuse; - } - if (!finish->specular) { - finish->specular = default_finish->specular; - } - if (!finish->reflection) { - finish->reflection = default_finish->reflection; - } -} diff --git a/libdimension/future-internal.h b/libdimension/future-internal.h deleted file mode 100644 index 7ca318a..0000000 --- a/libdimension/future-internal.h +++ /dev/null @@ -1,71 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Future object implementation. - */ - -#include - -/// Allocate a new future object. -DMNSN_INTERNAL dmnsn_future *dmnsn_new_future(void); - -/// Set the total number of loop iterations. -DMNSN_INTERNAL void dmnsn_future_set_total(dmnsn_future *future, size_t total); -/// Increment the progress of a background task. -DMNSN_INTERNAL void dmnsn_future_increment(dmnsn_future *future); -/// Instantly complete the background teask. -DMNSN_INTERNAL void dmnsn_future_finish(dmnsn_future *future); -/// Set the number of worker threads. -DMNSN_INTERNAL void dmnsn_future_set_nthreads(dmnsn_future *future, - unsigned int nthreads); -/// Notify completion of a worker thread. -DMNSN_INTERNAL void dmnsn_future_finish_thread(dmnsn_future *future); - -struct dmnsn_future { - size_t progress; ///< Completed loop iterations. - size_t total; ///< Total expected loop iterations. - - /// The worker thread. - pthread_t thread; - - /// Mutex to guard progress and total. - pthread_mutex_t mutex; - - /// Condition variable for waiting for a particular amount of progress. - pthread_cond_t cond; - - /// Minimum waited-on value. - double min_wait; - - /// Number of threads working on the future's background task. - unsigned int nthreads; - /// Number of threads not yet paused. - unsigned int nrunning; - /// Count of threads holding the future paused. - unsigned int npaused; - /// Condition variable for waiting for nrunning == 0. - pthread_cond_t none_running_cond; - /// Condition variable for waiting for nrunning == nthreads. - pthread_cond_t all_running_cond; - /// Condition variable for waiting for npaused == 0. - pthread_cond_t resume_cond; -}; diff --git a/libdimension/future.c b/libdimension/future.c deleted file mode 100644 index 8133812..0000000 --- a/libdimension/future.c +++ /dev/null @@ -1,279 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Future objects. - */ - -#include "dimension-internal.h" -#include - -/** - * Since C doesn't support anything like C++'s mutable, we fake it by casting - * away the constness. This is okay since all valid dmnsn_futures live on the - * heap, so cannot be const. - */ -#define MUTATE(future) ((dmnsn_future *)(future)) - -// Allocate a new dmnsn_future* -dmnsn_future * -dmnsn_new_future(void) -{ - dmnsn_future *future = DMNSN_MALLOC(dmnsn_future); - future->progress = 0; - future->total = 1; - - dmnsn_initialize_mutex(&future->mutex); - dmnsn_initialize_cond(&future->cond); - - future->min_wait = 1.0; - - future->nthreads = future->nrunning = 1; - future->npaused = 0; - dmnsn_initialize_cond(&future->none_running_cond); - dmnsn_initialize_cond(&future->all_running_cond); - dmnsn_initialize_cond(&future->resume_cond); - - return future; -} - -static void -dmnsn_delete_future(dmnsn_future *future) -{ - if (future) { - dmnsn_destroy_cond(&future->resume_cond); - dmnsn_destroy_cond(&future->all_running_cond); - dmnsn_destroy_cond(&future->none_running_cond); - dmnsn_destroy_cond(&future->cond); - dmnsn_destroy_mutex(&future->mutex); - dmnsn_free(future); - } -} - -// Join the worker thread and delete `future'. -int -dmnsn_future_join(dmnsn_future *future) -{ - void *ptr; - int retval = -1; - - if (future) { - dmnsn_assert(future->npaused == 0, "Attempt to join future while paused"); - - // Get the thread's return value - dmnsn_join_thread(future->thread, &ptr); - if (ptr && ptr != PTHREAD_CANCELED) { - retval = *(int *)ptr; - dmnsn_free(ptr); - } - - // Free the future object - dmnsn_delete_future(future); - } - - return retval; -} - -// Cancel a background thread -void -dmnsn_future_cancel(dmnsn_future *future) -{ - pthread_cancel(future->thread); -} - -/** - * Get the current progress, without locking anything. - * - * future->mutex must be locked for this call to be safe. - */ -static inline double -dmnsn_future_progress_unlocked(const dmnsn_future *future) -{ - return (double)future->progress/future->total; -} - -// Get the current progress of the worker thread, in [0.0, 1.0] -double -dmnsn_future_progress(const dmnsn_future *future) -{ - dmnsn_future *mfuture = MUTATE(future); - double progress; - - dmnsn_lock_mutex(&mfuture->mutex); - progress = dmnsn_future_progress_unlocked(mfuture); - dmnsn_unlock_mutex(&mfuture->mutex); - - return progress; -} - -// Find out whether the task is complete. -bool -dmnsn_future_is_done(const dmnsn_future *future) -{ - dmnsn_future *mfuture = MUTATE(future); - bool result; - - dmnsn_lock_mutex(&mfuture->mutex); - result = future->progress == future->total; - dmnsn_unlock_mutex(&mfuture->mutex); - - return result; -} - -// Wait until dmnsn_future_progress(future) >= progress -void -dmnsn_future_wait(const dmnsn_future *future, double progress) -{ - dmnsn_future *mfuture = MUTATE(future); - - dmnsn_lock_mutex(&mfuture->mutex); - while (dmnsn_future_progress_unlocked(mfuture) < progress) { - // Set the minimum waited-on value - if (progress < mfuture->min_wait) { - mfuture->min_wait = progress; - } - - dmnsn_cond_wait_safely(&mfuture->cond, &mfuture->mutex); - } - dmnsn_unlock_mutex(&mfuture->mutex); -} - -// Pause all threads working on a future. -void -dmnsn_future_pause(dmnsn_future *future) -{ - dmnsn_lock_mutex(&future->mutex); - while (future->nrunning < future->nthreads) { - dmnsn_cond_wait_safely(&future->all_running_cond, &future->mutex); - } - ++future->npaused; - while (future->nrunning > 0) { - dmnsn_cond_wait_safely(&future->none_running_cond, &future->mutex); - } - dmnsn_unlock_mutex(&future->mutex); -} - -// Resume all threads working on a future. -void -dmnsn_future_resume(dmnsn_future *future) -{ - dmnsn_lock_mutex(&future->mutex); - dmnsn_assert(future->npaused > 0, "dmnsn_future_resume() without matching dmnsn_future_pause()"); - if (--future->npaused == 0) { - dmnsn_cond_broadcast(&future->resume_cond); - } - dmnsn_unlock_mutex(&future->mutex); -} - -// Set the total number of loop iterations -void -dmnsn_future_set_total(dmnsn_future *future, size_t total) -{ - dmnsn_lock_mutex(&future->mutex); - future->total = total; - dmnsn_unlock_mutex(&future->mutex); -} - -static void -dmnsn_future_increment_cleanup(void *ptr) -{ - dmnsn_future *future = ptr; - ++future->nrunning; - dmnsn_unlock_mutex_impl(&future->mutex); -} - -// Increment the number of completed loop iterations -void -dmnsn_future_increment(dmnsn_future *future) -{ - // Allow a thread to be canceled whenever it increments a future object -- - // this is close to PTHREAD_CANCEL_ASYNCHRONOUS but allows consistent state - // on cancellation - pthread_testcancel(); - - dmnsn_lock_mutex(&future->mutex); - ++future->progress; - - if (dmnsn_future_progress_unlocked(future) >= future->min_wait) { - future->min_wait = 1.0; - dmnsn_cond_broadcast(&future->cond); - } - - if (future->npaused > 0) { - dmnsn_assert(future->nrunning > 0, "More worker threads than expected"); - - if (--future->nrunning == 0) { - dmnsn_cond_broadcast(&future->none_running_cond); - } - - pthread_cleanup_push(dmnsn_future_increment_cleanup, future); - do { - dmnsn_cond_wait(&future->resume_cond, &future->mutex); - } while (future->npaused > 0); - pthread_cleanup_pop(false); - - if (++future->nrunning == future->nthreads) { - dmnsn_cond_broadcast(&future->all_running_cond); - } - } - dmnsn_unlock_mutex(&future->mutex); -} - -// Immediately set to 100% completion -void -dmnsn_future_finish(dmnsn_future *future) -{ - dmnsn_lock_mutex(&future->mutex); - future->progress = future->total; - future->nthreads = future->nrunning = 0; - dmnsn_cond_broadcast(&future->cond); - dmnsn_cond_broadcast(&future->none_running_cond); - dmnsn_cond_broadcast(&future->all_running_cond); - dmnsn_unlock_mutex(&future->mutex); -} - -// Set the number of threads -void -dmnsn_future_set_nthreads(dmnsn_future *future, unsigned int nthreads) -{ - dmnsn_lock_mutex(&future->mutex); - dmnsn_assert(future->nrunning == future->nthreads, - "dmnsn_future_set_nthreads() called with paused threads"); - future->nthreads = future->nrunning = nthreads; - dmnsn_unlock_mutex(&future->mutex); -} - -// Notify completion of a worker thread -void -dmnsn_future_finish_thread(dmnsn_future *future) -{ - dmnsn_lock_mutex(&future->mutex); - dmnsn_assert(future->nthreads > 0, - "dmnsn_future_finish_thread() called with no threads"); - --future->nthreads; - - dmnsn_assert(future->nrunning > 0, - "dmnsn_future_finish_thread() called with no running threads"); - if (--future->nrunning == 0) { - dmnsn_cond_broadcast(&future->none_running_cond); - } - dmnsn_unlock_mutex(&future->mutex); -} diff --git a/libdimension/geometry.c b/libdimension/geometry.c deleted file mode 100644 index 4a16a3c..0000000 --- a/libdimension/geometry.c +++ /dev/null @@ -1,387 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Geometrical function implementations. - */ - -#include "dimension-internal.h" -#include - -// Identity matrix -dmnsn_matrix -dmnsn_identity_matrix(void) -{ - return dmnsn_new_matrix(1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0); -} - -// Scaling matrix -dmnsn_matrix -dmnsn_scale_matrix(dmnsn_vector s) -{ - return dmnsn_new_matrix(s.x, 0.0, 0.0, 0.0, - 0.0, s.y, 0.0, 0.0, - 0.0, 0.0, s.z, 0.0); -} - -// Translation matrix -dmnsn_matrix -dmnsn_translation_matrix(dmnsn_vector d) -{ - return dmnsn_new_matrix(1.0, 0.0, 0.0, d.x, - 0.0, 1.0, 0.0, d.y, - 0.0, 0.0, 1.0, d.z); -} - -// Left-handed rotation matrix; theta/|theta| = axis, |theta| = angle -dmnsn_matrix -dmnsn_rotation_matrix(dmnsn_vector theta) -{ - // Two trig calls, 25 multiplications, 13 additions - - double angle = dmnsn_vector_norm(theta); - if (fabs(angle) < dmnsn_epsilon) { - return dmnsn_identity_matrix(); - } - dmnsn_vector axis = dmnsn_vector_div(theta, angle); - - // Shorthand to make dmnsn_new_matrix() call legible - - double s = sin(angle); - double t = 1.0 - cos(angle); - - double x = axis.x; - double y = axis.y; - double z = axis.z; - - return dmnsn_new_matrix( - 1.0 + t*(x*x - 1.0), -z*s + t*x*y, y*s + t*x*z, 0.0, - z*s + t*x*y, 1.0 + t*(y*y - 1.0), -x*s + t*y*z, 0.0, - -y*s + t*x*z, x*s + t*y*z, 1.0 + t*(z*z - 1.0), 0.0 - ); -} - -// Find the angle between two vectors with respect to an axis -static double -dmnsn_axis_angle(dmnsn_vector from, dmnsn_vector to, dmnsn_vector axis) -{ - from = dmnsn_vector_sub(from, dmnsn_vector_proj(from, axis)); - to = dmnsn_vector_sub(to, dmnsn_vector_proj(to, axis)); - - double fromnorm = dmnsn_vector_norm(from); - double tonorm = dmnsn_vector_norm(to); - if (fromnorm < dmnsn_epsilon || tonorm < dmnsn_epsilon) { - return 0.0; - } - - from = dmnsn_vector_div(from, fromnorm); - to = dmnsn_vector_div(to, tonorm); - - double angle = acos(dmnsn_vector_dot(from, to)); - - if (dmnsn_vector_dot(dmnsn_vector_cross(from, to), axis) > 0.0) { - return angle; - } else { - return -angle; - } -} - -// Alignment matrix -dmnsn_matrix -dmnsn_alignment_matrix(dmnsn_vector from, dmnsn_vector to, - dmnsn_vector axis1, dmnsn_vector axis2) -{ - double theta1 = dmnsn_axis_angle(from, to, axis1); - dmnsn_matrix align1 = dmnsn_rotation_matrix(dmnsn_vector_mul(theta1, axis1)); - from = dmnsn_transform_direction(align1, from); - axis2 = dmnsn_transform_direction(align1, axis2); - - double theta2 = dmnsn_axis_angle(from, to, axis2); - dmnsn_matrix align2 = dmnsn_rotation_matrix(dmnsn_vector_mul(theta2, axis2)); - - return dmnsn_matrix_mul(align2, align1); -} - -// Matrix inversion helper functions - -/// A 2x2 matrix for inversion by partitioning. -typedef struct { double n[2][2]; } dmnsn_matrix2; - -/// Construct a 2x2 matrix. -static dmnsn_matrix2 dmnsn_new_matrix2(double a1, double a2, - double b1, double b2); -/// Invert a 2x2 matrix. -static dmnsn_matrix2 dmnsn_matrix2_inverse(dmnsn_matrix2 A); -/// Negate a 2x2 matrix. -static dmnsn_matrix2 dmnsn_matrix2_negate(dmnsn_matrix2 A); -/// Subtract two 2x2 matricies. -static dmnsn_matrix2 dmnsn_matrix2_sub(dmnsn_matrix2 lhs, dmnsn_matrix2 rhs); -/// Add two 2x2 matricies. -static dmnsn_matrix2 dmnsn_matrix2_mul(dmnsn_matrix2 lhs, dmnsn_matrix2 rhs); - -/// Invert a matrix with the slower cofactor algorithm, if partitioning failed. -static dmnsn_matrix dmnsn_matrix_inverse_generic(dmnsn_matrix A); -/// Get the [\p row, \p col] cofactor of A. -static double dmnsn_matrix_cofactor(dmnsn_matrix A, size_t row, size_t col); - -// Invert a matrix, by partitioning -dmnsn_matrix -dmnsn_matrix_inverse(dmnsn_matrix A) -{ - // Use partitioning to invert a matrix: - // - // [ P Q ] -1 - // [ R S ] - // - // = [ PP QQ ] - // [ RR SS ], - // - // with PP = inv(P) - inv(P)*Q*RR, - // QQ = -inv(P)*Q*SS, - // RR = -SS*R*inv(P), and - // SS = inv(S - R*inv(P)*Q). - - // The algorithm uses 2 inversions, 6 multiplications, and 2 subtractions, - // giving 52 multiplications, 34 additions, and 8 divisions. - - dmnsn_matrix2 P, Q, R, S, Pi, RPi, PiQ, RPiQ, PP, QQ, RR, SS; - double Pdet = A.n[0][0]*A.n[1][1] - A.n[0][1]*A.n[1][0]; - - if (dmnsn_unlikely(fabs(Pdet) < dmnsn_epsilon)) { - // If P is close to singular, try a more generic algorithm; this is very - // unlikely, but not impossible, eg. - // [ 1 1 0 0 ] - // [ 1 1 1 0 ] - // [ 0 1 1 0 ] - // [ 0 0 0 1 ] - return dmnsn_matrix_inverse_generic(A); - } - - // Partition the matrix - P = dmnsn_new_matrix2(A.n[0][0], A.n[0][1], - A.n[1][0], A.n[1][1]); - Q = dmnsn_new_matrix2(A.n[0][2], A.n[0][3], - A.n[1][2], A.n[1][3]); - R = dmnsn_new_matrix2(A.n[2][0], A.n[2][1], - 0.0, 0.0); - S = dmnsn_new_matrix2(A.n[2][2], A.n[2][3], - 0.0, 1.0); - - // Do this inversion ourselves, since we already have the determinant - Pi = dmnsn_new_matrix2( P.n[1][1]/Pdet, -P.n[0][1]/Pdet, - -P.n[1][0]/Pdet, P.n[0][0]/Pdet); - - // Calculate R*inv(P), inv(P)*Q, and R*inv(P)*Q - RPi = dmnsn_matrix2_mul(R, Pi); - PiQ = dmnsn_matrix2_mul(Pi, Q); - RPiQ = dmnsn_matrix2_mul(R, PiQ); - - // Calculate the partitioned inverse - SS = dmnsn_matrix2_inverse(dmnsn_matrix2_sub(S, RPiQ)); - RR = dmnsn_matrix2_negate(dmnsn_matrix2_mul(SS, RPi)); - QQ = dmnsn_matrix2_negate(dmnsn_matrix2_mul(PiQ, SS)); - PP = dmnsn_matrix2_sub(Pi, dmnsn_matrix2_mul(PiQ, RR)); - - // Reconstruct the matrix - return dmnsn_new_matrix(PP.n[0][0], PP.n[0][1], QQ.n[0][0], QQ.n[0][1], - PP.n[1][0], PP.n[1][1], QQ.n[1][0], QQ.n[1][1], - RR.n[0][0], RR.n[0][1], SS.n[0][0], SS.n[0][1]); -} - -// For nice shorthand -static dmnsn_matrix2 -dmnsn_new_matrix2(double a1, double a2, double b1, double b2) -{ - dmnsn_matrix2 m = { { { a1, a2 }, - { b1, b2 } } }; - return m; -} - -// Invert a 2x2 matrix -static dmnsn_matrix2 -dmnsn_matrix2_inverse(dmnsn_matrix2 A) -{ - // 4 divisions, 2 multiplications, 1 addition - double det = A.n[0][0]*A.n[1][1] - A.n[0][1]*A.n[1][0]; - return dmnsn_new_matrix2( A.n[1][1]/det, -A.n[0][1]/det, - -A.n[1][0]/det, A.n[0][0]/det); -} - -// Also basically a shorthand -static dmnsn_matrix2 -dmnsn_matrix2_negate(dmnsn_matrix2 A) -{ - return dmnsn_new_matrix2(-A.n[0][0], -A.n[0][1], - -A.n[1][0], -A.n[1][1]); -} - -// 2x2 matrix subtraction -static dmnsn_matrix2 -dmnsn_matrix2_sub(dmnsn_matrix2 lhs, dmnsn_matrix2 rhs) -{ - // 4 additions - return dmnsn_new_matrix2( - lhs.n[0][0] - rhs.n[0][0], lhs.n[0][1] - rhs.n[0][1], - lhs.n[1][0] - rhs.n[1][0], lhs.n[1][1] - rhs.n[1][1] - ); -} - -// 2x2 matrix multiplication -static dmnsn_matrix2 -dmnsn_matrix2_mul(dmnsn_matrix2 lhs, dmnsn_matrix2 rhs) -{ - // 8 multiplications, 4 additions - return dmnsn_new_matrix2( - lhs.n[0][0]*rhs.n[0][0] + lhs.n[0][1]*rhs.n[1][0], - lhs.n[0][0]*rhs.n[0][1] + lhs.n[0][1]*rhs.n[1][1], - lhs.n[1][0]*rhs.n[0][0] + lhs.n[1][1]*rhs.n[1][0], - lhs.n[1][0]*rhs.n[0][1] + lhs.n[1][1]*rhs.n[1][1] - ); -} - -// Invert a matrix, if partitioning failed (|P| == 0) -static dmnsn_matrix -dmnsn_matrix_inverse_generic(dmnsn_matrix A) -{ - // For A = [ A' b ] A^-1 = [ A'^-1 -(A'^-1)*b ] - // [ 0 ... 0 1 ], [ 0 ... 0 1 ]. - // - // Invert A' by calculating its adjucate. - dmnsn_matrix inv; - double det = 0.0, C; - - // Perform a Laplace expansion along the first row to give us the adjugate's - // first column and the determinant - for (size_t j = 0; j < 3; ++j) { - C = dmnsn_matrix_cofactor(A, 0, j); - det += A.n[0][j]*C; - inv.n[j][0] = C; - } - - // Divide the first column by the determinant - for (size_t j = 0; j < 3; ++j) { - inv.n[j][0] /= det; - } - - // Find the rest of A' - for (size_t j = 0; j < 3; ++j) { - for (size_t i = 1; i < 3; ++i) { - inv.n[j][i] = dmnsn_matrix_cofactor(A, i, j)/det; - } - inv.n[j][3] = 0.0; - } - - // Find the translational component of the inverse - for (size_t i = 0; i < 3; ++i) { - for (size_t j = 0; j < 3; ++j) { - inv.n[i][3] -= inv.n[i][j]*A.n[j][3]; - } - } - - return inv; -} - -// Gives the cofactor at row, col; the determinant of the matrix formed from the -// upper-left 3x3 corner of A by ignoring row `row' and column `col', -// times (-1)^(row + col) -static double -dmnsn_matrix_cofactor(dmnsn_matrix A, size_t row, size_t col) -{ - // 2 multiplications, 1 addition - double n[4]; - size_t k = 0; - for (size_t i = 0; i < 3; ++i) { - for (size_t j = 0; j < 3; ++j) { - if (i != row && j != col) { - n[k] = A.n[i][j]; - ++k; - } - } - } - - double C = n[0]*n[3] - n[1]*n[2]; - if ((row + col)%2 == 0) { - return C; - } else { - return -C; - } -} - -// 4x4 matrix multiplication -dmnsn_matrix -dmnsn_matrix_mul(dmnsn_matrix lhs, dmnsn_matrix rhs) -{ - // 36 multiplications, 27 additions - dmnsn_matrix r; - - r.n[0][0] = lhs.n[0][0]*rhs.n[0][0] + lhs.n[0][1]*rhs.n[1][0] + lhs.n[0][2]*rhs.n[2][0]; - r.n[0][1] = lhs.n[0][0]*rhs.n[0][1] + lhs.n[0][1]*rhs.n[1][1] + lhs.n[0][2]*rhs.n[2][1]; - r.n[0][2] = lhs.n[0][0]*rhs.n[0][2] + lhs.n[0][1]*rhs.n[1][2] + lhs.n[0][2]*rhs.n[2][2]; - r.n[0][3] = lhs.n[0][0]*rhs.n[0][3] + lhs.n[0][1]*rhs.n[1][3] + lhs.n[0][2]*rhs.n[2][3] + lhs.n[0][3]; - - r.n[1][0] = lhs.n[1][0]*rhs.n[0][0] + lhs.n[1][1]*rhs.n[1][0] + lhs.n[1][2]*rhs.n[2][0]; - r.n[1][1] = lhs.n[1][0]*rhs.n[0][1] + lhs.n[1][1]*rhs.n[1][1] + lhs.n[1][2]*rhs.n[2][1]; - r.n[1][2] = lhs.n[1][0]*rhs.n[0][2] + lhs.n[1][1]*rhs.n[1][2] + lhs.n[1][2]*rhs.n[2][2]; - r.n[1][3] = lhs.n[1][0]*rhs.n[0][3] + lhs.n[1][1]*rhs.n[1][3] + lhs.n[1][2]*rhs.n[2][3] + lhs.n[1][3]; - - r.n[2][0] = lhs.n[2][0]*rhs.n[0][0] + lhs.n[2][1]*rhs.n[1][0] + lhs.n[2][2]*rhs.n[2][0]; - r.n[2][1] = lhs.n[2][0]*rhs.n[0][1] + lhs.n[2][1]*rhs.n[1][1] + lhs.n[2][2]*rhs.n[2][1]; - r.n[2][2] = lhs.n[2][0]*rhs.n[0][2] + lhs.n[2][1]*rhs.n[1][2] + lhs.n[2][2]*rhs.n[2][2]; - r.n[2][3] = lhs.n[2][0]*rhs.n[0][3] + lhs.n[2][1]*rhs.n[1][3] + lhs.n[2][2]*rhs.n[2][3] + lhs.n[2][3]; - - return r; -} - -// Give an axis-aligned box that contains the given box transformed by `lhs' -dmnsn_bounding_box -dmnsn_transform_bounding_box(dmnsn_matrix trans, dmnsn_bounding_box box) -{ - // Infinite/zero bounding box support - if (isinf(box.min.x)) { - return box; - } - - // Taking the "absolute value" of the matrix saves some min/max calculations - for (int i = 0; i < 3; ++i) { - for (int j = 0; j < 3; ++j) { - trans.n[i][j] = fabs(trans.n[i][j]); - } - } - - dmnsn_vector Mt = dmnsn_matrix_column(trans, 3); - dmnsn_bounding_box ret = { Mt, Mt }; - - dmnsn_vector Mz = dmnsn_matrix_column(trans, 2); - ret.min = dmnsn_vector_add(ret.min, dmnsn_vector_mul(box.min.z, Mz)); - ret.max = dmnsn_vector_add(ret.max, dmnsn_vector_mul(box.max.z, Mz)); - - dmnsn_vector My = dmnsn_matrix_column(trans, 1); - ret.min = dmnsn_vector_add(ret.min, dmnsn_vector_mul(box.min.y, My)); - ret.max = dmnsn_vector_add(ret.max, dmnsn_vector_mul(box.max.y, My)); - - dmnsn_vector Mx = dmnsn_matrix_column(trans, 0); - ret.min = dmnsn_vector_add(ret.min, dmnsn_vector_mul(box.min.x, Mx)); - ret.max = dmnsn_vector_add(ret.max, dmnsn_vector_mul(box.max.x, Mx)); - - return ret; -} diff --git a/libdimension/gl-stubs.c b/libdimension/gl-stubs.c deleted file mode 100644 index df31308..0000000 --- a/libdimension/gl-stubs.c +++ /dev/null @@ -1,48 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Stubs for GL functions when compiled with --disable-gl. - */ - -#include "dimension.h" -#include - -int -dmnsn_gl_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas) -{ - errno = ENOSYS; - return -1; -} - -int -dmnsn_gl_write_canvas(const dmnsn_canvas *canvas) -{ - errno = ENOSYS; - return -1; -} - -int -dmnsn_gl_read_canvas(dmnsn_canvas *canvas, size_t x0, size_t y0) -{ - errno = ENOSYS; - return NULL; -} diff --git a/libdimension/gl.c b/libdimension/gl.c deleted file mode 100644 index 77099d2..0000000 --- a/libdimension/gl.c +++ /dev/null @@ -1,111 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * OpenGL import/export. - */ - -#include "dimension-internal.h" -#include -#include -#include - -// Optimize canvas for GL drawing -int -dmnsn_gl_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas) -{ - dmnsn_rgba8_optimize_canvas(pool, canvas); - return 0; -} - -// Write canvas to GL framebuffer. Returns 0 on success, nonzero on failure -int -dmnsn_gl_write_canvas(const dmnsn_canvas *canvas) -{ - size_t width = canvas->width; - size_t height = canvas->height; - - // Check if we can optimize this - dmnsn_rgba8_optimizer *rgba8 = (dmnsn_rgba8_optimizer *)dmnsn_canvas_find_optimizer(canvas, dmnsn_rgba8_optimizer_fn); - if (rgba8) { - glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_BYTE, rgba8->data); - return glGetError() == GL_NO_ERROR ? 0 : 1; - } - - // We couldn't, so transform the canvas to RGB now - GLubyte *pixels = dmnsn_malloc(4*width*height*sizeof(GLubyte)); - - for (size_t y = 0; y < height; ++y) { - for (size_t x = 0; x < width; ++x) { - GLubyte *pixel = pixels + 4*(y*width + x); - - dmnsn_tcolor tcolor = dmnsn_canvas_get_pixel(canvas, x, y); - tcolor = dmnsn_tcolor_remove_filter(tcolor); - tcolor.c = dmnsn_color_to_sRGB(tcolor.c); - tcolor = dmnsn_tcolor_clamp(tcolor); - - pixel[0] = lround(tcolor.c.R*UINT8_MAX); - pixel[1] = lround(tcolor.c.G*UINT8_MAX); - pixel[2] = lround(tcolor.c.B*UINT8_MAX); - pixel[3] = lround(tcolor.T*UINT8_MAX); - } - } - - glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - - dmnsn_free(pixels); - return glGetError() == GL_NO_ERROR ? 0 : 1; -} - -// Read a canvas from a GL framebuffer. Returns NULL on failure. -int -dmnsn_gl_read_canvas(dmnsn_canvas *canvas, size_t x0, size_t y0) -{ - size_t width = canvas->width; - size_t height = canvas->height; - - // Array of 16-bit ints in RGBA order - GLushort *pixels = dmnsn_malloc(4*width*height*sizeof(GLushort)); - glReadPixels(x0, y0, width, height, GL_RGBA, GL_UNSIGNED_SHORT, pixels); - if (glGetError() != GL_NO_ERROR) { - dmnsn_free(pixels); - return -1; - } - - for (size_t y = 0; y < height; ++y) { - for (size_t x = 0; x < width; ++x) { - GLushort *pixel = pixels + 4*(y*width + x); - - dmnsn_tcolor tcolor = dmnsn_new_tcolor5( - (double)pixel[0]/UINT16_MAX, - (double)pixel[1]/UINT16_MAX, - (double)pixel[2]/UINT16_MAX, - (double)pixel[3]/UINT16_MAX, - 0.0 - ); - tcolor.c = dmnsn_color_from_sRGB(tcolor.c); - dmnsn_canvas_set_pixel(canvas, x, y, tcolor); - } - } - - dmnsn_free(pixels); - return 0; -} diff --git a/libdimension/gradient.c b/libdimension/gradient.c deleted file mode 100644 index 74e8045..0000000 --- a/libdimension/gradient.c +++ /dev/null @@ -1,56 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Gradient pattern. - */ - -#include "dimension.h" - -/// Gradient pattern type. -typedef struct dmnns_gradient { - dmnsn_pattern pattern; - dmnsn_vector orientation; -} dmnsn_gradient; - -/// Gradient pattern callback. -static double -dmnsn_gradient_pattern_fn(const dmnsn_pattern *pattern, dmnsn_vector v) -{ - const dmnsn_gradient *gradient = (const dmnsn_gradient *)pattern; - double n = fmod(dmnsn_vector_dot(gradient->orientation, v), 1.0); - if (n < -dmnsn_epsilon) { - n += 1.0; - } - return n; -} - -dmnsn_pattern * -dmnsn_new_gradient_pattern(dmnsn_pool *pool, dmnsn_vector orientation) -{ - dmnsn_gradient *gradient = DMNSN_PALLOC(pool, dmnsn_gradient); - gradient->orientation = dmnsn_vector_normalized(orientation); - - dmnsn_pattern *pattern = &gradient->pattern; - dmnsn_init_pattern(pattern); - pattern->pattern_fn = dmnsn_gradient_pattern_fn; - return pattern; -} diff --git a/libdimension/inline.c b/libdimension/inline.c deleted file mode 100644 index b0622fe..0000000 --- a/libdimension/inline.c +++ /dev/null @@ -1,43 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Emit definitions of inline functions, if necessary. - */ - -// Set DMNSN_INLINE to produce definitions of inline functions, emitted here, -// if needed -#ifdef __cplusplus - // C++ inline semantics - #define DMNSN_INLINE inline -#elif __STDC_VERSION__ >= 199901L - // C99 inline semantics - #define DMNSN_INLINE -#elif defined(__GNUC__) - // GCC inline semantics - #define DMNSN_INLINE __inline__ -#else - // Unknown C - mark functions static and hope the compiler is smart enough to - // inline them - #define DMNSN_INLINE static -#endif - -#include "dimension.h" diff --git a/libdimension/interior.c b/libdimension/interior.c deleted file mode 100644 index d725b6f..0000000 --- a/libdimension/interior.c +++ /dev/null @@ -1,46 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Interiors. - */ - -#include "dimension-internal.h" -#include - -// Allocate an interior -dmnsn_interior * -dmnsn_new_interior(dmnsn_pool *pool) -{ - dmnsn_interior *interior = DMNSN_PALLOC(pool, dmnsn_interior); - interior->ior = 1.0; - return interior; -} - -// Cascade an interior -void -dmnsn_interior_cascade(dmnsn_interior *default_interior, - dmnsn_interior **interiorp) -{ - if (!*interiorp) { - *interiorp = default_interior; - } -} diff --git a/libdimension/internal.h b/libdimension/internal.h new file mode 100644 index 0000000..3db2612 --- /dev/null +++ b/libdimension/internal.h @@ -0,0 +1,35 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * The basic internal libdimension API. These functions and types are used to + * implement libdimension, but are not part of its public API. + */ + +#ifndef DMNSN_INTERNAL_H +#define DMNSN_INTERNAL_H + +#include "dimension/base.h" + +#include "internal/compiler.h" +#include "internal/profile.h" + +#endif // DMNSN_INTERNAL_H diff --git a/libdimension/internal/bvh.h b/libdimension/internal/bvh.h new file mode 100644 index 0000000..c97e2ba --- /dev/null +++ b/libdimension/internal/bvh.h @@ -0,0 +1,79 @@ +/************************************************************************* + * Copyright (C) 2012-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file. + * Bounding volume hierarchy. This generic interface allows different data + * structures to be represented in the same way, thus sharing code for their + * traversal algorithm. + */ + +#ifndef DMNSN_INTERNAL_BVH_H +#define DMNSN_INTERNAL_BVH_H + +#include "internal.h" +#include "dimension/math.h" +#include "dimension/model.h" +#include + +/// A bounding volume hierarchy. +typedef struct dmnsn_bvh dmnsn_bvh; + +/// Available BVH implementations. +typedef enum dmnsn_bvh_kind { + DMNSN_BVH_NONE, + DMNSN_BVH_PRTREE, +} dmnsn_bvh_kind; + +/// Create a BVH. +DMNSN_INTERNAL dmnsn_bvh *dmnsn_new_bvh(const dmnsn_array *objects, + dmnsn_bvh_kind kind); +/// Delete a BVH. +DMNSN_INTERNAL void dmnsn_delete_bvh(dmnsn_bvh *bvh); + +/// Find the closest ray-object intersection in the tree. +DMNSN_INTERNAL bool dmnsn_bvh_intersection(const dmnsn_bvh *bvh, dmnsn_ray ray, dmnsn_intersection *intersection, bool reset); +/// Determine whether a point is inside any object in the tree. +DMNSN_INTERNAL bool dmnsn_bvh_inside(const dmnsn_bvh *bvh, dmnsn_vector point); +/// Return the bounding box of the whole hierarchy. +DMNSN_INTERNAL dmnsn_aabb dmnsn_bvh_aabb(const dmnsn_bvh *bvh); + +/// A non-flat BVH representation, used by BVH implementations. +typedef struct dmnsn_bvh_node { + dmnsn_aabb aabb; ///< The bounding box of this node. + dmnsn_object *object; ///< The object, for leaf nodes. + unsigned int nchildren; ///< How many children this node has. + unsigned int max_children; ///< Maximum number of children. + struct dmnsn_bvh_node *children[]; ///< Flexible array of children. +} dmnsn_bvh_node; + +/// Create a BVH node. +DMNSN_INTERNAL dmnsn_bvh_node *dmnsn_new_bvh_node(unsigned int max_children); + +/// Create a BVH leaf node. +DMNSN_INTERNAL dmnsn_bvh_node *dmnsn_new_bvh_leaf_node(dmnsn_object *object); + +/// Delete a BVH node. +DMNSN_INTERNAL void dmnsn_delete_bvh_node(dmnsn_bvh_node *node); + +/// Add a child to a BVH node. +DMNSN_INTERNAL void dmnsn_bvh_node_add(dmnsn_bvh_node *parent, dmnsn_bvh_node *child); + +#endif // DMNSN_INTERNAL_BVH_H diff --git a/libdimension/internal/compiler.h b/libdimension/internal/compiler.h new file mode 100644 index 0000000..e3555a4 --- /dev/null +++ b/libdimension/internal/compiler.h @@ -0,0 +1,71 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Internally-used compiler abstractions. + */ + +#ifndef DMNSN_INTERNAL_H +#error "Please include \"internal.h\" instead of this header directly." +#endif + +#include + +/** + * @def DMNSN_HOT + * Mark a function as a hot path. + */ +/** + * @def DMNSN_INTERNAL + * Mark a function as internal linkage. + */ +/** + * @def DMNSN_DESTRUCTOR + * Queue a function to run at program termination. + */ +/** + * @def DMNSN_LATE_DESTRUCTOR + * Queue a function to run at program termination, after those labeled + * DMNSN_DESTRUCTOR. + */ +#if DMNSN_GNUC + #define DMNSN_HOT __attribute__((hot)) + #define DMNSN_INTERNAL __attribute__((visibility("hidden"))) + #define DMNSN_DESTRUCTOR __attribute__((destructor(102))) + #define DMNSN_LATE_DESTRUCTOR __attribute__((destructor(101))) +#else + #define DMNSN_HOT + #define DMNSN_INTERNAL + #define DMNSN_DESTRUCTOR + #define DMNSN_LATE_DESTRUCTOR +#endif + +/// Synonym for _Atomic that stdatomic.h doesn't define for some reason +#define atomic _Atomic + +/// Nearly-compliant alloca variant +#define DMNSN_ALLOCA(var, size) DMNSN_ALLOCA_IMPL(var, size, __LINE__) + +#define DMNSN_ALLOCA_IMPL(var, size, ctr) DMNSN_ALLOCA_IMPL2(var, size, ctr) + +#define DMNSN_ALLOCA_IMPL2(var, size, ctr) \ + alignas(max_align_t) char dmnsn_alloca##ctr[size]; \ + var = (void *)dmnsn_alloca##ctr diff --git a/libdimension/internal/concurrency.h b/libdimension/internal/concurrency.h new file mode 100644 index 0000000..e9c024a --- /dev/null +++ b/libdimension/internal/concurrency.h @@ -0,0 +1,236 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Internal threading abstraction. + */ + +#ifndef DMNSN_INTERNAL_CONCURRENCY_H +#define DMNSN_INTERNAL_CONCURRENCY_H + +#include "internal.h" +#include "dimension/concurrency.h" +#include + +/// Allocate a new future object. +DMNSN_INTERNAL dmnsn_future *dmnsn_new_future(void); + +/// Set the total number of loop iterations. +DMNSN_INTERNAL void dmnsn_future_set_total(dmnsn_future *future, size_t total); +/// Increment the progress of a background task. +DMNSN_INTERNAL void dmnsn_future_increment(dmnsn_future *future); +/// Instantly complete the background teask. +DMNSN_INTERNAL void dmnsn_future_finish(dmnsn_future *future); +/// Set the number of worker threads. +DMNSN_INTERNAL void dmnsn_future_set_nthreads(dmnsn_future *future, unsigned int nthreads); +/// Notify completion of a worker thread. +DMNSN_INTERNAL void dmnsn_future_finish_thread(dmnsn_future *future); + +/** + * Thread callback type. + * @param[in,out] ptr An arbitrary pointer. + * @return 0 on success, non-zero on failure. + */ +typedef int dmnsn_thread_fn(void *ptr); + +/** + * Create a thread that cleans up after itself on errors. + * @param[in,out] future The future object to associate with the thread. + * @param[in] thread_fn The thread callback. + * @param[in,out] arg The pointer to pass to the thread callback. + */ +DMNSN_INTERNAL void dmnsn_new_thread(dmnsn_future *future, + dmnsn_thread_fn *thread_fn, void *arg); + +/** + * Thread callback type for parallel tasks. + * @param[in,out] ptr An arbitrary pointer. + * @param[in] thread An ID for this thread, in [0, \p nthreads). + * @param[in] nthreads The number of concurrent threads. + * @return 0 on success, non-zero on failure. + */ +typedef int dmnsn_ccthread_fn(void *ptr, unsigned int thread, + unsigned int nthreads); + +/** + * Run \p nthreads threads in parallel. + * @param[in,out] future The future object to associate with the threads, + * possibly NULL. + * @param[in] ccthread_fn The routine to run in each concurrent thread. + * @param[in,out] arg The pointer to pass to the thread callbacks. + * @param[in] nthreads The number of concurrent threads to run. + * @return 0 if all threads were successful, and an error code otherwise. + */ +DMNSN_INTERNAL int dmnsn_execute_concurrently(dmnsn_future *future, + dmnsn_ccthread_fn *ccthread_fn, + void *arg, unsigned int nthreads); + +/** + * Initialize a mutex, bailing out on failure. + * @param[out] mutex The mutex to initialize. + */ +DMNSN_INTERNAL void dmnsn_initialize_mutex(pthread_mutex_t *mutex); + +/// dmnsn_lock_mutex() implementation. +DMNSN_INTERNAL void dmnsn_lock_mutex_impl(pthread_mutex_t *mutex); +/// dmnsn_unlock_mutex() implementation. +DMNSN_INTERNAL void dmnsn_unlock_mutex_impl(void *mutex); + +/** + * Lock a mutex, bailing out on failure. + * Contains a {, so must be used in the same block as dmnsn_unlock_mutex(). + * @param[in,out] mutex The mutex to lock. + */ +#define dmnsn_lock_mutex(mutex) do { dmnsn_lock_mutex_impl((mutex)) + +/** + * Unlock a mutex, bailing out on failure. + * Contains a }, so must be used in the same block as dmnsn_lock_mutex(). + * @param[in,out] mutex The mutex to unlock. + */ +#define dmnsn_unlock_mutex(mutex) dmnsn_unlock_mutex_impl((mutex)); } while (0) + +/** + * Destroy a mutex, warning on failure. + * @param[in,out] mutex The mutex to destroy. + */ +DMNSN_INTERNAL void dmnsn_destroy_mutex(pthread_mutex_t *mutex); + +/** + * Initialize a read-write lock, bailing out on failure. + * @param[out] rwlock The read-write lock to initialize. + */ +DMNSN_INTERNAL void dmnsn_initialize_rwlock(pthread_rwlock_t *rwlock); + +/// dmnsn_read_lock() implementation. +DMNSN_INTERNAL void dmnsn_read_lock_impl(pthread_rwlock_t *rwlock); +/// dmnsn_write_lock() implementation. +DMNSN_INTERNAL void dmnsn_write_lock_impl(pthread_rwlock_t *rwlock); +/// dmnsn_unlock_rwlock() implementation. +DMNSN_INTERNAL void dmnsn_unlock_rwlock_impl(pthread_rwlock_t *rwlock); + +/** + * Read-lock a read-write lock, bailing out on failure. + * Contains a {, so must be used in the same block as dmnsn_unlock_rwlock(). + * @param[in,out] rwlock The read-write lock to lock. + */ +#define dmnsn_read_lock(rwlock) do { dmnsn_read_lock_impl((rwlock)) + +/** + * Write-lock a read-write lock, bailing out on failure. + * Contains a {, so must be used in the same block as dmnsn_unlock_rwlock(). + * @param[in,out] rwlock The read-write lock to lock. + */ +#define dmnsn_write_lock(rwlock) do { dmnsn_write_lock_impl((rwlock)) + +/** + * Unlock a read-write lock, bailing out on failure. + * Contains a }, so must be used in the same block as dmnsn_read_lock() or + * dmnsn_write_lock(). + * @param[in,out] rwlock The read-write lock to lock. + */ +#define dmnsn_unlock_rwlock(rwlock) \ + dmnsn_unlock_rwlock_impl((rwlock)); } while (0) + +/** + * Destroy a read-write lock, warning on failure. + * @param[in,out] rwlock The read-write lock to destroy. + */ +DMNSN_INTERNAL void dmnsn_destroy_rwlock(pthread_rwlock_t *rwlock); + +/** + * Initialize a condition variable, bailing out on failure. + * @param[out] cond The condition variable to initialize. + */ +DMNSN_INTERNAL void dmnsn_initialize_cond(pthread_cond_t *cond); + +/** + * Wait on a condition variable, bailing out on error. + * @param[in] cond The condition variable to wait on. + * @param[in] mutex The associated mutex. + */ +DMNSN_INTERNAL void dmnsn_cond_wait(pthread_cond_t *cond, + pthread_mutex_t *mutex); + +/** + * Wait on a condition variable, bailing out on error, and unlock the mutex if + * cancelled. + * @param[in] cond The condition variable to wait on. + * @param[in] mutex The associated mutex. + */ +#define dmnsn_cond_wait_safely(cond, mutex) \ + do { \ + pthread_cleanup_push(dmnsn_unlock_mutex_impl, (mutex)); \ + dmnsn_cond_wait((cond), (mutex)); \ + pthread_cleanup_pop(false); \ + } while (0) + +/** + * Signal a condition variable, bailing out on error. + * @param[in] cond The condition variable to signal. + */ +DMNSN_INTERNAL void dmnsn_cond_broadcast(pthread_cond_t *cond); + +/** + * Destroy a condition variable, warning on failure. + * @param[in,out] cond The condition variable to destroy. + */ +DMNSN_INTERNAL void dmnsn_destroy_cond(pthread_cond_t *cond); + +/** + * Once-called callback type. + */ +typedef void dmnsn_once_fn(void); + +/** + * Call a function exactly once, bailing out on failure. + * @param[in,out] once The once control. + * @param[in] once_fn The function to call. + */ +DMNSN_INTERNAL void dmnsn_once(pthread_once_t *once, dmnsn_once_fn *once_fn); + +/** + * Initialize a thread-local storage key, bailing out on failure. + * @param[out] key The key to initialize. + * @param[in] destructor An optional destructor callback. + */ +DMNSN_INTERNAL void dmnsn_key_create(pthread_key_t *key, dmnsn_callback_fn *destructor); + +/** + * Set a thread-specific pointer, bailing out on failure. + * @param[in] key The thread-local storage key. + * @param[in] value The value to set. + */ +DMNSN_INTERNAL void dmnsn_setspecific(pthread_key_t key, const void *value); + +/** + * Destroy a thread-local storage key, warning on failure. + * @param[out] key The key to destroy. + */ +DMNSN_INTERNAL void dmnsn_key_delete(pthread_key_t key); + +/** + * Join a thread, bailing out on failure. + * @param[in,out] thread The thread to join. + */ +DMNSN_INTERNAL void dmnsn_join_thread(pthread_t thread, void **retval); + +#endif // DMNSN_INTERNAL_CONCURRENCY_H diff --git a/libdimension/internal/future.h b/libdimension/internal/future.h new file mode 100644 index 0000000..047523e --- /dev/null +++ b/libdimension/internal/future.h @@ -0,0 +1,61 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Future object implementation. + */ + +#ifndef DMNSN_INTERNAL_FUTURE_H +#define DMNSN_INTERNAL_FUTURE_H + +#include + +struct dmnsn_future { + size_t progress; ///< Completed loop iterations. + size_t total; ///< Total expected loop iterations. + + /// The worker thread. + pthread_t thread; + + /// Mutex to guard progress and total. + pthread_mutex_t mutex; + + /// Condition variable for waiting for a particular amount of progress. + pthread_cond_t cond; + + /// Minimum waited-on value. + double min_wait; + + /// Number of threads working on the future's background task. + unsigned int nthreads; + /// Number of threads not yet paused. + unsigned int nrunning; + /// Count of threads holding the future paused. + unsigned int npaused; + /// Condition variable for waiting for nrunning == 0. + pthread_cond_t none_running_cond; + /// Condition variable for waiting for nrunning == nthreads. + pthread_cond_t all_running_cond; + /// Condition variable for waiting for npaused == 0. + pthread_cond_t resume_cond; +}; + +#endif // DMNSN_INTERNAL_FUTURE_H diff --git a/libdimension/internal/platform.h b/libdimension/internal/platform.h new file mode 100644 index 0000000..e037d21 --- /dev/null +++ b/libdimension/internal/platform.h @@ -0,0 +1,67 @@ +/************************************************************************* + * Copyright (C) 2010-2011 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Internal platform abstractions. + */ + +#ifndef DMNSN_INTERNAL_PLATFORM_H +#define DMNSN_INTERNAL_PLATFORM_H + +#include "internal.h" +#include "dimension/platform.h" +#include +#include +#include + +/** + * Print a stack trace, if implemented for the current platform. + * @param[in,out] file The file to which to write the stack trace. + */ +DMNSN_INTERNAL void dmnsn_backtrace(FILE *file); + +/** + * Is the calling thread the main thread? + * @return Whether this is the main execution thread, or \c true if we can't + * tell. + */ +DMNSN_INTERNAL bool dmnsn_is_main_thread(void); + +/** + * Are we running on a little-endian computer? + * @return Whether the current architecture is little-endian. + */ +DMNSN_INTERNAL bool dmnsn_is_little_endian(void); + +/** + * How many CPUs are available? + * @return The number of CPUs available to dimension. + */ +DMNSN_INTERNAL size_t dmnsn_ncpus(void); + +/** + * Calculate process times. + * @param[out] timer The timer in which to store the current total process + * times. + */ +DMNSN_INTERNAL void dmnsn_get_times(dmnsn_timer *timer); + +#endif // DMNSN_INTERNAL_PLATFORM_H diff --git a/libdimension/internal/polynomial.h b/libdimension/internal/polynomial.h new file mode 100644 index 0000000..7af3e5a --- /dev/null +++ b/libdimension/internal/polynomial.h @@ -0,0 +1,85 @@ +/************************************************************************* + * Copyright (C) 2009-2011 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Utility functions for working with and numerically solving polynomials. + * Polynomials are represented as simple arrays where the ith element is the + * coefficient on x^i. In general, we are only interested in positive roots. + */ + +#include "internal.h" +#include +#include + +/** + * Evaluate a polynomial at \p x. + * @param[in] poly The coefficients of the polynomial to evaluate, in order + * from lowest degree to highest degree. The array should + * have dimension degree + 1. + * @param[in] degree The degree of the polynomial. + * @param[in] x The value of the variable at which to evaluate. + */ +DMNSN_INTERNAL DMNSN_INLINE double +dmnsn_polynomial_evaluate(const double poly[], size_t degree, double x) +{ + double ret = poly[degree]; + size_t i; + for (i = degree; i-- > 0;) { + ret = ret*x + poly[i]; + } + return ret; +} + +/** + * Evaluate the derivative of a polynomial at \p x. + * @param[in] poly The coefficients of the polynomial to evaluate. + * @param[in] degree The degree of the polynomial. + * @param[in] x The value of the variable at which to evaluate. + */ +DMNSN_INTERNAL DMNSN_INLINE double +dmnsn_polynomial_evaluate_derivative(const double poly[], size_t degree, double x) +{ + double ret = poly[degree]*degree; + size_t i; + for (i = degree - 1; i >= 1; --i) { + ret = ret*x + poly[i]*i; + } + return ret; +} + +/** + * Find the positive roots of a polynomial. + * @param[in] poly The coefficients of the polynomial to solve. + * @param[in] degree The degree of the polynomial. + * @param[out] x An array in which to store the roots. It should have + * dimension \p degree. + * @return The number of positive roots stored in \c x[]. + */ +DMNSN_INTERNAL size_t dmnsn_polynomial_solve(const double poly[], size_t degree, double x[]); + +/** + * Output a polynomial. The polynomial is printed as a function of x suitable + * for input into a CAS, and without a trailing newline. + * @param[in,out] file The file to write to. + * @param[in] poly The coefficients of the polynomial to print. + * @param[in] degree The degree of the polynomial. + */ +DMNSN_INTERNAL void dmnsn_polynomial_print(FILE *file, const double poly[], size_t degree); diff --git a/libdimension/internal/profile.h b/libdimension/internal/profile.h new file mode 100644 index 0000000..0f59b8f --- /dev/null +++ b/libdimension/internal/profile.h @@ -0,0 +1,68 @@ +/************************************************************************* + * Copyright (C) 2010-2011 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Built-in branch profiler. + */ + +#ifndef DMNSN_INTERNAL_H +#error "Please include \"internal.h\" instead of this header directly." +#endif + +#include + +/** + * @def dmnsn_likely + * Indicate that a test is likely to succeed. + * @param test The test to perform. + * @return The truth value of \p test. + */ +/** + * @def dmnsn_unlikely + * Indicate that a test is unlikely to succeed. + * @param test The test to perform. + * @return The truth value of \p test. + */ +#ifdef DMNSN_PROFILE + #define dmnsn_likely(test) \ + dmnsn_expect(!!(test), true, DMNSN_FUNC, __FILE__, __LINE__) + #define dmnsn_unlikely(test) \ + dmnsn_expect(!!(test), false, DMNSN_FUNC, __FILE__, __LINE__) +#elif DMNSN_GNUC + #define dmnsn_likely(test) __builtin_expect(!!(test), true) + #define dmnsn_unlikely(test) __builtin_expect(!!(test), false) +#else + #define dmnsn_likely(test) (!!(test)) + #define dmnsn_unlikely(test) (!!(test)) +#endif + +/** + * Record a test and its expected result. Called by dmnsn_[un]likely(); + * don't call directly. + * @param[in] result The result of the test. + * @param[in] expected The expected result of the test. + * @param[in] func The name of the function the test occurs in. + * @param[in] file The name of the file the test occurs in. + * @param[in] line The line number on which the test occurs. + * @return \p result. + */ +DMNSN_INTERNAL bool dmnsn_expect(bool result, bool expected, const char *func, + const char *file, unsigned int line); diff --git a/libdimension/internal/prtree.h b/libdimension/internal/prtree.h new file mode 100644 index 0000000..7d85f44 --- /dev/null +++ b/libdimension/internal/prtree.h @@ -0,0 +1,38 @@ +/************************************************************************* + * Copyright (C) 2010-2012 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file. + * Priority R-trees. PR-trees are a data structure introduced by Arge, de Berg, + * Haverkort, and Yi, which provides asymptotically optimal worst-case lookup, + * while remaining efficient with real-world data. Their structure is derived + * from B-trees. + */ + +#ifndef DMNSN_INTERNAL_PRTREE_H +#define DMNSN_INTERNAL_PRTREE_H + +#include "internal.h" +#include "internal/bvh.h" + +/// Create a PR-tree. +DMNSN_INTERNAL dmnsn_bvh_node *dmnsn_new_prtree(const dmnsn_array *objects); + +#endif // DMNSN_INTERNAL_PRTREE_H diff --git a/libdimension/internal/rgba.h b/libdimension/internal/rgba.h new file mode 100644 index 0000000..964d622 --- /dev/null +++ b/libdimension/internal/rgba.h @@ -0,0 +1,55 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * RGBA canvas optimizer interface, used by image optimizers. + */ + +#ifndef DMNSN_INTERNAL_RGBA_H +#define DMNSN_INTERNAL_RGBA_H + +#include "internal.h" +#include "dimension/canvas.h" +#include + +/// RGBA8 optimizer type. +typedef struct dmnsn_rgba8_optimizer { + dmnsn_canvas_optimizer optimizer; + uint8_t data[]; +} dmnsn_rgba8_optimizer; + +/// RGBA16 optimizer type. +typedef struct dmnsn_rgba16_optimizer { + dmnsn_canvas_optimizer optimizer; + uint16_t data[]; +} dmnsn_rgba16_optimizer; + +/// Apply the RGBA8 optimizer to a canvas. +DMNSN_INTERNAL void dmnsn_rgba8_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas); +/// Apply the RGBA16 optimizer to a canvas. +DMNSN_INTERNAL void dmnsn_rgba16_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas); + +/// RGBA8 optimizer callback. +DMNSN_INTERNAL void dmnsn_rgba8_optimizer_fn(dmnsn_canvas_optimizer *optimizer, const dmnsn_canvas *canvas, size_t x, size_t y); +/// RGBA16 optimizer callback. +DMNSN_INTERNAL void dmnsn_rgba16_optimizer_fn(dmnsn_canvas_optimizer *optimizer, const dmnsn_canvas *canvas, size_t x, size_t y); + +#endif // DMNSN_INTERNAL_RGBA_H diff --git a/libdimension/lambertian.c b/libdimension/lambertian.c deleted file mode 100644 index cced4a7..0000000 --- a/libdimension/lambertian.c +++ /dev/null @@ -1,57 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Diffuse finish. - */ - -#include "dimension.h" -#include -#include - -/// Lambertian diffuse type. -typedef struct dmnsn_lambertian { - dmnsn_diffuse diffuse; - double coeff; -} dmnsn_lambertian; - -/// Diffuse finish callback. -static dmnsn_color -dmnsn_lambertian_diffuse_fn(const dmnsn_diffuse *diffuse, - dmnsn_color light, dmnsn_color color, - dmnsn_vector ray, dmnsn_vector normal) -{ - const dmnsn_lambertian *lambertian = (const dmnsn_lambertian *)diffuse; - double diffuse_factor = fabs((lambertian->coeff)*dmnsn_vector_dot(ray, normal)); - return dmnsn_color_mul(diffuse_factor, dmnsn_color_illuminate(light, color)); -} - -dmnsn_diffuse * -dmnsn_new_lambertian(dmnsn_pool *pool, double coeff) -{ - dmnsn_lambertian *lambertian = DMNSN_PALLOC(pool, dmnsn_lambertian); - lambertian->coeff = coeff; - - dmnsn_diffuse *diffuse = &lambertian->diffuse; - dmnsn_init_diffuse(diffuse); - diffuse->diffuse_fn = dmnsn_lambertian_diffuse_fn; - return diffuse; -} diff --git a/libdimension/leopard.c b/libdimension/leopard.c deleted file mode 100644 index a4000d2..0000000 --- a/libdimension/leopard.c +++ /dev/null @@ -1,46 +0,0 @@ -/************************************************************************* - * Copyright (C) 2011-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Leopard pattern. - */ - -#include "dimension-internal.h" -#include - -/// Leopard pattern callback. -static double -dmnsn_leopard_pattern_fn(const dmnsn_pattern *leopard, dmnsn_vector v) -{ - double val = (sin(v.x) + sin(v.y) + sin(v.z))/3.0; - return val*val; -} - -/// The singleton instance. -static dmnsn_pattern dmnsn_leopard_instance = { - .pattern_fn = dmnsn_leopard_pattern_fn, -}; - -dmnsn_pattern * -dmnsn_new_leopard_pattern(dmnsn_pool *pool) -{ - return &dmnsn_leopard_instance; -} diff --git a/libdimension/light.c b/libdimension/light.c deleted file mode 100644 index 14917de..0000000 --- a/libdimension/light.c +++ /dev/null @@ -1,45 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Lights. - */ - -#include "dimension-internal.h" -#include - -// Allocate a new dummy light -dmnsn_light * -dmnsn_new_light(dmnsn_pool *pool) -{ - dmnsn_light *light = DMNSN_PALLOC(pool, dmnsn_light); - dmnsn_init_light(light); - return light; -} - -// Initialize a light -void -dmnsn_init_light(dmnsn_light *light) -{ - light->direction_fn = NULL; - light->illumination_fn = NULL; - light->shadow_fn = NULL; -} diff --git a/libdimension/malloc.c b/libdimension/malloc.c deleted file mode 100644 index 2e4969d..0000000 --- a/libdimension/malloc.c +++ /dev/null @@ -1,94 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Dynamic memory. - */ - -#include "dimension-internal.h" -#include -#include -#include - -#if DMNSN_DEBUG -static atomic_size_t dmnsn_allocs = ATOMIC_VAR_INIT(0); -#endif - -void * -dmnsn_malloc(size_t size) -{ - void *ptr = malloc(size); - if (!ptr) { - dmnsn_error("Memory allocation failed."); - } - -#if DMNSN_DEBUG - atomic_fetch_add(&dmnsn_allocs, 1); -#endif - - return ptr; -} - -void * -dmnsn_realloc(void *ptr, size_t size) -{ -#if DMNSN_DEBUG - if (!ptr) { - atomic_fetch_add(&dmnsn_allocs, 1); - } -#endif - - ptr = realloc(ptr, size); - if (!ptr) { - dmnsn_error("Memory allocation failed."); - } - return ptr; -} - -char * -dmnsn_strdup(const char *s) -{ - char *copy = dmnsn_malloc(strlen(s) + 1); - strcpy(copy, s); - return copy; -} - -void -dmnsn_free(void *ptr) -{ -#if DMNSN_DEBUG - if (ptr) { - atomic_fetch_sub(&dmnsn_allocs, 1); - } -#endif - - free(ptr); -} - -#if DMNSN_DEBUG -DMNSN_LATE_DESTRUCTOR static void -dmnsn_leak_check(void) -{ - if (atomic_load_explicit(&dmnsn_allocs, memory_order_relaxed) > 0) { - dmnsn_warning("Leaking memory."); - } -} -#endif diff --git a/libdimension/map.c b/libdimension/map.c deleted file mode 100644 index f5454b1..0000000 --- a/libdimension/map.c +++ /dev/null @@ -1,125 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Generic maps. - */ - -#include "dimension-internal.h" - -/// dmnsn_map definition. -struct dmnsn_map { - size_t obj_size; ///< @internal The size of the mapped objects. - dmnsn_array *array; ///< @internal The map entries. -}; - -/// An [index, object] pair. -typedef struct dmnsn_map_entry { - double n; - char object[]; -} dmnsn_map_entry; - -dmnsn_map * -dmnsn_new_map(dmnsn_pool *pool, size_t size) -{ - dmnsn_map *map = DMNSN_PALLOC(pool, dmnsn_map); - map->obj_size = size; - map->array = dmnsn_palloc_array(pool, sizeof(dmnsn_map_entry) + size); - return map; -} - -void -dmnsn_map_add_entry(dmnsn_map *map, double n, const void *obj) -{ - dmnsn_map_entry *entry; - DMNSN_ALLOCA(entry, sizeof(dmnsn_map_entry) + map->obj_size); - - entry->n = n; - memcpy(entry->object, obj, map->obj_size); - - // Sorted insertion - size_t i; - for (i = dmnsn_array_size(map->array); i-- > 0;) { - dmnsn_map_entry *other = dmnsn_array_at(map->array, i); - if (other->n <= n) { - break; - } - } - - dmnsn_array_insert(map->array, i + 1, entry); -} - -size_t -dmnsn_map_size(const dmnsn_map *map) -{ - return dmnsn_array_size(map->array); -} - -void -dmnsn_map_evaluate(const dmnsn_map *map, double n, - double *val, void *obj1, void *obj2) -{ - dmnsn_assert(dmnsn_array_size(map->array) > 0, - "Attempt to evaluate empty map."); - - const dmnsn_map_entry *entry = dmnsn_array_first(map->array); - - double n1, n2 = 0.0; - const void *o1, *o2 = entry->object; - - if (n < n2) { - *val = 0.0; - memcpy(obj1, o2, map->obj_size); - memcpy(obj2, o2, map->obj_size); - return; - } - - ptrdiff_t skip = sizeof(dmnsn_map_entry) + map->obj_size; - for (const dmnsn_map_entry *last = dmnsn_array_last(map->array); - entry <= last; - entry = (const dmnsn_map_entry *)((const char *)entry + skip)) { - n1 = n2; - o1 = o2; - - n2 = entry->n; - o2 = entry->object; - - if (n < n2) { - *val = (n - n1)/(n2 - n1); - memcpy(obj1, o1, map->obj_size); - memcpy(obj2, o2, map->obj_size); - return; - } - } - - *val = 1.0; - memcpy(obj1, o2, map->obj_size); - memcpy(obj2, o2, map->obj_size); -} - -void -dmnsn_map_apply(dmnsn_map *map, dmnsn_callback_fn *callback) -{ - for (size_t i = 0; i < dmnsn_array_size(map->array); ++i) { - dmnsn_map_entry *entry = dmnsn_array_at(map->array, i); - callback(entry->object); - } -} diff --git a/libdimension/math/matrix.c b/libdimension/math/matrix.c new file mode 100644 index 0000000..25590d8 --- /dev/null +++ b/libdimension/math/matrix.c @@ -0,0 +1,388 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Matrix function implementations. + */ + +#include "internal.h" +#include "dimension/math.h" +#include + +// Identity matrix +dmnsn_matrix +dmnsn_identity_matrix(void) +{ + return dmnsn_new_matrix(1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0); +} + +// Scaling matrix +dmnsn_matrix +dmnsn_scale_matrix(dmnsn_vector s) +{ + return dmnsn_new_matrix(s.x, 0.0, 0.0, 0.0, + 0.0, s.y, 0.0, 0.0, + 0.0, 0.0, s.z, 0.0); +} + +// Translation matrix +dmnsn_matrix +dmnsn_translation_matrix(dmnsn_vector d) +{ + return dmnsn_new_matrix(1.0, 0.0, 0.0, d.x, + 0.0, 1.0, 0.0, d.y, + 0.0, 0.0, 1.0, d.z); +} + +// Left-handed rotation matrix; theta/|theta| = axis, |theta| = angle +dmnsn_matrix +dmnsn_rotation_matrix(dmnsn_vector theta) +{ + // Two trig calls, 25 multiplications, 13 additions + + double angle = dmnsn_vector_norm(theta); + if (fabs(angle) < dmnsn_epsilon) { + return dmnsn_identity_matrix(); + } + dmnsn_vector axis = dmnsn_vector_div(theta, angle); + + // Shorthand to make dmnsn_new_matrix() call legible + + double s = sin(angle); + double t = 1.0 - cos(angle); + + double x = axis.x; + double y = axis.y; + double z = axis.z; + + return dmnsn_new_matrix( + 1.0 + t*(x*x - 1.0), -z*s + t*x*y, y*s + t*x*z, 0.0, + z*s + t*x*y, 1.0 + t*(y*y - 1.0), -x*s + t*y*z, 0.0, + -y*s + t*x*z, x*s + t*y*z, 1.0 + t*(z*z - 1.0), 0.0 + ); +} + +// Find the angle between two vectors with respect to an axis +static double +dmnsn_axis_angle(dmnsn_vector from, dmnsn_vector to, dmnsn_vector axis) +{ + from = dmnsn_vector_sub(from, dmnsn_vector_proj(from, axis)); + to = dmnsn_vector_sub(to, dmnsn_vector_proj(to, axis)); + + double fromnorm = dmnsn_vector_norm(from); + double tonorm = dmnsn_vector_norm(to); + if (fromnorm < dmnsn_epsilon || tonorm < dmnsn_epsilon) { + return 0.0; + } + + from = dmnsn_vector_div(from, fromnorm); + to = dmnsn_vector_div(to, tonorm); + + double angle = acos(dmnsn_vector_dot(from, to)); + + if (dmnsn_vector_dot(dmnsn_vector_cross(from, to), axis) > 0.0) { + return angle; + } else { + return -angle; + } +} + +// Alignment matrix +dmnsn_matrix +dmnsn_alignment_matrix(dmnsn_vector from, dmnsn_vector to, + dmnsn_vector axis1, dmnsn_vector axis2) +{ + double theta1 = dmnsn_axis_angle(from, to, axis1); + dmnsn_matrix align1 = dmnsn_rotation_matrix(dmnsn_vector_mul(theta1, axis1)); + from = dmnsn_transform_direction(align1, from); + axis2 = dmnsn_transform_direction(align1, axis2); + + double theta2 = dmnsn_axis_angle(from, to, axis2); + dmnsn_matrix align2 = dmnsn_rotation_matrix(dmnsn_vector_mul(theta2, axis2)); + + return dmnsn_matrix_mul(align2, align1); +} + +// Matrix inversion helper functions + +/// A 2x2 matrix for inversion by partitioning. +typedef struct { double n[2][2]; } dmnsn_matrix2; + +/// Construct a 2x2 matrix. +static dmnsn_matrix2 dmnsn_new_matrix2(double a1, double a2, + double b1, double b2); +/// Invert a 2x2 matrix. +static dmnsn_matrix2 dmnsn_matrix2_inverse(dmnsn_matrix2 A); +/// Negate a 2x2 matrix. +static dmnsn_matrix2 dmnsn_matrix2_negate(dmnsn_matrix2 A); +/// Subtract two 2x2 matricies. +static dmnsn_matrix2 dmnsn_matrix2_sub(dmnsn_matrix2 lhs, dmnsn_matrix2 rhs); +/// Add two 2x2 matricies. +static dmnsn_matrix2 dmnsn_matrix2_mul(dmnsn_matrix2 lhs, dmnsn_matrix2 rhs); + +/// Invert a matrix with the slower cofactor algorithm, if partitioning failed. +static dmnsn_matrix dmnsn_matrix_inverse_generic(dmnsn_matrix A); +/// Get the [\p row, \p col] cofactor of A. +static double dmnsn_matrix_cofactor(dmnsn_matrix A, size_t row, size_t col); + +// Invert a matrix, by partitioning +dmnsn_matrix +dmnsn_matrix_inverse(dmnsn_matrix A) +{ + // Use partitioning to invert a matrix: + // + // [ P Q ] -1 + // [ R S ] + // + // = [ PP QQ ] + // [ RR SS ], + // + // with PP = inv(P) - inv(P)*Q*RR, + // QQ = -inv(P)*Q*SS, + // RR = -SS*R*inv(P), and + // SS = inv(S - R*inv(P)*Q). + + // The algorithm uses 2 inversions, 6 multiplications, and 2 subtractions, + // giving 52 multiplications, 34 additions, and 8 divisions. + + dmnsn_matrix2 P, Q, R, S, Pi, RPi, PiQ, RPiQ, PP, QQ, RR, SS; + double Pdet = A.n[0][0]*A.n[1][1] - A.n[0][1]*A.n[1][0]; + + if (dmnsn_unlikely(fabs(Pdet) < dmnsn_epsilon)) { + // If P is close to singular, try a more generic algorithm; this is very + // unlikely, but not impossible, eg. + // [ 1 1 0 0 ] + // [ 1 1 1 0 ] + // [ 0 1 1 0 ] + // [ 0 0 0 1 ] + return dmnsn_matrix_inverse_generic(A); + } + + // Partition the matrix + P = dmnsn_new_matrix2(A.n[0][0], A.n[0][1], + A.n[1][0], A.n[1][1]); + Q = dmnsn_new_matrix2(A.n[0][2], A.n[0][3], + A.n[1][2], A.n[1][3]); + R = dmnsn_new_matrix2(A.n[2][0], A.n[2][1], + 0.0, 0.0); + S = dmnsn_new_matrix2(A.n[2][2], A.n[2][3], + 0.0, 1.0); + + // Do this inversion ourselves, since we already have the determinant + Pi = dmnsn_new_matrix2( P.n[1][1]/Pdet, -P.n[0][1]/Pdet, + -P.n[1][0]/Pdet, P.n[0][0]/Pdet); + + // Calculate R*inv(P), inv(P)*Q, and R*inv(P)*Q + RPi = dmnsn_matrix2_mul(R, Pi); + PiQ = dmnsn_matrix2_mul(Pi, Q); + RPiQ = dmnsn_matrix2_mul(R, PiQ); + + // Calculate the partitioned inverse + SS = dmnsn_matrix2_inverse(dmnsn_matrix2_sub(S, RPiQ)); + RR = dmnsn_matrix2_negate(dmnsn_matrix2_mul(SS, RPi)); + QQ = dmnsn_matrix2_negate(dmnsn_matrix2_mul(PiQ, SS)); + PP = dmnsn_matrix2_sub(Pi, dmnsn_matrix2_mul(PiQ, RR)); + + // Reconstruct the matrix + return dmnsn_new_matrix(PP.n[0][0], PP.n[0][1], QQ.n[0][0], QQ.n[0][1], + PP.n[1][0], PP.n[1][1], QQ.n[1][0], QQ.n[1][1], + RR.n[0][0], RR.n[0][1], SS.n[0][0], SS.n[0][1]); +} + +// For nice shorthand +static dmnsn_matrix2 +dmnsn_new_matrix2(double a1, double a2, double b1, double b2) +{ + dmnsn_matrix2 m = { { { a1, a2 }, + { b1, b2 } } }; + return m; +} + +// Invert a 2x2 matrix +static dmnsn_matrix2 +dmnsn_matrix2_inverse(dmnsn_matrix2 A) +{ + // 4 divisions, 2 multiplications, 1 addition + double det = A.n[0][0]*A.n[1][1] - A.n[0][1]*A.n[1][0]; + return dmnsn_new_matrix2( A.n[1][1]/det, -A.n[0][1]/det, + -A.n[1][0]/det, A.n[0][0]/det); +} + +// Also basically a shorthand +static dmnsn_matrix2 +dmnsn_matrix2_negate(dmnsn_matrix2 A) +{ + return dmnsn_new_matrix2(-A.n[0][0], -A.n[0][1], + -A.n[1][0], -A.n[1][1]); +} + +// 2x2 matrix subtraction +static dmnsn_matrix2 +dmnsn_matrix2_sub(dmnsn_matrix2 lhs, dmnsn_matrix2 rhs) +{ + // 4 additions + return dmnsn_new_matrix2( + lhs.n[0][0] - rhs.n[0][0], lhs.n[0][1] - rhs.n[0][1], + lhs.n[1][0] - rhs.n[1][0], lhs.n[1][1] - rhs.n[1][1] + ); +} + +// 2x2 matrix multiplication +static dmnsn_matrix2 +dmnsn_matrix2_mul(dmnsn_matrix2 lhs, dmnsn_matrix2 rhs) +{ + // 8 multiplications, 4 additions + return dmnsn_new_matrix2( + lhs.n[0][0]*rhs.n[0][0] + lhs.n[0][1]*rhs.n[1][0], + lhs.n[0][0]*rhs.n[0][1] + lhs.n[0][1]*rhs.n[1][1], + lhs.n[1][0]*rhs.n[0][0] + lhs.n[1][1]*rhs.n[1][0], + lhs.n[1][0]*rhs.n[0][1] + lhs.n[1][1]*rhs.n[1][1] + ); +} + +// Invert a matrix, if partitioning failed (|P| == 0) +static dmnsn_matrix +dmnsn_matrix_inverse_generic(dmnsn_matrix A) +{ + // For A = [ A' b ] A^-1 = [ A'^-1 -(A'^-1)*b ] + // [ 0 ... 0 1 ], [ 0 ... 0 1 ]. + // + // Invert A' by calculating its adjucate. + dmnsn_matrix inv; + double det = 0.0, C; + + // Perform a Laplace expansion along the first row to give us the adjugate's + // first column and the determinant + for (size_t j = 0; j < 3; ++j) { + C = dmnsn_matrix_cofactor(A, 0, j); + det += A.n[0][j]*C; + inv.n[j][0] = C; + } + + // Divide the first column by the determinant + for (size_t j = 0; j < 3; ++j) { + inv.n[j][0] /= det; + } + + // Find the rest of A' + for (size_t j = 0; j < 3; ++j) { + for (size_t i = 1; i < 3; ++i) { + inv.n[j][i] = dmnsn_matrix_cofactor(A, i, j)/det; + } + inv.n[j][3] = 0.0; + } + + // Find the translational component of the inverse + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + inv.n[i][3] -= inv.n[i][j]*A.n[j][3]; + } + } + + return inv; +} + +// Gives the cofactor at row, col; the determinant of the matrix formed from the +// upper-left 3x3 corner of A by ignoring row `row' and column `col', +// times (-1)^(row + col) +static double +dmnsn_matrix_cofactor(dmnsn_matrix A, size_t row, size_t col) +{ + // 2 multiplications, 1 addition + double n[4]; + size_t k = 0; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + if (i != row && j != col) { + n[k] = A.n[i][j]; + ++k; + } + } + } + + double C = n[0]*n[3] - n[1]*n[2]; + if ((row + col)%2 == 0) { + return C; + } else { + return -C; + } +} + +// 4x4 matrix multiplication +dmnsn_matrix +dmnsn_matrix_mul(dmnsn_matrix lhs, dmnsn_matrix rhs) +{ + // 36 multiplications, 27 additions + dmnsn_matrix r; + + r.n[0][0] = lhs.n[0][0]*rhs.n[0][0] + lhs.n[0][1]*rhs.n[1][0] + lhs.n[0][2]*rhs.n[2][0]; + r.n[0][1] = lhs.n[0][0]*rhs.n[0][1] + lhs.n[0][1]*rhs.n[1][1] + lhs.n[0][2]*rhs.n[2][1]; + r.n[0][2] = lhs.n[0][0]*rhs.n[0][2] + lhs.n[0][1]*rhs.n[1][2] + lhs.n[0][2]*rhs.n[2][2]; + r.n[0][3] = lhs.n[0][0]*rhs.n[0][3] + lhs.n[0][1]*rhs.n[1][3] + lhs.n[0][2]*rhs.n[2][3] + lhs.n[0][3]; + + r.n[1][0] = lhs.n[1][0]*rhs.n[0][0] + lhs.n[1][1]*rhs.n[1][0] + lhs.n[1][2]*rhs.n[2][0]; + r.n[1][1] = lhs.n[1][0]*rhs.n[0][1] + lhs.n[1][1]*rhs.n[1][1] + lhs.n[1][2]*rhs.n[2][1]; + r.n[1][2] = lhs.n[1][0]*rhs.n[0][2] + lhs.n[1][1]*rhs.n[1][2] + lhs.n[1][2]*rhs.n[2][2]; + r.n[1][3] = lhs.n[1][0]*rhs.n[0][3] + lhs.n[1][1]*rhs.n[1][3] + lhs.n[1][2]*rhs.n[2][3] + lhs.n[1][3]; + + r.n[2][0] = lhs.n[2][0]*rhs.n[0][0] + lhs.n[2][1]*rhs.n[1][0] + lhs.n[2][2]*rhs.n[2][0]; + r.n[2][1] = lhs.n[2][0]*rhs.n[0][1] + lhs.n[2][1]*rhs.n[1][1] + lhs.n[2][2]*rhs.n[2][1]; + r.n[2][2] = lhs.n[2][0]*rhs.n[0][2] + lhs.n[2][1]*rhs.n[1][2] + lhs.n[2][2]*rhs.n[2][2]; + r.n[2][3] = lhs.n[2][0]*rhs.n[0][3] + lhs.n[2][1]*rhs.n[1][3] + lhs.n[2][2]*rhs.n[2][3] + lhs.n[2][3]; + + return r; +} + +// Give an axis-aligned box that contains the given box transformed by `lhs' +dmnsn_aabb +dmnsn_transform_aabb(dmnsn_matrix trans, dmnsn_aabb box) +{ + // Infinite/zero bounding box support + if (isinf(box.min.x)) { + return box; + } + + // Taking the "absolute value" of the matrix saves some min/max calculations + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + trans.n[i][j] = fabs(trans.n[i][j]); + } + } + + dmnsn_vector Mt = dmnsn_matrix_column(trans, 3); + dmnsn_aabb ret = { Mt, Mt }; + + dmnsn_vector Mz = dmnsn_matrix_column(trans, 2); + ret.min = dmnsn_vector_add(ret.min, dmnsn_vector_mul(box.min.z, Mz)); + ret.max = dmnsn_vector_add(ret.max, dmnsn_vector_mul(box.max.z, Mz)); + + dmnsn_vector My = dmnsn_matrix_column(trans, 1); + ret.min = dmnsn_vector_add(ret.min, dmnsn_vector_mul(box.min.y, My)); + ret.max = dmnsn_vector_add(ret.max, dmnsn_vector_mul(box.max.y, My)); + + dmnsn_vector Mx = dmnsn_matrix_column(trans, 0); + ret.min = dmnsn_vector_add(ret.min, dmnsn_vector_mul(box.min.x, Mx)); + ret.max = dmnsn_vector_add(ret.max, dmnsn_vector_mul(box.max.x, Mx)); + + return ret; +} diff --git a/libdimension/math/polynomial.c b/libdimension/math/polynomial.c new file mode 100644 index 0000000..09e9603 --- /dev/null +++ b/libdimension/math/polynomial.c @@ -0,0 +1,443 @@ +/************************************************************************* + * Copyright (C) 2010-2011 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Real root isolation algorithm based on work by Vincent, Uspensky, Collins and + * Akritas, Johnson, Krandick, and Rouillier and Zimmerman. + */ + +#include "internal.h" +#include "internal/polynomial.h" +#include "dimension/math.h" +#include + +/// Get the real degree of a polynomial, ignoring leading zeros. +static inline size_t +dmnsn_real_degree(const double poly[], size_t degree) +{ + for (size_t i = degree + 1; i-- > 0;) { + if (dmnsn_likely(fabs(poly[i]) >= dmnsn_epsilon)) { + return i; + } + } + + return 0; +} + +/// Divide each coefficient by the leading coefficient. +static inline void +dmnsn_polynomial_normalize(double poly[], size_t degree) +{ + for (size_t i = 0; i < degree; ++i) { + poly[i] /= poly[degree]; + } + poly[degree] = 1.0; +} + +/// Eliminate trivial zero roots from \p poly[]. +static inline void +dmnsn_eliminate_zero_roots(double **poly, size_t *degree) +{ + size_t i; + for (i = 0; i <= *degree; ++i) { + if (dmnsn_likely(fabs((*poly)[i]) >= dmnsn_epsilon)) { + break; + } + } + + *poly += i; + *degree -= i; +} + +/// Calculate a finite upper bound on the roots of a normalized polynomial. +static inline double +dmnsn_root_bound(const double poly[], size_t degree) +{ + double bound = fabs(poly[0]); + for (size_t i = 1; i < degree; ++i) { + bound = dmnsn_max(bound, fabs(poly[i])); + } + bound += 1.0; + return bound; +} + +/// Copy a polynomial. +static inline void +dmnsn_polynomial_copy(double dest[], const double src[], size_t degree) +{ + for (size_t i = 0; i <= degree; ++i) { + dest[i] = src[i]; + } +} + +/// Transform a polynomial by P'(x) = P(x + 1). +static inline void +dmnsn_polynomial_translate(double poly[], size_t degree) +{ + for (size_t i = 0; i <= degree; ++i) { + for (size_t j = degree - i; j <= degree - 1; ++j) { + poly[j] += poly[j + 1]; + } + } +} + +/// Transform a polynomial by P'(x) = P(c*x). +static inline void +dmnsn_polynomial_scale(double poly[], size_t degree, double c) +{ + double factor = c; + for (size_t i = 1; i <= degree; ++i) { + poly[i] *= factor; + factor *= c; + } +} + +/// Returns the result of Descartes' rule on x^degree * poly(1/(x + 1)). +static size_t +dmnsn_descartes_bound(const double poly[], size_t degree) +{ + // Copy the polynomial so we can be destructive + double p[degree + 1]; + dmnsn_polynomial_copy(p, poly, degree); + + // Calculate poly(1/(1/x + 1)) which avoids reversal + for (size_t i = 1; i <= degree; ++i) { + for (size_t j = i; j >= 1; --j) { + p[j] += p[j - 1]; + } + } + + // Find the number of sign changes in p[] + size_t changes = 0; + int lastsign = dmnsn_sgn(p[0]); + for (size_t i = 1; changes <= 1 && i <= degree; ++i) { + int sign = dmnsn_sgn(p[i]); + if (sign != 0 && sign != lastsign) { + ++changes; + lastsign = sign; + } + } + + return changes; +} + +/// Depth-first search of possible isolating intervals. +static size_t +dmnsn_root_bounds_recursive(double poly[], size_t degree, double *c, double *k, + double bounds[][2], size_t nbounds) +{ + size_t s = dmnsn_descartes_bound(poly, degree); + if (s >= 2) { + // Get the left child + dmnsn_polynomial_scale(poly, degree, 1.0/2.0); + *c *= 2.0; + *k /= 2.0; + double currc = *c, currk = *k; + + // Test the left child + size_t n = dmnsn_root_bounds_recursive(poly, degree, c, k, bounds, nbounds); + if (nbounds == n) { + return n; + } + bounds += n; + nbounds -= n; + + // Get the right child from the last tested polynomial + dmnsn_polynomial_translate(poly, degree); + dmnsn_polynomial_scale(poly, degree, currk/(*k)); + *c = currc + 1.0; + *k = currk; + + // Test the right child + n += dmnsn_root_bounds_recursive(poly, degree, c, k, bounds, nbounds); + return n; + } else if (s == 1) { + bounds[0][0] = (*c)*(*k); + bounds[0][1] = (*c + 1.0)*(*k); + return 1; + } else { + return 0; + } +} + +/// Find ranges that contain a single root. +static size_t +dmnsn_root_bounds(const double poly[], size_t degree, double bounds[][2], + size_t nbounds) +{ + // Copy the polynomial so we can be destructive + double p[degree + 1]; + dmnsn_polynomial_copy(p, poly, degree); + + // Scale the roots to within (0, 1] + double bound = dmnsn_root_bound(p, degree); + dmnsn_polynomial_scale(p, degree, bound); + + // Bounding intervals are of the form (c*k, (c + 1)*k) + double c = 0.0, k = 1.0; + + // Isolate the roots + size_t n = dmnsn_root_bounds_recursive(p, degree, &c, &k, bounds, nbounds); + + // Scale the roots back to within (0, bound] + for (size_t i = 0; i < n; ++i) { + bounds[i][0] *= bound; + bounds[i][1] *= bound; + } + + return n; +} + +/// Maximum number of iterations in dmnsn_bisect_root() before bailout. +#define DMNSN_BISECT_ITERATIONS 64 + +/// Use the false position method to find a root in a range that contains +/// exactly one root. +static inline double +dmnsn_bisect_root(const double poly[], size_t degree, double min, double max) +{ + double evmin = dmnsn_polynomial_evaluate(poly, degree, min); + double evmax = dmnsn_polynomial_evaluate(poly, degree, max); + + // Handle equal bounds, and equal values at the bounds. + if (dmnsn_unlikely(fabs(evmax - evmin) < dmnsn_epsilon)) { + return (min + max)/2.0; + } + + double evinitial = dmnsn_min(fabs(evmin), fabs(evmax)); + double mid, evmid; + int lastsign = 0; + + for (size_t i = 0; i < DMNSN_BISECT_ITERATIONS; ++i) { + mid = (min*evmax - max*evmin)/(evmax - evmin); + evmid = dmnsn_polynomial_evaluate(poly, degree, mid); + int sign = dmnsn_sgn(evmid); + + if ((fabs(evmid) < fabs(mid)*dmnsn_epsilon + // This condition improves stability when one of the bounds is close to + // a different root than we are trying to find + && fabs(evmid) <= evinitial) + || max - min < fabs(mid)*dmnsn_epsilon) + { + break; + } + + if (mid < min) { + // This can happen due to numerical instability in the root bounding + // algorithm, so behave like the normal secant method + max = min; + evmax = evmin; + min = mid; + evmin = evmid; + } else if (mid > max) { + min = max; + evmin = evmax; + max = mid; + evmax = evmid; + } else if (sign == dmnsn_sgn(evmax)) { + max = mid; + evmax = evmid; + if (sign == lastsign) { + // Don't allow the algorithm to keep the same endpoint for three + // iterations in a row; this ensures superlinear convergence + evmin /= 2.0; + } + } else { + min = mid; + evmin = evmid; + if (sign == lastsign) { + evmax /= 2.0; + } + } + + lastsign = sign; + } + + return mid; +} + +/// Use synthetic division to eliminate the root \p r from \p poly[]. +static inline size_t +dmnsn_eliminate_root(double poly[], size_t degree, double r) +{ + double rem = poly[degree]; + for (size_t i = degree; i-- > 0;) { + double temp = poly[i]; + poly[i] = rem; + rem = temp + r*rem; + } + return degree - 1; +} + +/// Solve a normalized linear polynomial algebraically. +static inline size_t +dmnsn_solve_linear(const double poly[2], double x[1]) +{ + x[0] = -poly[0]; + if (x[0] >= dmnsn_epsilon) + return 1; + else + return 0; +} + +/// Solve a normalized quadratic polynomial algebraically. +static inline size_t +dmnsn_solve_quadratic(const double poly[3], double x[2]) +{ + double disc = poly[1]*poly[1] - 4.0*poly[0]; + if (disc >= 0.0) { + double s = sqrt(disc); + x[0] = (-poly[1] + s)/2.0; + x[1] = (-poly[1] - s)/2.0; + + if (x[1] >= dmnsn_epsilon) + return 2; + else if (x[0] >= dmnsn_epsilon) + return 1; + else + return 0; + } else { + return 0; + } +} + +/// Solve a normalized cubic polynomial algebraically. +static inline size_t +dmnsn_solve_cubic(double poly[4], double x[3]) +{ + // Reduce to a monic trinomial (t^3 + p*t + q, t = x + b/3) + double b2 = poly[2]*poly[2]; + double p = poly[1] - b2/3.0; + double q = poly[0] - poly[2]*(9.0*poly[1] - 2.0*b2)/27.0; + + double disc = 4.0*p*p*p + 27.0*q*q; + double bdiv3 = poly[2]/3.0; + + if (disc < 0.0) { + // Three real roots -- this implies p < 0 + double msqrtp3 = -sqrt(-p/3.0); + double theta = acos(3*q/(2*p*msqrtp3))/3.0; + + // Store the roots in order from largest to smallest + x[2] = 2.0*msqrtp3*cos(theta) - bdiv3; + x[0] = -2.0*msqrtp3*cos(4.0*atan(1.0)/3.0 - theta) - bdiv3; + x[1] = -(x[0] + x[2] + poly[2]); + + if (x[2] >= dmnsn_epsilon) + return 3; + else if (x[1] >= dmnsn_epsilon) + return 2; + } else if (disc > 0.0) { + // One real root + double cbrtdiscq = cbrt(sqrt(disc/108.0) + fabs(q)/2.0); + double abst = cbrtdiscq - p/(3.0*cbrtdiscq); + + if (q >= 0) { + x[0] = -abst - bdiv3; + } else { + x[0] = abst - bdiv3; + } + } else if (fabs(p) < dmnsn_epsilon) { + // Equation is a perfect cube + x[0] = -bdiv3; + } else { + // Two real roots; one duplicate + double t1 = -(3.0*q)/(2.0*p), t2 = -2.0*t1; + x[0] = dmnsn_max(t1, t2) - bdiv3; + x[1] = dmnsn_min(t1, t2) - bdiv3; + if (x[1] >= dmnsn_epsilon) + return 2; + } + + if (x[0] >= dmnsn_epsilon) + return 1; + else + return 0; +} + +// Solve a polynomial +DMNSN_HOT size_t +dmnsn_polynomial_solve(const double poly[], size_t degree, double x[]) +{ + // Copy the polynomial so we can be destructive + double copy[degree + 1], *p = copy; + dmnsn_polynomial_copy(p, poly, degree); + + // Index into x[] + size_t i = 0; + + // Account for leading zero coefficients + degree = dmnsn_real_degree(p, degree); + // Normalize the leading coefficient to 1.0 + dmnsn_polynomial_normalize(p, degree); + // Eliminate simple zero roots + dmnsn_eliminate_zero_roots(&p, °ree); + + static const size_t max_algebraic = 3; + if (degree > max_algebraic) { + // Find isolating intervals for (degree - max_algebraic) roots of p[] + double ranges[degree - max_algebraic][2]; + size_t n = dmnsn_root_bounds(p, degree, ranges, degree - max_algebraic); + + for (size_t j = 0; j < n; ++j) { + // Bisect within the found range + double r = dmnsn_bisect_root(p, degree, ranges[j][0], ranges[j][1]); + + // Use synthetic division to eliminate the root `r' + degree = dmnsn_eliminate_root(p, degree, r); + + // Store the found root + x[i] = r; + ++i; + } + } + + switch (degree) { + case 1: + i += dmnsn_solve_linear(p, x + i); + break; + case 2: + i += dmnsn_solve_quadratic(p, x + i); + break; + case 3: + i += dmnsn_solve_cubic(p, x + i); + break; + } + + return i; +} + +// Print a polynomial +void +dmnsn_polynomial_print(FILE *file, const double poly[], size_t degree) +{ + for (size_t i = degree + 1; i-- > 0;) { + if (i < degree) { + fprintf(file, (poly[i] >= 0.0) ? " + " : " - "); + } + fprintf(file, "%.17g", fabs(poly[i])); + if (i >= 2) { + fprintf(file, "*x^%zu", i); + } else if (i == 1) { + fprintf(file, "*x"); + } + } +} diff --git a/libdimension/model/camera.c b/libdimension/model/camera.c new file mode 100644 index 0000000..8d05c28 --- /dev/null +++ b/libdimension/model/camera.c @@ -0,0 +1,52 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Cameras. + */ + +#include "internal.h" +#include "dimension/model.h" +#include + +// Allocate a new dummy camera +dmnsn_camera * +dmnsn_new_camera(dmnsn_pool *pool) +{ + dmnsn_camera *camera = DMNSN_PALLOC(pool, dmnsn_camera); + dmnsn_init_camera(camera); + return camera; +} + +// Initialize a camera +void +dmnsn_init_camera(dmnsn_camera *camera) +{ + camera->trans = dmnsn_identity_matrix(); +} + +// Invoke the camera ray function +dmnsn_ray +dmnsn_camera_ray(const dmnsn_camera *camera, double x, double y) +{ + dmnsn_ray ray = camera->ray_fn(camera, x, y); + return dmnsn_transform_ray(camera->trans, ray); +} diff --git a/libdimension/model/cameras/perspective.c b/libdimension/model/cameras/perspective.c new file mode 100644 index 0000000..6f1a9fc --- /dev/null +++ b/libdimension/model/cameras/perspective.c @@ -0,0 +1,47 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Perspective cameras. + */ + +#include "dimension/model.h" +#include + +/// Perspective camera ray callback. +static dmnsn_ray +dmnsn_perspective_camera_ray_fn(const dmnsn_camera *camera, double x, double y) +{ + dmnsn_ray l = dmnsn_new_ray( + dmnsn_zero, + dmnsn_new_vector(x - 0.5, y - 0.5, 1.0) + ); + return l; +} + +// Create a new perspective camera. +dmnsn_camera * +dmnsn_new_perspective_camera(dmnsn_pool *pool) +{ + dmnsn_camera *camera = dmnsn_new_camera(pool); + camera->ray_fn = dmnsn_perspective_camera_ray_fn; + return camera; +} diff --git a/libdimension/model/finish.c b/libdimension/model/finish.c new file mode 100644 index 0000000..a5dbaf9 --- /dev/null +++ b/libdimension/model/finish.c @@ -0,0 +1,103 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Finishes. + */ + +#include "internal.h" +#include "dimension/model.h" + +dmnsn_ambient * +dmnsn_new_ambient(dmnsn_pool *pool, dmnsn_color ambient_light) +{ + dmnsn_ambient *ambient = DMNSN_PALLOC(pool, dmnsn_ambient); + ambient->ambient = ambient_light; + return ambient; +} + +dmnsn_diffuse * +dmnsn_new_diffuse(dmnsn_pool *pool) +{ + dmnsn_diffuse *diffuse = DMNSN_PALLOC(pool, dmnsn_diffuse); + dmnsn_init_diffuse(diffuse); + return diffuse; +} + +void +dmnsn_init_diffuse(dmnsn_diffuse *diffuse) +{ +} + +dmnsn_specular * +dmnsn_new_specular(dmnsn_pool *pool) +{ + dmnsn_specular *specular = DMNSN_PALLOC(pool, dmnsn_specular); + dmnsn_init_specular(specular); + return specular; +} + +void +dmnsn_init_specular(dmnsn_specular *specular) +{ +} + +dmnsn_reflection * +dmnsn_new_reflection(dmnsn_pool *pool) +{ + dmnsn_reflection *reflection = DMNSN_PALLOC(pool, dmnsn_reflection); + dmnsn_init_reflection(reflection); + return reflection; +} + +void +dmnsn_init_reflection(dmnsn_reflection *reflection) +{ +} + +dmnsn_finish +dmnsn_new_finish(void) +{ + dmnsn_finish finish = { + .ambient = NULL, + .diffuse = NULL, + .specular = NULL, + .reflection = NULL, + }; + return finish; +} + +void +dmnsn_finish_cascade(const dmnsn_finish *default_finish, dmnsn_finish *finish) +{ + if (!finish->ambient) { + finish->ambient = default_finish->ambient; + } + if (!finish->diffuse) { + finish->diffuse = default_finish->diffuse; + } + if (!finish->specular) { + finish->specular = default_finish->specular; + } + if (!finish->reflection) { + finish->reflection = default_finish->reflection; + } +} diff --git a/libdimension/model/finishes/lambertian.c b/libdimension/model/finishes/lambertian.c new file mode 100644 index 0000000..9e041ed --- /dev/null +++ b/libdimension/model/finishes/lambertian.c @@ -0,0 +1,57 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Diffuse finish. + */ + +#include "dimension/model.h" +#include +#include + +/// Lambertian diffuse type. +typedef struct dmnsn_lambertian { + dmnsn_diffuse diffuse; + double coeff; +} dmnsn_lambertian; + +/// Diffuse finish callback. +static dmnsn_color +dmnsn_lambertian_diffuse_fn(const dmnsn_diffuse *diffuse, + dmnsn_color light, dmnsn_color color, + dmnsn_vector ray, dmnsn_vector normal) +{ + const dmnsn_lambertian *lambertian = (const dmnsn_lambertian *)diffuse; + double diffuse_factor = fabs((lambertian->coeff)*dmnsn_vector_dot(ray, normal)); + return dmnsn_color_mul(diffuse_factor, dmnsn_color_illuminate(light, color)); +} + +dmnsn_diffuse * +dmnsn_new_lambertian(dmnsn_pool *pool, double coeff) +{ + dmnsn_lambertian *lambertian = DMNSN_PALLOC(pool, dmnsn_lambertian); + lambertian->coeff = coeff; + + dmnsn_diffuse *diffuse = &lambertian->diffuse; + dmnsn_init_diffuse(diffuse); + diffuse->diffuse_fn = dmnsn_lambertian_diffuse_fn; + return diffuse; +} diff --git a/libdimension/model/finishes/phong.c b/libdimension/model/finishes/phong.c new file mode 100644 index 0000000..6b1b1e1 --- /dev/null +++ b/libdimension/model/finishes/phong.c @@ -0,0 +1,69 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Phong highlights. + */ + +#include "dimension/model.h" +#include + +/// Phone specular type. +typedef struct dmnsn_phong { + dmnsn_specular specular; + double coeff; + double exp; +} dmnsn_phong; + +/// Phong specular highlight callback. +static dmnsn_color +dmnsn_phong_specular_fn(const dmnsn_specular *specular, + dmnsn_color light, dmnsn_color color, + dmnsn_vector ray, dmnsn_vector normal, + dmnsn_vector viewer) +{ + const dmnsn_phong *phong = (const dmnsn_phong *)specular; + + dmnsn_vector proj = dmnsn_vector_mul(2*dmnsn_vector_dot(ray, normal), normal); + dmnsn_vector reflected = dmnsn_vector_sub(proj, ray); + + double specular_factor = dmnsn_vector_dot(reflected, viewer); + if (specular_factor < 0.0) { + return dmnsn_black; + } + + specular_factor = pow(specular_factor, phong->exp); + return dmnsn_color_mul(phong->coeff*specular_factor, light); +} + +// A phong finish +dmnsn_specular * +dmnsn_new_phong(dmnsn_pool *pool, double coeff, double exp) +{ + dmnsn_phong *phong = DMNSN_PALLOC(pool, dmnsn_phong); + phong->coeff = coeff; + phong->exp = exp; + + dmnsn_specular *specular = &phong->specular; + dmnsn_init_specular(specular); + specular->specular_fn = dmnsn_phong_specular_fn; + return specular; +} diff --git a/libdimension/model/finishes/reflection.c b/libdimension/model/finishes/reflection.c new file mode 100644 index 0000000..afaae8f --- /dev/null +++ b/libdimension/model/finishes/reflection.c @@ -0,0 +1,64 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Reflective finish. + */ + +#include "dimension/model.h" +#include +#include + +/// Basic reflective finish type. +typedef struct dmnsn_basic_reflection { + dmnsn_reflection reflection; + dmnsn_color min, max; + double falloff; +} dmnsn_basic_reflection; + +/// Reflective finish callback. +static dmnsn_color +dmnsn_basic_reflection_fn(const dmnsn_reflection *reflection, + dmnsn_color reflect, dmnsn_color color, + dmnsn_vector ray, dmnsn_vector normal) +{ + const dmnsn_basic_reflection *basic = (const dmnsn_basic_reflection *)reflection; + double coeff = pow(fabs(dmnsn_vector_dot(ray, normal)), basic->falloff); + + return dmnsn_color_illuminate( + dmnsn_color_gradient(basic->min, basic->max, coeff), + reflect + ); +} + +dmnsn_reflection * +dmnsn_new_basic_reflection(dmnsn_pool *pool, dmnsn_color min, dmnsn_color max, double falloff) +{ + dmnsn_basic_reflection *basic = DMNSN_PALLOC(pool, dmnsn_basic_reflection); + basic->min = min; + basic->max = max; + basic->falloff = falloff; + + dmnsn_reflection *reflection = &basic->reflection; + dmnsn_init_reflection(reflection); + reflection->reflection_fn = dmnsn_basic_reflection_fn; + return reflection; +} diff --git a/libdimension/model/interior.c b/libdimension/model/interior.c new file mode 100644 index 0000000..82db3ea --- /dev/null +++ b/libdimension/model/interior.c @@ -0,0 +1,47 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Interiors. + */ + +#include "internal.h" +#include "dimension/model.h" +#include + +// Allocate an interior +dmnsn_interior * +dmnsn_new_interior(dmnsn_pool *pool) +{ + dmnsn_interior *interior = DMNSN_PALLOC(pool, dmnsn_interior); + interior->ior = 1.0; + return interior; +} + +// Cascade an interior +void +dmnsn_interior_cascade(dmnsn_interior *default_interior, + dmnsn_interior **interiorp) +{ + if (!*interiorp) { + *interiorp = default_interior; + } +} diff --git a/libdimension/model/light.c b/libdimension/model/light.c new file mode 100644 index 0000000..6143b3c --- /dev/null +++ b/libdimension/model/light.c @@ -0,0 +1,46 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Lights. + */ + +#include "internal.h" +#include "dimension/model.h" +#include + +// Allocate a new dummy light +dmnsn_light * +dmnsn_new_light(dmnsn_pool *pool) +{ + dmnsn_light *light = DMNSN_PALLOC(pool, dmnsn_light); + dmnsn_init_light(light); + return light; +} + +// Initialize a light +void +dmnsn_init_light(dmnsn_light *light) +{ + light->direction_fn = NULL; + light->illumination_fn = NULL; + light->shadow_fn = NULL; +} diff --git a/libdimension/model/lights/point_light.c b/libdimension/model/lights/point_light.c new file mode 100644 index 0000000..08aae67 --- /dev/null +++ b/libdimension/model/lights/point_light.c @@ -0,0 +1,72 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Point lights. + */ + +#include "dimension/model.h" +#include + +/// Point light type. +typedef struct dmnsn_point_light { + dmnsn_light light; + dmnsn_vector origin; + dmnsn_color color; +} dmnsn_point_light; + +/// Point light direction callback. +static dmnsn_vector +dmnsn_point_light_direction_fn(const dmnsn_light *light, dmnsn_vector v) +{ + const dmnsn_point_light *point_light = (const dmnsn_point_light *)light; + return dmnsn_vector_sub(point_light->origin, v); +} + +/// Point light illumination callback. +static dmnsn_color +dmnsn_point_light_illumination_fn(const dmnsn_light *light, dmnsn_vector v) +{ + const dmnsn_point_light *point_light = (const dmnsn_point_light *)light; + return point_light->color; +} + +/// Point light illumination callback. +static bool +dmnsn_point_light_shadow_fn(const dmnsn_light *light, double t) +{ + return t < 1.0; +} + +dmnsn_light * +dmnsn_new_point_light(dmnsn_pool *pool, dmnsn_vector x0, dmnsn_color color) +{ + dmnsn_point_light *point_light = DMNSN_PALLOC(pool, dmnsn_point_light); + point_light->origin = x0; + point_light->color = color; + + dmnsn_light *light = &point_light->light; + dmnsn_init_light(light); + light->direction_fn = dmnsn_point_light_direction_fn; + light->illumination_fn = dmnsn_point_light_illumination_fn; + light->shadow_fn = dmnsn_point_light_shadow_fn; + return light; +} diff --git a/libdimension/model/object.c b/libdimension/model/object.c new file mode 100644 index 0000000..0473f54 --- /dev/null +++ b/libdimension/model/object.c @@ -0,0 +1,110 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Objects. + */ + +#include "internal.h" +#include "dimension/model.h" +#include + +// Allocate a dummy object +dmnsn_object * +dmnsn_new_object(dmnsn_pool *pool) +{ + dmnsn_object *object = DMNSN_PALLOC(pool, dmnsn_object); + dmnsn_init_object(object); + return object; +} + +// Initialize a dmnsn_object field +void +dmnsn_init_object(dmnsn_object *object) +{ + object->vtable = NULL; + object->texture = NULL; + object->interior = NULL; + object->trans = dmnsn_identity_matrix(); + object->intrinsic_trans = dmnsn_identity_matrix(); + object->children = NULL; + object->split_children = false; + object->precomputed = false; +} + +/// Recursively precompute objects. +static void +dmnsn_object_precompute_recursive(dmnsn_object *object, dmnsn_matrix pigment_trans) +{ + dmnsn_assert(!object->precomputed, "Object double-precomputed."); + object->precomputed = true; + + const dmnsn_object_vtable *vtable = object->vtable; + dmnsn_assert(vtable->intersection_fn, "Missing intersection function."); + dmnsn_assert(vtable->inside_fn, "Missing inside function."); + dmnsn_assert(vtable->bounding_fn || vtable->precompute_fn, "Missing bounding and precompute function."); + + // Initialize the texture + if (!object->texture->initialized) { + dmnsn_texture_initialize(object->texture); + } + + dmnsn_matrix total_trans = dmnsn_matrix_mul(object->trans, object->intrinsic_trans); + + // Precompute the object's children + if (object->children) { + DMNSN_ARRAY_FOREACH (dmnsn_object **, child, object->children) { + dmnsn_matrix saved_trans = (*child)->trans; + (*child)->trans = dmnsn_matrix_mul(total_trans, saved_trans); + + dmnsn_matrix child_pigment_trans; + if ((*child)->texture == NULL || (*child)->texture->pigment == NULL) { + // Don't transform cascaded pigments with the child object + child_pigment_trans = pigment_trans; + } else { + child_pigment_trans = dmnsn_matrix_inverse((*child)->trans); + } + + dmnsn_texture_cascade(object->texture, &(*child)->texture); + dmnsn_interior_cascade(object->interior, &(*child)->interior); + dmnsn_object_precompute_recursive(*child, child_pigment_trans); + (*child)->trans = saved_trans; + } + } + + // Precalculate object values + object->pigment_trans = pigment_trans; + object->trans_inv = dmnsn_matrix_inverse(total_trans); + if (vtable->bounding_fn) { + object->aabb = vtable->bounding_fn(object, total_trans); + } + if (vtable->precompute_fn) { + vtable->precompute_fn(object); + } +} + +// Precompute object properties +void +dmnsn_object_precompute(dmnsn_object *object) +{ + dmnsn_matrix pigment_trans = dmnsn_matrix_inverse(object->trans); + dmnsn_object_precompute_recursive(object, pigment_trans); +} diff --git a/libdimension/model/objects/cone.c b/libdimension/model/objects/cone.c new file mode 100644 index 0000000..26e59ca --- /dev/null +++ b/libdimension/model/objects/cone.c @@ -0,0 +1,207 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Cones/cylinders. + */ + +#include "internal/polynomial.h" +#include "dimension/model.h" +#include + +/// Cone type. +typedef struct dmnsn_cone { + dmnsn_object object; + double r1, r2; +} dmnsn_cone; + +/// Intersection callback for a cone. +static bool +dmnsn_cone_intersection_fn(const dmnsn_object *object, dmnsn_ray l, + dmnsn_intersection *intersection) +{ + const dmnsn_cone *cone = (const dmnsn_cone *)object; + double r1 = cone->r1, r2 = cone->r2; + + // Solve (x0 + nx*t)^2 + (z0 + nz*t)^2 == (((r2 - r1)*(y0 + ny*t) + r1 + r2)/2)^2 + double poly[3], x[2]; + poly[2] = l.n.x*l.n.x + l.n.z*l.n.z - l.n.y*l.n.y*(r2 - r1)*(r2 - r1)/4.0; + poly[1] = 2.0*(l.n.x*l.x0.x + l.n.z*l.x0.z) + - l.n.y*(r2 - r1)*(l.x0.y*(r2 - r1) + r2 + r1)/2.0; + poly[0] = l.x0.x*l.x0.x + l.x0.z*l.x0.z + - (l.x0.y*(r2 - r1) + r2 + r1)*(l.x0.y*(r2 - r1) + r2 + r1)/4.0; + + size_t n = dmnsn_polynomial_solve(poly, 2, x); + + if (n > 0) { + double t = x[0]; + dmnsn_vector p; + if (n == 2) { + t = dmnsn_min(t, x[1]); + p = dmnsn_ray_point(l, t); + + if (p.y <= -1.0 || p.y >= 1.0) { + t = dmnsn_max(x[0], x[1]); + p = dmnsn_ray_point(l, t); + } + } else { + p = dmnsn_ray_point(l, t); + } + + if (t >= 0.0 && p.y >= -1.0 && p.y <= 1.0) { + double r = ((r2 - r1)*p.y + r1 + r2)/2.0; + dmnsn_vector norm = dmnsn_new_vector(p.x, -r*(r2 - r1)/2.0, p.z); + intersection->t = t; + intersection->normal = norm; + return true; + } + } + + return false; +} + +/// Inside callback for a cone. +static bool +dmnsn_cone_inside_fn(const dmnsn_object *object, dmnsn_vector point) +{ + const dmnsn_cone *cone = (const dmnsn_cone *)object; + double r1 = cone->r1, r2 = cone->r2; + double r = (point.y*(r2 - r1) + r1 + r2)/2.0; + return point.x*point.x + point.z*point.z < r*r + && point.y > -1.0 && point.y < 1.0; +} + +/// Cone bounding callback. +static dmnsn_aabb +dmnsn_cone_bounding_fn(const dmnsn_object *object, dmnsn_matrix trans) +{ + const dmnsn_cone *cone = (const dmnsn_cone *)object; + + double rmax = dmnsn_max(cone->r1, cone->r2); + dmnsn_aabb box = dmnsn_symmetric_aabb(dmnsn_new_vector(rmax, 1.0, rmax)); + return dmnsn_transform_aabb(trans, box); +} + +/// Cone vtable. +static const dmnsn_object_vtable dmnsn_cone_vtable = { + .intersection_fn = dmnsn_cone_intersection_fn, + .inside_fn = dmnsn_cone_inside_fn, + .bounding_fn = dmnsn_cone_bounding_fn, +}; + +/// Cone cap type. +typedef struct dmnsn_cone_cap { + dmnsn_object object; + double r; +} dmnsn_cone_cap; + +/// Cone cap intersection function. +static bool +dmnsn_cone_cap_intersection_fn(const dmnsn_object *object, dmnsn_ray l, + dmnsn_intersection *intersection) +{ + if (l.n.y != 0.0) { + const dmnsn_cone_cap *cap = (const dmnsn_cone_cap *)object; + double r = cap->r; + double t = -l.x0.y/l.n.y; + dmnsn_vector p = dmnsn_ray_point(l, t); + if (t >= 0.0 && p.x*p.x + p.z*p.z <= r*r) { + intersection->t = t; + intersection->normal = dmnsn_new_vector(0.0, -1.0, 0.0); + return true; + } + } + + return false; +} + +/// Inside callback for a cone cap. +static bool +dmnsn_cone_cap_inside_fn(const dmnsn_object *object, dmnsn_vector point) +{ + return false; +} + +/// Cone cap bounding callback. +static dmnsn_aabb +dmnsn_cone_cap_bounding_fn(const dmnsn_object *object, dmnsn_matrix trans) +{ + const dmnsn_cone_cap *cap = (const dmnsn_cone_cap *)object; + dmnsn_aabb box = dmnsn_symmetric_aabb(dmnsn_new_vector(cap->r, 0.0, cap->r)); + return dmnsn_transform_aabb(trans, box); +} + +/// Cone cap vtable. +static const dmnsn_object_vtable dmnsn_cone_cap_vtable = { + .intersection_fn = dmnsn_cone_cap_intersection_fn, + .inside_fn = dmnsn_cone_cap_inside_fn, + .bounding_fn = dmnsn_cone_cap_bounding_fn, +}; + +/// Allocate a new cone cap. +dmnsn_object * +dmnsn_new_cone_cap(dmnsn_pool *pool, double r) +{ + dmnsn_cone_cap *cap = DMNSN_PALLOC(pool, dmnsn_cone_cap); + cap->r = r; + + dmnsn_object *object = &cap->object; + dmnsn_init_object(object); + object->vtable = &dmnsn_cone_cap_vtable; + return object; +} + +// Allocate a new cone object +dmnsn_object * +dmnsn_new_cone(dmnsn_pool *pool, double r1, double r2, bool open) +{ + dmnsn_cone *cone = DMNSN_PALLOC(pool, dmnsn_cone); + cone->r1 = r1; + cone->r2 = r2; + + dmnsn_object *object = &cone->object; + dmnsn_init_object(object); + object->vtable = &dmnsn_cone_vtable; + + if (open) { + return object; + } + + // Implement closed cones as a union with the caps + dmnsn_object *cap1 = dmnsn_new_cone_cap(pool, r1); + dmnsn_object *cap2 = dmnsn_new_cone_cap(pool, r2); + cap1->intrinsic_trans = dmnsn_translation_matrix( + dmnsn_new_vector(0.0, -1.0, 0.0) + ); + cap2->intrinsic_trans = dmnsn_translation_matrix( + dmnsn_new_vector(0.0, +1.0, 0.0) + ); + // Flip the normal around for the top cap + cap2->intrinsic_trans.n[1][1] = -1.0; + + dmnsn_array *withcaps = DMNSN_PALLOC_ARRAY(pool, dmnsn_object *); + dmnsn_array_push(withcaps, &cone); + dmnsn_array_push(withcaps, &cap1); + dmnsn_array_push(withcaps, &cap2); + dmnsn_object *cone_cap_union = dmnsn_new_csg_union(pool, withcaps); + + return cone_cap_union; +} diff --git a/libdimension/model/objects/csg.c b/libdimension/model/objects/csg.c new file mode 100644 index 0000000..15008c0 --- /dev/null +++ b/libdimension/model/objects/csg.c @@ -0,0 +1,334 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Constructive solid geometry. + */ + +#include "internal.h" +#include "internal/bvh.h" +#include "dimension/model.h" +#include + +//////////// +// Unions // +//////////// + +typedef struct { + dmnsn_object object; + dmnsn_bvh *bvh; +} dmnsn_csg_union; + +/// CSG union intersection callback. +static bool +dmnsn_csg_union_intersection_fn(const dmnsn_object *object, + dmnsn_ray ray, + dmnsn_intersection *intersection) +{ + const dmnsn_csg_union *csg = (const dmnsn_csg_union *)object; + return dmnsn_bvh_intersection(csg->bvh, ray, intersection, true); +} + +/// CSG union inside callback. +static bool +dmnsn_csg_union_inside_fn(const dmnsn_object *object, dmnsn_vector point) +{ + const dmnsn_csg_union *csg = (const dmnsn_csg_union *)object; + return dmnsn_bvh_inside(csg->bvh, point); +} + +/// CSG union precomputation callback. +static void +dmnsn_csg_union_precompute_fn(dmnsn_object *object) +{ + dmnsn_csg_union *csg = (dmnsn_csg_union *)object; + csg->object.trans_inv = dmnsn_identity_matrix(); + + dmnsn_bvh *bvh = dmnsn_new_bvh(csg->object.children, DMNSN_BVH_PRTREE); + csg->bvh = bvh; + csg->object.aabb = dmnsn_bvh_aabb(bvh); +} + +/// CSG union vtable. +static const dmnsn_object_vtable dmnsn_csg_union_vtable = { + .intersection_fn = dmnsn_csg_union_intersection_fn, + .inside_fn = dmnsn_csg_union_inside_fn, + .precompute_fn = dmnsn_csg_union_precompute_fn, +}; + +/// CSG union destruction callback. +static void +dmnsn_csg_union_cleanup(void *ptr) +{ + dmnsn_csg_union *csg = ptr; + dmnsn_delete_bvh(csg->bvh); +} + +// Bulk-load a union +dmnsn_object * +dmnsn_new_csg_union(dmnsn_pool *pool, dmnsn_array *objects) +{ + dmnsn_csg_union *csg = DMNSN_PALLOC_TIDY(pool, dmnsn_csg_union, dmnsn_csg_union_cleanup); + csg->bvh = NULL; + + dmnsn_object *object = &csg->object; + dmnsn_init_object(object); + + object->vtable = &dmnsn_csg_union_vtable; + object->children = objects; + object->split_children = true; + + return object; +} + +/** + * Generic CSG intersection callback. + * @param[in] csg The CSG object. + * @param[in] ray The intersection ray. + * @param[out] intersection The intersection data. + * @param[in] inside1 Whether the first object is allowed inside the + * second object. + * @param[in] inside2 Whether the second object is allowed inside the + * first object. + * @return Whether \p ray intersected \p csg. + */ +static bool +dmnsn_csg_intersection_fn(const dmnsn_object *csg, dmnsn_ray ray, + dmnsn_intersection *intersection, + bool inside1, bool inside2) +{ + const dmnsn_object *A = *(dmnsn_object **)dmnsn_array_first(csg->children); + const dmnsn_object *B = *(dmnsn_object **)dmnsn_array_last(csg->children); + + dmnsn_intersection i1, i2; + bool is_i1 = dmnsn_object_intersection(A, ray, &i1); + bool is_i2 = dmnsn_object_intersection(B, ray, &i2); + + double oldt = 0.0; + while (is_i1) { + i1.ray = ray; + i1.t += oldt; + oldt = i1.t + dmnsn_epsilon; + + dmnsn_vector point = dmnsn_ray_point(i1.ray, i1.t); + if (inside2 ^ dmnsn_object_inside(B, point)) { + dmnsn_ray newray = ray; + newray.x0 = dmnsn_ray_point(ray, i1.t); + newray = dmnsn_ray_add_epsilon(newray); + is_i1 = dmnsn_object_intersection(A, newray, &i1); + } else { + break; + } + } + + oldt = 0.0; + while (is_i2) { + i2.ray = ray; + i2.t += oldt; + oldt = i2.t + dmnsn_epsilon; + + dmnsn_vector point = dmnsn_ray_point(i2.ray, i2.t); + if (inside1 ^ dmnsn_object_inside(A, point)) { + dmnsn_ray newray = ray; + newray.x0 = dmnsn_ray_point(ray, i2.t); + newray = dmnsn_ray_add_epsilon(newray); + is_i2 = dmnsn_object_intersection(B, newray, &i2); + } else { + break; + } + } + + if (is_i1 && is_i2) { + if (i1.t < i2.t) { + *intersection = i1; + } else { + *intersection = i2; + } + } else if (is_i1) { + *intersection = i1; + } else if (is_i2) { + *intersection = i2; + } else { + return false; + } + + return true; +} + +/////////////////// +// Intersections // +/////////////////// + +/// CSG intersection intersection callback. +static bool +dmnsn_csg_intersection_intersection_fn(const dmnsn_object *csg, + dmnsn_ray ray, + dmnsn_intersection *intersection) +{ + return dmnsn_csg_intersection_fn(csg, ray, intersection, true, true); +} + +/// CSG intersection inside callback. +static bool +dmnsn_csg_intersection_inside_fn(const dmnsn_object *csg, dmnsn_vector point) +{ + const dmnsn_object *A = *(dmnsn_object **)dmnsn_array_first(csg->children); + const dmnsn_object *B = *(dmnsn_object **)dmnsn_array_last(csg->children); + return dmnsn_object_inside(A, point) && dmnsn_object_inside(B, point); +} + +/// CSG intersection precomputation callback. +static void +dmnsn_csg_intersection_precompute_fn(dmnsn_object *csg) +{ + dmnsn_object *A = *(dmnsn_object **)dmnsn_array_first(csg->children); + dmnsn_object *B = *(dmnsn_object **)dmnsn_array_last(csg->children); + + csg->trans_inv = dmnsn_identity_matrix(); + csg->aabb.min = dmnsn_vector_max(A->aabb.min, B->aabb.min); + csg->aabb.max = dmnsn_vector_min(A->aabb.max, B->aabb.max); +} + +/// CSG intersection vtable. +static const dmnsn_object_vtable dmnsn_csg_intersection_vtable = { + .intersection_fn = dmnsn_csg_intersection_intersection_fn, + .inside_fn = dmnsn_csg_intersection_inside_fn, + .precompute_fn = dmnsn_csg_intersection_precompute_fn, +}; + +dmnsn_object * +dmnsn_new_csg_intersection(dmnsn_pool *pool, dmnsn_object *A, dmnsn_object *B) +{ + dmnsn_object *csg = dmnsn_new_object(pool); + csg->vtable = &dmnsn_csg_intersection_vtable; + + csg->children = DMNSN_PALLOC_ARRAY(pool, dmnsn_object *); + dmnsn_array_push(csg->children, &A); + dmnsn_array_push(csg->children, &B); + + return csg; +} + +///////////////// +// Differences // +///////////////// + +/// CSG difference intersection callback. +static bool +dmnsn_csg_difference_intersection_fn(const dmnsn_object *csg, + dmnsn_ray ray, + dmnsn_intersection *intersection) +{ + return dmnsn_csg_intersection_fn(csg, ray, intersection, true, false); +} + +/// CSG difference inside callback. +static bool +dmnsn_csg_difference_inside_fn(const dmnsn_object *csg, dmnsn_vector point) +{ + const dmnsn_object *A = *(dmnsn_object **)dmnsn_array_first(csg->children); + const dmnsn_object *B = *(dmnsn_object **)dmnsn_array_last(csg->children); + return dmnsn_object_inside(A, point) && !dmnsn_object_inside(B, point); +} + +/// CSG difference precomputation callback. +static void +dmnsn_csg_difference_precompute_fn(dmnsn_object *csg) +{ + dmnsn_object *A = *(dmnsn_object **)dmnsn_array_first(csg->children); + + csg->trans_inv = dmnsn_identity_matrix(); + csg->aabb = A->aabb; +} + +/// CSG difference vtable. +static const dmnsn_object_vtable dmnsn_csg_difference_vtable = { + .intersection_fn = dmnsn_csg_difference_intersection_fn, + .inside_fn = dmnsn_csg_difference_inside_fn, + .precompute_fn = dmnsn_csg_difference_precompute_fn, +}; + +dmnsn_object * +dmnsn_new_csg_difference(dmnsn_pool *pool, dmnsn_object *A, dmnsn_object *B) +{ + dmnsn_object *csg = dmnsn_new_object(pool); + csg->vtable = &dmnsn_csg_difference_vtable; + + csg->children = DMNSN_PALLOC_ARRAY(pool, dmnsn_object *); + dmnsn_array_push(csg->children, &A); + dmnsn_array_push(csg->children, &B); + + return csg; +} + +//////////// +// Merges // +//////////// + +/// CSG merge intersection callback. +static bool +dmnsn_csg_merge_intersection_fn(const dmnsn_object *csg, + dmnsn_ray ray, + dmnsn_intersection *intersection) +{ + return dmnsn_csg_intersection_fn(csg, ray, intersection, false, false); +} + +/// CSG merge inside callback. +static bool +dmnsn_csg_merge_inside_fn(const dmnsn_object *csg, dmnsn_vector point) +{ + const dmnsn_object *A = *(dmnsn_object **)dmnsn_array_first(csg->children); + const dmnsn_object *B = *(dmnsn_object **)dmnsn_array_last(csg->children); + return dmnsn_object_inside(A, point) || dmnsn_object_inside(B, point); +} + +/// CSG merge precomputation callback. +static void +dmnsn_csg_merge_precompute_fn(dmnsn_object *csg) +{ + dmnsn_object *A = *(dmnsn_object **)dmnsn_array_first(csg->children); + dmnsn_object *B = *(dmnsn_object **)dmnsn_array_last(csg->children); + + csg->trans_inv = dmnsn_identity_matrix(); + csg->aabb.min = dmnsn_vector_min(A->aabb.min, B->aabb.min); + csg->aabb.max = dmnsn_vector_max(A->aabb.max, B->aabb.max); +} + +/// CSG merge vtable. +static const dmnsn_object_vtable dmnsn_csg_merge_vtable = { + .intersection_fn = dmnsn_csg_merge_intersection_fn, + .inside_fn = dmnsn_csg_merge_inside_fn, + .precompute_fn = dmnsn_csg_merge_precompute_fn, +}; + +dmnsn_object * +dmnsn_new_csg_merge(dmnsn_pool *pool, dmnsn_object *A, dmnsn_object *B) +{ + dmnsn_object *csg = dmnsn_new_object(pool); + csg->vtable = &dmnsn_csg_merge_vtable; + + csg->children = DMNSN_PALLOC_ARRAY(pool, dmnsn_object *); + dmnsn_array_push(csg->children, &A); + dmnsn_array_push(csg->children, &B); + + return csg; +} diff --git a/libdimension/model/objects/cube.c b/libdimension/model/objects/cube.c new file mode 100644 index 0000000..7d6fe0f --- /dev/null +++ b/libdimension/model/objects/cube.c @@ -0,0 +1,154 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Cubes. + */ + +#include "dimension/model.h" +#include + +/// Intersection callback for a cube. +static bool +dmnsn_cube_intersection_fn(const dmnsn_object *cube, dmnsn_ray ray, + dmnsn_intersection *intersection) +{ + // Clip the given ray against the X, Y, and Z slabs + + dmnsn_vector nmin, nmax; + double tmin, tmax; + + double tx1 = (-1.0 - ray.x0.x)/ray.n.x; + double tx2 = (+1.0 - ray.x0.x)/ray.n.x; + + if (tx1 < tx2) { + tmin = tx1; + tmax = tx2; + nmin = dmnsn_new_vector(-1.0, 0.0, 0.0); + nmax = dmnsn_new_vector(+1.0, 0.0, 0.0); + } else { + tmin = tx2; + tmax = tx1; + nmin = dmnsn_new_vector(+1.0, 0.0, 0.0); + nmax = dmnsn_new_vector(-1.0, 0.0, 0.0); + } + + if (tmin > tmax) + return false; + + double ty1 = (-1.0 - ray.x0.y)/ray.n.y; + double ty2 = (+1.0 - ray.x0.y)/ray.n.y; + + if (ty1 < ty2) { + if (ty1 > tmin) { + tmin = ty1; + nmin = dmnsn_new_vector(0.0, -1.0, 0.0); + } + if (ty2 < tmax) { + tmax = ty2; + nmax = dmnsn_new_vector(0.0, +1.0, 0.0); + } + } else { + if (ty2 > tmin) { + tmin = ty2; + nmin = dmnsn_new_vector(0.0, +1.0, 0.0); + } + if (ty1 < tmax) { + tmax = ty1; + nmax = dmnsn_new_vector(0.0, -1.0, 0.0); + } + } + + if (tmin > tmax) + return false; + + double tz1 = (-1.0 - ray.x0.z)/ray.n.z; + double tz2 = (+1.0 - ray.x0.z)/ray.n.z; + + if (tz1 < tz2) { + if (tz1 > tmin) { + tmin = tz1; + nmin = dmnsn_new_vector(0.0, 0.0, -1.0); + } + if (tz2 < tmax) { + tmax = tz2; + nmax = dmnsn_new_vector(0.0, 0.0, +1.0); + } + } else { + if (tz2 > tmin) { + tmin = tz2; + nmin = dmnsn_new_vector(0.0, 0.0, +1.0); + } + if (tz1 < tmax) { + tmax = tz1; + nmax = dmnsn_new_vector(0.0, 0.0, -1.0); + } + } + + if (tmin > tmax) + return false; + + if (tmin < 0.0) { + tmin = tmax; + nmin = nmax; + } + + if (tmin >= 0.0) { + intersection->t = tmin; + intersection->normal = nmin; + return true; + } else { + return false; + } +} + +/// Inside callback for a cube. +static bool +dmnsn_cube_inside_fn(const dmnsn_object *cube, dmnsn_vector point) +{ + return point.x > -1.0 && point.x < 1.0 + && point.y > -1.0 && point.y < 1.0 + && point.z > -1.0 && point.z < 1.0; +} + +/// Boundary callback for a cube. +static dmnsn_aabb +dmnsn_cube_bounding_fn(const dmnsn_object *object, dmnsn_matrix trans) +{ + dmnsn_aabb box = dmnsn_symmetric_aabb(dmnsn_new_vector(1.0, 1.0, 1.0)); + return dmnsn_transform_aabb(trans, box); +} + +/// Cube vtable. +static const dmnsn_object_vtable dmnsn_cube_vtable = { + .intersection_fn = dmnsn_cube_intersection_fn, + .inside_fn = dmnsn_cube_inside_fn, + .bounding_fn = dmnsn_cube_bounding_fn, +}; + +// Allocate a new cube object +dmnsn_object * +dmnsn_new_cube(dmnsn_pool *pool) +{ + dmnsn_object *cube = dmnsn_new_object(pool); + cube->vtable = &dmnsn_cube_vtable; + return cube; +} diff --git a/libdimension/model/objects/plane.c b/libdimension/model/objects/plane.c new file mode 100644 index 0000000..b34d8aa --- /dev/null +++ b/libdimension/model/objects/plane.c @@ -0,0 +1,88 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Planes. + */ + +#include "dimension/model.h" +#include +#include + +/// Plane type. +typedef struct { + dmnsn_object object; + dmnsn_vector normal; +} dmnsn_plane; + +/// Returns the closest intersection of `ray' with `plane'. +static bool +dmnsn_plane_intersection_fn(const dmnsn_object *object, dmnsn_ray ray, + dmnsn_intersection *intersection) +{ + const dmnsn_plane *plane = (const dmnsn_plane *)object; + dmnsn_vector normal = plane->normal; + + double den = dmnsn_vector_dot(ray.n, normal); + if (den != 0.0) { + double t = -dmnsn_vector_dot(ray.x0, normal)/den; + if (t >= 0.0) { + intersection->t = t; + intersection->normal = normal; + return true; + } + } + return false; +} + +/// Return whether a point is inside a plane. +static bool +dmnsn_plane_inside_fn(const dmnsn_object *object, dmnsn_vector point) +{ + const dmnsn_plane *plane = (const dmnsn_plane *)object; + return dmnsn_vector_dot(point, plane->normal) < 0.0; +} + +/// Plane bounding callback. +static dmnsn_aabb +dmnsn_plane_bounding_fn(const dmnsn_object *object, dmnsn_matrix trans) +{ + return dmnsn_infinite_aabb(); +} + +/// Plane vtable. +static const dmnsn_object_vtable dmnsn_plane_vtable = { + .intersection_fn = dmnsn_plane_intersection_fn, + .inside_fn = dmnsn_plane_inside_fn, + .bounding_fn = dmnsn_plane_bounding_fn, +}; + +dmnsn_object * +dmnsn_new_plane(dmnsn_pool *pool, dmnsn_vector normal) +{ + dmnsn_plane *plane = DMNSN_PALLOC(pool, dmnsn_plane); + plane->normal = normal; + + dmnsn_object *object = &plane->object; + dmnsn_init_object(object); + object->vtable = &dmnsn_plane_vtable; + return object; +} diff --git a/libdimension/model/objects/sphere.c b/libdimension/model/objects/sphere.c new file mode 100644 index 0000000..e1ca784 --- /dev/null +++ b/libdimension/model/objects/sphere.c @@ -0,0 +1,116 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Spheres. + */ + +#include "internal.h" +#include "internal/polynomial.h" +#include "dimension/model.h" + +/// Sphere intersection callback. +static bool +dmnsn_sphere_intersection_fn(const dmnsn_object *sphere, dmnsn_ray l, + dmnsn_intersection *intersection) +{ + // Solve (x0 + nx*t)^2 + (y0 + ny*t)^2 + (z0 + nz*t)^2 == 1 + double poly[3], x[2]; + poly[2] = dmnsn_vector_dot(l.n, l.n); + poly[1] = 2.0*dmnsn_vector_dot(l.n, l.x0); + poly[0] = dmnsn_vector_dot(l.x0, l.x0) - 1.0; + + size_t n = dmnsn_polynomial_solve(poly, 2, x); + if (n == 0) { + return false; + } + + double t = x[0]; + // Optimize for the case where we're outside the sphere + if (dmnsn_likely(n == 2)) { + t = dmnsn_min(t, x[1]); + } + + intersection->t = t; + intersection->normal = dmnsn_ray_point(l, t); + return true; +} + +/// Sphere inside callback. +static bool +dmnsn_sphere_inside_fn(const dmnsn_object *sphere, dmnsn_vector point) +{ + return point.x*point.x + point.y*point.y + point.z*point.z < 1.0; +} + +/// Helper for sphere bounding box calculation. +static inline double +dmnsn_implicit_dot(double row[4]) +{ + double ret = 0.0; + for (int i = 0; i < 3; ++i) { + ret += row[i]*row[i]; + } + return ret; +} + +/// Sphere bounding callback. +static dmnsn_aabb +dmnsn_sphere_bounding_fn(const dmnsn_object *object, dmnsn_matrix trans) +{ + // Get a tight bound using the quadric representation of a sphere. For + // details, see + // http://tavianator.com/2014/06/exact-bounding-boxes-for-spheres-ellipsoids + + dmnsn_aabb box; + + double cx = trans.n[0][3]; + double dx = sqrt(dmnsn_implicit_dot(trans.n[0])); + box.min.x = cx - dx; + box.max.x = cx + dx; + + double cy = trans.n[1][3]; + double dy = sqrt(dmnsn_implicit_dot(trans.n[1])); + box.min.y = cy - dy; + box.max.y = cy + dy; + + double cz = trans.n[2][3]; + double dz = sqrt(dmnsn_implicit_dot(trans.n[2])); + box.min.z = cz - dz; + box.max.z = cz + dz; + + return box; +} + +/// Sphere vtable. +static const dmnsn_object_vtable dmnsn_sphere_vtable = { + .intersection_fn = dmnsn_sphere_intersection_fn, + .inside_fn = dmnsn_sphere_inside_fn, + .bounding_fn = dmnsn_sphere_bounding_fn, +}; + +dmnsn_object * +dmnsn_new_sphere(dmnsn_pool *pool) +{ + dmnsn_object *sphere = dmnsn_new_object(pool); + sphere->vtable = &dmnsn_sphere_vtable; + return sphere; +} diff --git a/libdimension/model/objects/torus.c b/libdimension/model/objects/torus.c new file mode 100644 index 0000000..b4baebd --- /dev/null +++ b/libdimension/model/objects/torus.c @@ -0,0 +1,173 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Torii. A special case of a quartic. + */ + +#include "internal/polynomial.h" +#include "dimension/model.h" + +/// Torus type. +typedef struct { + dmnsn_object object; + double major, minor; +} dmnsn_torus; + +/// Bound the torus in a cylindrical shell. +static inline bool +dmnsn_torus_bound_intersection(const dmnsn_torus *torus, dmnsn_ray l) +{ + double R = torus->major, r = torus->minor; + double rmax = R + r, rmin = R - r; + double rmax2 = rmax*rmax, rmin2 = rmin*rmin; + + // Try the caps first + double tlower = (-r - l.x0.y)/l.n.y; + double tupper = (+r - l.x0.y)/l.n.y; + dmnsn_vector lower = dmnsn_ray_point(l, tlower); + dmnsn_vector upper = dmnsn_ray_point(l, tupper); + double ldist2 = lower.x*lower.x + lower.z*lower.z; + double udist2 = upper.x*upper.x + upper.z*upper.z; + if ((ldist2 < rmin2 || ldist2 > rmax2) && (udist2 < rmin2 || udist2 > rmax2)) { + // No valid intersection with the caps, try the cylinder walls + double dist2 = l.x0.x*l.x0.x + l.x0.z*l.x0.z; + double bigcyl[3], smallcyl[3]; + bigcyl[2] = smallcyl[2] = l.n.x*l.n.x + l.n.z*l.n.z; + bigcyl[1] = smallcyl[1] = 2.0*(l.n.x*l.x0.x + l.n.z*l.x0.z); + bigcyl[0] = dist2 - rmax2; + smallcyl[0] = dist2 - rmin2; + + double x[4]; + size_t n = dmnsn_polynomial_solve(bigcyl, 2, x); + n += dmnsn_polynomial_solve(smallcyl, 2, x + n); + + size_t i; + for (i = 0; i < n; ++i) { + dmnsn_vector p = dmnsn_ray_point(l, x[i]); + if (p.y >= -r && p.y <= r) + break; + } + + if (i == n) { + // No valid intersection found + return false; + } + } + + return true; +} + +/// Torus intersection callback. +static bool +dmnsn_torus_intersection_fn(const dmnsn_object *object, dmnsn_ray l, + dmnsn_intersection *intersection) +{ + const dmnsn_torus *torus = (const dmnsn_torus *)object; + double R = torus->major, r = torus->minor; + double RR = R*R, rr = r*r; + + if (!dmnsn_torus_bound_intersection(torus, l)) { + return false; + } + + // This bit of algebra here is correct + dmnsn_vector x0mod = dmnsn_new_vector(l.x0.x, -l.x0.y, l.x0.z); + dmnsn_vector nmod = dmnsn_new_vector(l.n.x, -l.n.y, l.n.z); + double nn = dmnsn_vector_dot(l.n, l.n); + double nx0 = dmnsn_vector_dot(l.n, l.x0); + double x0x0 = dmnsn_vector_dot(l.x0, l.x0); + double x0x0mod = dmnsn_vector_dot(l.x0, x0mod); + double nx0mod = dmnsn_vector_dot(l.n, x0mod); + double nnmod = dmnsn_vector_dot(l.n, nmod); + + double poly[5]; + poly[4] = nn*nn; + poly[3] = 4*nn*nx0; + poly[2] = 2.0*(nn*(x0x0 - rr) + 2.0*nx0*nx0 - RR*nnmod); + poly[1] = 4.0*(nx0*(x0x0 - rr) - RR*nx0mod); + poly[0] = x0x0*x0x0 + RR*(RR - 2.0*x0x0mod) - rr*(2.0*(RR + x0x0) - rr); + + double x[4]; + size_t n = dmnsn_polynomial_solve(poly, 4, x); + if (n == 0) + return false; + + double t = x[0]; + for (size_t i = 1; i < n; ++i) { + t = dmnsn_min(t, x[i]); + } + + if (t < 0.0) { + return false; + } + + dmnsn_vector p = dmnsn_ray_point(l, t); + dmnsn_vector center = dmnsn_vector_mul( + R, + dmnsn_vector_normalized(dmnsn_new_vector(p.x, 0.0, p.z)) + ); + dmnsn_vector normal = dmnsn_vector_sub(p, center); + + intersection->t = t; + intersection->normal = normal; + return true; +} + +/// Torus inside callback. +static bool +dmnsn_torus_inside_fn(const dmnsn_object *object, dmnsn_vector point) +{ + const dmnsn_torus *torus = (const dmnsn_torus *)object; + double dmajor = torus->major - sqrt(point.x*point.x + point.z*point.z); + return dmajor*dmajor + point.y*point.y < torus->minor*torus->minor; +} + +/// Torus bounding callback. +static dmnsn_aabb +dmnsn_torus_bounding_fn(const dmnsn_object *object, dmnsn_matrix trans) +{ + const dmnsn_torus *torus = (const dmnsn_torus *)object; + + double extent = torus->major + torus->minor; + dmnsn_aabb box = dmnsn_symmetric_aabb(dmnsn_new_vector(extent, torus->minor, extent)); + return dmnsn_transform_aabb(trans, box); +} + +/// Torus vtable. +static const dmnsn_object_vtable dmnsn_torus_vtable = { + .intersection_fn = dmnsn_torus_intersection_fn, + .inside_fn = dmnsn_torus_inside_fn, + .bounding_fn = dmnsn_torus_bounding_fn, +}; + +dmnsn_object * +dmnsn_new_torus(dmnsn_pool *pool, double major, double minor) +{ + dmnsn_torus *torus = DMNSN_PALLOC(pool, dmnsn_torus); + torus->major = major; + torus->minor = minor; + + dmnsn_object *object = &torus->object; + dmnsn_init_object(object); + object->vtable = &dmnsn_torus_vtable; + return object; +} diff --git a/libdimension/model/objects/triangle.c b/libdimension/model/objects/triangle.c new file mode 100644 index 0000000..5af3301 --- /dev/null +++ b/libdimension/model/objects/triangle.c @@ -0,0 +1,163 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Triangles. See + * http://tavianator.com/2014/05/a-beautiful-raytriangle-intersection-method/ + * for a description of the intersection algorithm. + */ + +#include "internal.h" +#include "dimension/model.h" + +/// Optimized ray/triangle intersection test. +static inline bool +dmnsn_ray_triangle_intersection(dmnsn_ray l, double *t, double *u, double *v) +{ + // See the change of basis in dmnsn_triangle_basis() + *t = -l.x0.z/l.n.z; + *u = l.x0.x + (*t)*l.n.x; + *v = l.x0.y + (*t)*l.n.y; + return *t >= 0.0 && *u >= 0.0 && *v >= 0.0 && *u + *v <= 1.0; +} + +/// Triangle intersection callback. +DMNSN_HOT static bool +dmnsn_triangle_intersection_fn(const dmnsn_object *object, dmnsn_ray l, + dmnsn_intersection *intersection) +{ + double t, u, v; + if (dmnsn_ray_triangle_intersection(l, &t, &u, &v)) { + intersection->t = t; + intersection->normal = dmnsn_z; + return true; + } + + return false; +} + +/// Triangle inside callback. +static bool +dmnsn_triangle_inside_fn(const dmnsn_object *object, dmnsn_vector point) +{ + return false; +} + +/// Triangle bounding callback. +static dmnsn_aabb +dmnsn_triangle_bounding_fn(const dmnsn_object *object, dmnsn_matrix trans) +{ + dmnsn_vector a = dmnsn_transform_point(trans, dmnsn_zero); + dmnsn_vector b = dmnsn_transform_point(trans, dmnsn_x); + dmnsn_vector c = dmnsn_transform_point(trans, dmnsn_y); + + dmnsn_aabb box = dmnsn_new_aabb(a, a); + box = dmnsn_aabb_swallow(box, b); + box = dmnsn_aabb_swallow(box, c); + return box; +} + +/// Triangle vtable. +static const dmnsn_object_vtable dmnsn_triangle_vtable = { + .intersection_fn = dmnsn_triangle_intersection_fn, + .inside_fn = dmnsn_triangle_inside_fn, + .bounding_fn = dmnsn_triangle_bounding_fn, +}; + +/// Smooth triangle type. +typedef struct { + dmnsn_object object; + dmnsn_vector na, nab, nac; +} dmnsn_smooth_triangle; + +/// Smooth triangle intersection callback. +DMNSN_HOT static bool +dmnsn_smooth_triangle_intersection_fn(const dmnsn_object *object, dmnsn_ray l, + dmnsn_intersection *intersection) +{ + const dmnsn_smooth_triangle *triangle = (const dmnsn_smooth_triangle *)object; + + double t, u, v; + if (dmnsn_ray_triangle_intersection(l, &t, &u, &v)) { + intersection->t = t; + intersection->normal = dmnsn_vector_add( + triangle->na, + dmnsn_vector_add( + dmnsn_vector_mul(u, triangle->nab), + dmnsn_vector_mul(v, triangle->nac) + ) + ); + return true; + } + + return false; +} + +/// Smooth triangle vtable. +static const dmnsn_object_vtable dmnsn_smooth_triangle_vtable = { + .intersection_fn = dmnsn_smooth_triangle_intersection_fn, + .inside_fn = dmnsn_triangle_inside_fn, + .bounding_fn = dmnsn_triangle_bounding_fn, +}; + +/// Make a change-of-basis matrix. +static inline dmnsn_matrix +dmnsn_triangle_basis(dmnsn_vector vertices[3]) +{ + // The new vector space has corners at <1, 0, 0>, <0, 1, 0>, and 0, + // corresponding to the basis (ab, ac, ab X ac). + dmnsn_vector ab = dmnsn_vector_sub(vertices[1], vertices[0]); + dmnsn_vector ac = dmnsn_vector_sub(vertices[2], vertices[0]); + dmnsn_vector normal = dmnsn_vector_cross(ab, ac); + return dmnsn_new_matrix4(ab, ac, normal, vertices[0]); +} + +dmnsn_object * +dmnsn_new_triangle(dmnsn_pool *pool, dmnsn_vector vertices[3]) +{ + dmnsn_object *object = dmnsn_new_object(pool); + object->vtable = &dmnsn_triangle_vtable; + object->intrinsic_trans = dmnsn_triangle_basis(vertices); + return object; +} + +dmnsn_object * +dmnsn_new_smooth_triangle(dmnsn_pool *pool, dmnsn_vector vertices[3], dmnsn_vector normals[3]) +{ + dmnsn_matrix P = dmnsn_triangle_basis(vertices); + + // Transform the given normals. + dmnsn_vector na = dmnsn_vector_normalized(dmnsn_transform_normal(P, normals[0])); + dmnsn_vector nb = dmnsn_vector_normalized(dmnsn_transform_normal(P, normals[1])); + dmnsn_vector nc = dmnsn_vector_normalized(dmnsn_transform_normal(P, normals[2])); + + dmnsn_smooth_triangle *triangle = DMNSN_PALLOC(pool, dmnsn_smooth_triangle); + triangle->na = na; + triangle->nab = dmnsn_vector_sub(nb, na); + triangle->nac = dmnsn_vector_sub(nc, na); + + dmnsn_object *object = &triangle->object; + dmnsn_init_object(object); + object->vtable = &dmnsn_smooth_triangle_vtable; + object->intrinsic_trans = P; + + return object; +} diff --git a/libdimension/model/objects/triangle_fan.c b/libdimension/model/objects/triangle_fan.c new file mode 100644 index 0000000..93768a9 --- /dev/null +++ b/libdimension/model/objects/triangle_fan.c @@ -0,0 +1,347 @@ +/************************************************************************* + * Copyright (C) 2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Triangle fans. See + * http://tavianator.com/2014/05/a-beautiful-raymesh-intersection-algorithm/ + * for a description of the intersection algorithm. + */ + +#include "internal.h" +#include "dimension/model.h" + +/// Triangle fan type. +typedef struct { + dmnsn_object object; + size_t ncoeffs; + double coeffs[][6]; +} dmnsn_triangle_fan; + +/// Change basis from one triangle to the next. +static inline dmnsn_vector +dmnsn_change_basis(const double coeffs[6], dmnsn_vector v) +{ + return dmnsn_new_vector( + coeffs[0]*v.x + coeffs[1]*v.z + v.y, + coeffs[2]*v.x + coeffs[3]*v.z, + coeffs[4]*v.x + coeffs[5]*v.z + ); +} + +/// Change basis from one triangle to the next for a normal vector. +static inline dmnsn_vector +dmnsn_change_normal_basis(const double coeffs[6], dmnsn_vector n) +{ + return dmnsn_new_vector( + coeffs[0]*n.x + coeffs[2]*n.y + coeffs[4]*n.z, + n.x, + coeffs[1]*n.x + coeffs[3]*n.y + coeffs[5]*n.z + ); +} + +/// Change basis from one triangle to the next for a ray +static inline dmnsn_ray +dmnsn_change_ray_basis(const double coeffs[6], dmnsn_ray l) +{ + return dmnsn_new_ray(dmnsn_change_basis(coeffs, l.x0), dmnsn_change_basis(coeffs, l.n)); +} + +/// Store the compressed incremental matrix. +static inline void +dmnsn_compress_coeffs(double coeffs[6], dmnsn_matrix incremental) +{ + coeffs[0] = incremental.n[0][0]; + coeffs[1] = incremental.n[0][2]; + coeffs[2] = incremental.n[1][0]; + coeffs[3] = incremental.n[1][2]; + coeffs[4] = incremental.n[2][0]; + coeffs[5] = incremental.n[2][2]; +} + +/// Decompress the incremental matrix. +static inline dmnsn_matrix +dmnsn_decompress_coeffs(const double coeffs[6]) +{ + dmnsn_matrix incremental = dmnsn_new_matrix( + coeffs[0], 1.0, coeffs[1], 0.0, + coeffs[2], 0.0, coeffs[3], 0.0, + coeffs[4], 0.0, coeffs[5], 0.0 + ); + return incremental; +} + +/// Make a change-of-basis matrix for a triangle. +static inline dmnsn_matrix +dmnsn_triangle_basis(dmnsn_vector a, dmnsn_vector ab, dmnsn_vector ac) +{ + dmnsn_vector normal = dmnsn_vector_cross(ab, ac); + return dmnsn_new_matrix4(ab, ac, normal, a); +} + +/// Optimized ray/triangle intersection test. +static inline bool +dmnsn_ray_triangle_intersection(dmnsn_ray l, double *t, double *u, double *v) +{ + *t = -l.x0.z/l.n.z; + *u = l.x0.x + (*t)*l.n.x; + *v = l.x0.y + (*t)*l.n.y; + return *t >= 0.0 && *u >= 0.0 && *v >= 0.0 && *u + *v <= 1.0; +} + +/// Triangle fan intersection callback. +DMNSN_HOT static bool +dmnsn_triangle_fan_intersection_fn(const dmnsn_object *object, dmnsn_ray l, dmnsn_intersection *intersection) +{ + const dmnsn_triangle_fan *fan = (const dmnsn_triangle_fan *)object; + + double t, u, v; + + double best_t = INFINITY; + if (dmnsn_ray_triangle_intersection(l, &t, &u, &v)) { + best_t = t; + } + + dmnsn_vector normal = dmnsn_z; + dmnsn_vector best_normal = normal; + + for (size_t i = 0; i < fan->ncoeffs; ++i) { + const double *coeffs = fan->coeffs[i]; + l = dmnsn_change_ray_basis(coeffs, l); + normal = dmnsn_change_normal_basis(coeffs, normal); + + if (dmnsn_ray_triangle_intersection(l, &t, &u, &v) && t < best_t) { + best_t = t; + best_normal = normal; + } + } + + if (!isinf(best_t)) { + intersection->t = t; + intersection->normal = best_normal; + return true; + } + + return false; +} + +/// Triangle fan inside callback. +static bool +dmnsn_triangle_fan_inside_fn(const dmnsn_object *object, dmnsn_vector point) +{ + return false; +} + +/// Computes the bounding box for the first triangle +static inline dmnsn_aabb +dmnsn_bound_first_triangle(dmnsn_matrix trans) +{ + dmnsn_vector a = dmnsn_transform_point(trans, dmnsn_zero); + dmnsn_vector b = dmnsn_transform_point(trans, dmnsn_x); + dmnsn_vector c = dmnsn_transform_point(trans, dmnsn_y); + + dmnsn_aabb box = dmnsn_new_aabb(a, a); + box = dmnsn_aabb_swallow(box, b); + box = dmnsn_aabb_swallow(box, c); + + return box; +} + +/// Triangle fan bounding callback. +static dmnsn_aabb +dmnsn_triangle_fan_bounding_fn(const dmnsn_object *object, dmnsn_matrix trans) +{ + const dmnsn_triangle_fan *fan = (const dmnsn_triangle_fan *)object; + + dmnsn_aabb box = dmnsn_bound_first_triangle(trans); + + for (size_t i = 0; i < fan->ncoeffs; ++i) { + dmnsn_matrix incremental = dmnsn_decompress_coeffs(fan->coeffs[i]); + trans = dmnsn_matrix_mul(trans, dmnsn_matrix_inverse(incremental)); + dmnsn_vector vertex = dmnsn_transform_point(trans, dmnsn_y); + box = dmnsn_aabb_swallow(box, vertex); + } + + return box; +} + +/// Triangle fan vtable. +static dmnsn_object_vtable dmnsn_triangle_fan_vtable = { + .intersection_fn = dmnsn_triangle_fan_intersection_fn, + .inside_fn = dmnsn_triangle_fan_inside_fn, + .bounding_fn = dmnsn_triangle_fan_bounding_fn, +}; + +/// Smooth triangle fan type. +typedef struct dmnsn_smooth_triangle_fan { + dmnsn_object object; + dmnsn_vector na, nab, nac; + size_t ncoeffs; + double coeffs[][9]; ///< 0-6 is same as dmnsn_triangle_fan, 6-9 is the normal +} dmnsn_smooth_triangle_fan; + +/// Smooth triangle fan intersection callback. +DMNSN_HOT static bool +dmnsn_smooth_triangle_fan_intersection_fn(const dmnsn_object *object, dmnsn_ray l, dmnsn_intersection *intersection) +{ + const dmnsn_smooth_triangle_fan *fan = (const dmnsn_smooth_triangle_fan *)object; + + dmnsn_vector nab = fan->nab; + dmnsn_vector nac = fan->nac; + + double t, u, v; + + double best_t = INFINITY; + dmnsn_vector best_normal; + if (dmnsn_ray_triangle_intersection(l, &t, &u, &v)) { + best_t = t; + best_normal = dmnsn_vector_add(dmnsn_vector_mul(u, nab), dmnsn_vector_mul(v, nac)); + } + + for (size_t i = 0; i < fan->ncoeffs; ++i) { + const double *coeffs = fan->coeffs[i]; + l = dmnsn_change_ray_basis(coeffs, l); + nab = nac; + nac = dmnsn_new_vector(coeffs[6], coeffs[7], coeffs[8]); + + if (dmnsn_ray_triangle_intersection(l, &t, &u, &v) && t < best_t) { + best_t = t; + best_normal = dmnsn_vector_add(dmnsn_vector_mul(u, nab), dmnsn_vector_mul(v, nac)); + } + } + + if (!isinf(best_t)) { + intersection->t = t; + intersection->normal = dmnsn_vector_add(fan->na, best_normal); + return true; + } + + return false; +} + +/// Smooth triangle fan bounding callback. +static dmnsn_aabb +dmnsn_smooth_triangle_fan_bounding_fn(const dmnsn_object *object, dmnsn_matrix trans) +{ + const dmnsn_smooth_triangle_fan *fan = (const dmnsn_smooth_triangle_fan *)object; + + dmnsn_aabb box = dmnsn_bound_first_triangle(trans); + + for (size_t i = 0; i < fan->ncoeffs; ++i) { + dmnsn_matrix incremental = dmnsn_decompress_coeffs(fan->coeffs[i]); + trans = dmnsn_matrix_mul(trans, dmnsn_matrix_inverse(incremental)); + dmnsn_vector vertex = dmnsn_transform_point(trans, dmnsn_y); + box = dmnsn_aabb_swallow(box, vertex); + } + + return box; +} + +/// Smooth triangle fan vtable. +static dmnsn_object_vtable dmnsn_smooth_triangle_fan_vtable = { + .intersection_fn = dmnsn_smooth_triangle_fan_intersection_fn, + .inside_fn = dmnsn_triangle_fan_inside_fn, + .bounding_fn = dmnsn_smooth_triangle_fan_bounding_fn, +}; + +dmnsn_object * +dmnsn_new_triangle_fan(dmnsn_pool *pool, dmnsn_vector vertices[], size_t nvertices) +{ + dmnsn_assert(nvertices >= 3, "Not enough vertices for one triangle"); + + size_t ncoeffs = nvertices - 3; + dmnsn_triangle_fan *fan = dmnsn_palloc(pool, sizeof(dmnsn_triangle_fan) + ncoeffs*sizeof(double[6])); + fan->ncoeffs = ncoeffs; + + dmnsn_object *object = &fan->object; + dmnsn_init_object(object); + object->vtable = &dmnsn_triangle_fan_vtable; + + // Compute the initial matrix and the coefficients + dmnsn_vector a = vertices[0]; + dmnsn_vector ab = dmnsn_vector_sub(vertices[1], a); + dmnsn_vector ac = dmnsn_vector_sub(vertices[2], a); + dmnsn_matrix P = dmnsn_triangle_basis(a, ab, ac); + object->intrinsic_trans = P; + + for (size_t i = 0; i < ncoeffs; ++i) { + ab = ac; + ac = dmnsn_vector_sub(vertices[i + 3], a); + + dmnsn_matrix newP = dmnsn_triangle_basis(a, ab, ac); + dmnsn_matrix incremental = dmnsn_matrix_mul(dmnsn_matrix_inverse(newP), P); + dmnsn_compress_coeffs(fan->coeffs[i], incremental); + + P = newP; + } + + return object; +} + +dmnsn_object * +dmnsn_new_smooth_triangle_fan(dmnsn_pool *pool, dmnsn_vector vertices[], dmnsn_vector normals[], size_t nvertices) +{ + dmnsn_assert(nvertices >= 3, "Not enough vertices for one triangle"); + + size_t ncoeffs = nvertices - 3; + dmnsn_smooth_triangle_fan *fan = dmnsn_palloc(pool, sizeof(dmnsn_smooth_triangle_fan) + ncoeffs*sizeof(double[9])); + fan->ncoeffs = ncoeffs; + + dmnsn_object *object = &fan->object; + dmnsn_init_object(object); + object->vtable = &dmnsn_smooth_triangle_fan_vtable; + + // Compute the initial matrix + dmnsn_vector a = vertices[0]; + dmnsn_vector ab = dmnsn_vector_sub(vertices[1], a); + dmnsn_vector ac = dmnsn_vector_sub(vertices[2], a); + dmnsn_matrix P = dmnsn_triangle_basis(a, ab, ac); + dmnsn_matrix Pabc = P; + object->intrinsic_trans = P; + + // Transform the first three normals + dmnsn_vector na = dmnsn_vector_normalized(dmnsn_transform_normal(P, normals[0])); + dmnsn_vector nb = dmnsn_vector_normalized(dmnsn_transform_normal(P, normals[1])); + dmnsn_vector nc = dmnsn_vector_normalized(dmnsn_transform_normal(P, normals[2])); + fan->na = na; + fan->nab = dmnsn_vector_sub(nb, na); + fan->nac = dmnsn_vector_sub(nc, na); + + // Compute the coefficients + for (size_t i = 0; i < ncoeffs; ++i) { + ab = ac; + ac = dmnsn_vector_sub(vertices[i + 3], a); + + dmnsn_matrix newP = dmnsn_triangle_basis(a, ab, ac); + dmnsn_matrix incremental = dmnsn_matrix_mul(dmnsn_matrix_inverse(newP), P); + double *coeffs = fan->coeffs[i]; + dmnsn_compress_coeffs(coeffs, incremental); + + nc = dmnsn_vector_normalized(dmnsn_transform_normal(Pabc, normals[i + 3])); + dmnsn_vector nac = dmnsn_vector_sub(nc, na); + coeffs[6] = nac.x; + coeffs[7] = nac.y; + coeffs[8] = nac.z; + + P = newP; + } + + return object; +} diff --git a/libdimension/model/pigment.c b/libdimension/model/pigment.c new file mode 100644 index 0000000..a8c7c0b --- /dev/null +++ b/libdimension/model/pigment.c @@ -0,0 +1,72 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Pigments. + */ + +#include "dimension/model.h" + +// Allocate a dummy pigment +dmnsn_pigment * +dmnsn_new_pigment(dmnsn_pool *pool) +{ + dmnsn_pigment *pigment = DMNSN_PALLOC(pool, dmnsn_pigment); + dmnsn_init_pigment(pigment); + return pigment; +} + +// Initialize a pigment +void +dmnsn_init_pigment(dmnsn_pigment *pigment) +{ + pigment->pigment_fn = NULL; + pigment->initialize_fn = NULL; + pigment->trans = dmnsn_identity_matrix(); + pigment->quick_color = DMNSN_TCOLOR(dmnsn_black); + pigment->initialized = false; +} + +// Precompute pigment properties +void +dmnsn_pigment_initialize(dmnsn_pigment *pigment) +{ + dmnsn_assert(!pigment->initialized, "Pigment double-initialized."); + pigment->initialized = true; + + if (pigment->initialize_fn) { + pigment->initialize_fn(pigment); + } + + pigment->trans_inv = dmnsn_matrix_inverse(pigment->trans); +} + +// Evaluate a pigment +dmnsn_tcolor +dmnsn_pigment_evaluate(const dmnsn_pigment *pigment, dmnsn_vector v) +{ + if (pigment->pigment_fn) { + dmnsn_vector v_trans = dmnsn_transform_point(pigment->trans_inv, v); + return pigment->pigment_fn(pigment, v_trans); + } else { + return pigment->quick_color; + } +} diff --git a/libdimension/model/pigments/canvas_pigment.c b/libdimension/model/pigments/canvas_pigment.c new file mode 100644 index 0000000..bb83b0a --- /dev/null +++ b/libdimension/model/pigments/canvas_pigment.c @@ -0,0 +1,57 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Image maps. + */ + +#include "dimension/model.h" + +/// Canvas pigment type. +typedef struct dmnsn_canvas_pigment { + dmnsn_pigment pigment; + dmnsn_canvas *canvas; +} dmnsn_canvas_pigment; + +/// Canvas pigment color callback. +static dmnsn_tcolor +dmnsn_canvas_pigment_fn(const dmnsn_pigment *pigment, dmnsn_vector v) +{ + const dmnsn_canvas_pigment *canvas_pigment = (const dmnsn_canvas_pigment *)pigment; + dmnsn_canvas *canvas = canvas_pigment->canvas; + + size_t x = llround((fmod(v.x, 1.0) + 1.0)*(canvas->width - 1)); + size_t y = llround((fmod(v.y, 1.0) + 1.0)*(canvas->height - 1)); + return dmnsn_canvas_get_pixel(canvas, x%canvas->width, y%canvas->height); +} + +// Create a canvas color +dmnsn_pigment * +dmnsn_new_canvas_pigment(dmnsn_pool *pool, dmnsn_canvas *canvas) +{ + dmnsn_canvas_pigment *canvas_pigment = DMNSN_PALLOC(pool, dmnsn_canvas_pigment); + canvas_pigment->canvas = canvas; + + dmnsn_pigment *pigment = &canvas_pigment->pigment; + dmnsn_init_pigment(pigment); + pigment->pigment_fn = dmnsn_canvas_pigment_fn; + return pigment; +} diff --git a/libdimension/model/pigments/pigment_map.c b/libdimension/model/pigments/pigment_map.c new file mode 100644 index 0000000..a74a66a --- /dev/null +++ b/libdimension/model/pigments/pigment_map.c @@ -0,0 +1,96 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Pigment-mapped pigment patterns. + */ + +#include "dimension/model.h" + +/// Initialize a pigment in a pigment map. +static void +dmnsn_initialize_mapped_pigment(void *ptr) +{ + dmnsn_pigment **pigment = ptr; + dmnsn_pigment_initialize(*pigment); +} + +dmnsn_map * +dmnsn_new_pigment_map(dmnsn_pool *pool) +{ + return dmnsn_new_map(pool, sizeof(dmnsn_pigment *)); +} + +/// Pigment map type. +typedef struct dmnsn_pigment_map { + dmnsn_pigment pigment; + dmnsn_pattern *pattern; + dmnsn_map *map; + dmnsn_pigment_map_flags flags; +} dmnsn_pigment_map; + +/// pigment_map pigment callback. +static dmnsn_tcolor +dmnsn_pigment_map_pigment_fn(const dmnsn_pigment *pigment, dmnsn_vector v) +{ + const dmnsn_pigment_map *pigment_map = (const dmnsn_pigment_map *)pigment; + double n; + dmnsn_pigment *pigment1, *pigment2; + dmnsn_map_evaluate(pigment_map->map, + dmnsn_pattern_value(pigment_map->pattern, v), + &n, &pigment1, &pigment2); + dmnsn_tcolor color1 = dmnsn_pigment_evaluate(pigment1, v); + dmnsn_tcolor color2 = dmnsn_pigment_evaluate(pigment2, v); + + if (pigment_map->flags == DMNSN_PIGMENT_MAP_SRGB) { + color1.c = dmnsn_color_to_sRGB(color1.c); + color2.c = dmnsn_color_to_sRGB(color2.c); + } + dmnsn_tcolor ret = dmnsn_tcolor_gradient(color1, color2, n); + if (pigment_map->flags == DMNSN_PIGMENT_MAP_SRGB) { + ret.c = dmnsn_color_from_sRGB(ret.c); + } + + return ret; +} + +/// pigment_map initialization callback. +static void +dmnsn_pigment_map_initialize_fn(dmnsn_pigment *pigment) +{ + dmnsn_pigment_map *pigment_map = (dmnsn_pigment_map *)pigment; + dmnsn_map_apply(pigment_map->map, dmnsn_initialize_mapped_pigment); +} + +dmnsn_pigment * +dmnsn_new_pigment_map_pigment(dmnsn_pool *pool, dmnsn_pattern *pattern, dmnsn_map *map, dmnsn_pigment_map_flags flags) +{ + dmnsn_pigment_map *pigment_map = DMNSN_PALLOC(pool, dmnsn_pigment_map); + pigment_map->pattern = pattern; + pigment_map->map = map; + pigment_map->flags = flags; + + dmnsn_pigment *pigment = &pigment_map->pigment; + dmnsn_init_pigment(pigment); + pigment->pigment_fn = dmnsn_pigment_map_pigment_fn; + pigment->initialize_fn = dmnsn_pigment_map_initialize_fn; + return pigment; +} diff --git a/libdimension/model/pigments/solid_pigment.c b/libdimension/model/pigments/solid_pigment.c new file mode 100644 index 0000000..bdbd724 --- /dev/null +++ b/libdimension/model/pigments/solid_pigment.c @@ -0,0 +1,35 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Solid color pigments. + */ + +#include "dimension/model.h" + +// Create a solid color +dmnsn_pigment * +dmnsn_new_solid_pigment(dmnsn_pool *pool, dmnsn_tcolor color) +{ + dmnsn_pigment *pigment = dmnsn_new_pigment(pool); + pigment->quick_color = color; + return pigment; +} diff --git a/libdimension/model/scene.c b/libdimension/model/scene.c new file mode 100644 index 0000000..875c376 --- /dev/null +++ b/libdimension/model/scene.c @@ -0,0 +1,79 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Scenes. + */ + +#include "internal.h" +#include "internal/platform.h" +#include "dimension/model.h" +#include + +// Allocate an empty scene +dmnsn_scene * +dmnsn_new_scene(dmnsn_pool *pool) +{ + dmnsn_scene *scene = DMNSN_PALLOC(pool, dmnsn_scene); + + scene->background = NULL; + scene->default_texture = dmnsn_new_texture(pool); + scene->default_interior = dmnsn_new_interior(pool); + scene->canvas = NULL; + scene->region_x = 0; + scene->region_y = 0; + scene->outer_width = 0; + scene->outer_height = 0; + scene->objects = DMNSN_PALLOC_ARRAY(pool, dmnsn_object *); + scene->lights = DMNSN_PALLOC_ARRAY(pool, dmnsn_light *); + scene->camera = NULL; + scene->quality = DMNSN_RENDER_FULL; + scene->reclimit = 5; + scene->adc_bailout = 1.0/255.0; + scene->nthreads = dmnsn_ncpus(); + scene->initialized = false; + + return scene; +} + +void +dmnsn_scene_initialize(dmnsn_scene *scene) +{ + dmnsn_assert(!scene->initialized, "Scene double-initialized."); + scene->initialized = true; + + if (scene->outer_width == 0) { + scene->outer_width = scene->canvas->width; + } + if (scene->outer_height == 0) { + scene->outer_height = scene->canvas->height; + } + + dmnsn_pigment_initialize(scene->background); + + dmnsn_texture_initialize(scene->default_texture); + + DMNSN_ARRAY_FOREACH (dmnsn_object **, object, scene->objects) { + dmnsn_texture_cascade(scene->default_texture, &(*object)->texture); + dmnsn_interior_cascade(scene->default_interior, &(*object)->interior); + dmnsn_object_precompute(*object); + } +} diff --git a/libdimension/model/texture.c b/libdimension/model/texture.c new file mode 100644 index 0000000..b7eb7ef --- /dev/null +++ b/libdimension/model/texture.c @@ -0,0 +1,68 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Textures. + */ + +#include "dimension/model.h" + +dmnsn_texture * +dmnsn_new_texture(dmnsn_pool *pool) +{ + dmnsn_texture *texture = DMNSN_PALLOC(pool, dmnsn_texture); + texture->pigment = NULL; + texture->finish = dmnsn_new_finish(); + texture->trans = dmnsn_identity_matrix(); + texture->initialized = false; + return texture; +} + +void +dmnsn_texture_initialize(dmnsn_texture *texture) +{ + dmnsn_assert(!texture->initialized, "Texture double-initialized."); + texture->initialized = true; + + texture->trans_inv = dmnsn_matrix_inverse(texture->trans); + + if (!texture->pigment->initialized) { + texture->pigment->trans = dmnsn_matrix_mul(texture->trans, + texture->pigment->trans); + dmnsn_pigment_initialize(texture->pigment); + } +} + +void +dmnsn_texture_cascade(dmnsn_texture *default_texture, dmnsn_texture **texturep) +{ + if (!*texturep) { + *texturep = default_texture; + } + + dmnsn_texture *texture = *texturep; + + if (!texture->pigment) { + texture->pigment = default_texture->pigment; + } + + dmnsn_finish_cascade(&default_texture->finish, &texture->finish); +} diff --git a/libdimension/object.c b/libdimension/object.c deleted file mode 100644 index a67b8cb..0000000 --- a/libdimension/object.c +++ /dev/null @@ -1,109 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Objects. - */ - -#include "dimension-internal.h" -#include - -// Allocate a dummy object -dmnsn_object * -dmnsn_new_object(dmnsn_pool *pool) -{ - dmnsn_object *object = DMNSN_PALLOC(pool, dmnsn_object); - dmnsn_init_object(object); - return object; -} - -// Initialize a dmnsn_object field -void -dmnsn_init_object(dmnsn_object *object) -{ - object->vtable = NULL; - object->texture = NULL; - object->interior = NULL; - object->trans = dmnsn_identity_matrix(); - object->intrinsic_trans = dmnsn_identity_matrix(); - object->children = NULL; - object->split_children = false; - object->precomputed = false; -} - -/// Recursively precompute objects. -static void -dmnsn_object_precompute_recursive(dmnsn_object *object, dmnsn_matrix pigment_trans) -{ - dmnsn_assert(!object->precomputed, "Object double-precomputed."); - object->precomputed = true; - - const dmnsn_object_vtable *vtable = object->vtable; - dmnsn_assert(vtable->intersection_fn, "Missing intersection function."); - dmnsn_assert(vtable->inside_fn, "Missing inside function."); - dmnsn_assert(vtable->bounding_fn || vtable->precompute_fn, "Missing bounding and precompute function."); - - // Initialize the texture - if (!object->texture->initialized) { - dmnsn_texture_initialize(object->texture); - } - - dmnsn_matrix total_trans = dmnsn_matrix_mul(object->trans, object->intrinsic_trans); - - // Precompute the object's children - if (object->children) { - DMNSN_ARRAY_FOREACH (dmnsn_object **, child, object->children) { - dmnsn_matrix saved_trans = (*child)->trans; - (*child)->trans = dmnsn_matrix_mul(total_trans, saved_trans); - - dmnsn_matrix child_pigment_trans; - if ((*child)->texture == NULL || (*child)->texture->pigment == NULL) { - // Don't transform cascaded pigments with the child object - child_pigment_trans = pigment_trans; - } else { - child_pigment_trans = dmnsn_matrix_inverse((*child)->trans); - } - - dmnsn_texture_cascade(object->texture, &(*child)->texture); - dmnsn_interior_cascade(object->interior, &(*child)->interior); - dmnsn_object_precompute_recursive(*child, child_pigment_trans); - (*child)->trans = saved_trans; - } - } - - // Precalculate object values - object->pigment_trans = pigment_trans; - object->trans_inv = dmnsn_matrix_inverse(total_trans); - if (vtable->bounding_fn) { - object->bounding_box = vtable->bounding_fn(object, total_trans); - } - if (vtable->precompute_fn) { - vtable->precompute_fn(object); - } -} - -// Precompute object properties -void -dmnsn_object_precompute(dmnsn_object *object) -{ - dmnsn_matrix pigment_trans = dmnsn_matrix_inverse(object->trans); - dmnsn_object_precompute_recursive(object, pigment_trans); -} diff --git a/libdimension/pattern.c b/libdimension/pattern.c deleted file mode 100644 index 22f5c13..0000000 --- a/libdimension/pattern.c +++ /dev/null @@ -1,45 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Patterns. - */ - -#include "dimension-internal.h" - -dmnsn_pattern * -dmnsn_new_pattern(dmnsn_pool *pool) -{ - dmnsn_pattern *pattern = DMNSN_PALLOC(pool, dmnsn_pattern); - dmnsn_init_pattern(pattern); - return pattern; -} - -void -dmnsn_init_pattern(dmnsn_pattern *pattern) -{ -} - -double -dmnsn_pattern_value(const dmnsn_pattern *pattern, dmnsn_vector v) -{ - return pattern->pattern_fn(pattern, v); -} diff --git a/libdimension/pattern/checker.c b/libdimension/pattern/checker.c new file mode 100644 index 0000000..cce9623 --- /dev/null +++ b/libdimension/pattern/checker.c @@ -0,0 +1,63 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Checker pattern. + */ + +#include "dimension/pattern.h" + +/// Checker pattern callback. +static double +dmnsn_checker_pattern_fn(const dmnsn_pattern *checker, dmnsn_vector v) +{ + double xmod = fmod(v.x, 2.0); + double ymod = fmod(v.y, 2.0); + double zmod = fmod(v.z, 2.0); + + if (xmod < -dmnsn_epsilon) + xmod += 2.0; + if (ymod < -dmnsn_epsilon) + ymod += 2.0; + if (zmod < -dmnsn_epsilon) + zmod += 2.0; + + // Return 0 when an even number of coordinates are in [0, 1), 1 otherwise + unsigned int n = 0; + if (xmod >= 1.0) + ++n; + if (ymod >= 1.0) + ++n; + if (zmod >= 1.0) + ++n; + return (n%2 == 0) ? 0.0 : 1.0; +} + +/// The singleton instance. +static dmnsn_pattern dmnsn_checker_instance = { + .pattern_fn = dmnsn_checker_pattern_fn, +}; + +dmnsn_pattern * +dmnsn_new_checker_pattern(dmnsn_pool *pool) +{ + return &dmnsn_checker_instance; +} diff --git a/libdimension/pattern/gradient.c b/libdimension/pattern/gradient.c new file mode 100644 index 0000000..f7a2b97 --- /dev/null +++ b/libdimension/pattern/gradient.c @@ -0,0 +1,56 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Gradient pattern. + */ + +#include "dimension/pattern.h" + +/// Gradient pattern type. +typedef struct dmnns_gradient { + dmnsn_pattern pattern; + dmnsn_vector orientation; +} dmnsn_gradient; + +/// Gradient pattern callback. +static double +dmnsn_gradient_pattern_fn(const dmnsn_pattern *pattern, dmnsn_vector v) +{ + const dmnsn_gradient *gradient = (const dmnsn_gradient *)pattern; + double n = fmod(dmnsn_vector_dot(gradient->orientation, v), 1.0); + if (n < -dmnsn_epsilon) { + n += 1.0; + } + return n; +} + +dmnsn_pattern * +dmnsn_new_gradient_pattern(dmnsn_pool *pool, dmnsn_vector orientation) +{ + dmnsn_gradient *gradient = DMNSN_PALLOC(pool, dmnsn_gradient); + gradient->orientation = dmnsn_vector_normalized(orientation); + + dmnsn_pattern *pattern = &gradient->pattern; + dmnsn_init_pattern(pattern); + pattern->pattern_fn = dmnsn_gradient_pattern_fn; + return pattern; +} diff --git a/libdimension/pattern/leopard.c b/libdimension/pattern/leopard.c new file mode 100644 index 0000000..1a7bce0 --- /dev/null +++ b/libdimension/pattern/leopard.c @@ -0,0 +1,46 @@ +/************************************************************************* + * Copyright (C) 2011-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Leopard pattern. + */ + +#include "dimension/pattern.h" +#include + +/// Leopard pattern callback. +static double +dmnsn_leopard_pattern_fn(const dmnsn_pattern *leopard, dmnsn_vector v) +{ + double val = (sin(v.x) + sin(v.y) + sin(v.z))/3.0; + return val*val; +} + +/// The singleton instance. +static dmnsn_pattern dmnsn_leopard_instance = { + .pattern_fn = dmnsn_leopard_pattern_fn, +}; + +dmnsn_pattern * +dmnsn_new_leopard_pattern(dmnsn_pool *pool) +{ + return &dmnsn_leopard_instance; +} diff --git a/libdimension/pattern/map.c b/libdimension/pattern/map.c new file mode 100644 index 0000000..ac07960 --- /dev/null +++ b/libdimension/pattern/map.c @@ -0,0 +1,126 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Generic maps. + */ + +#include "internal.h" +#include "dimension/pattern.h" + +/// dmnsn_map definition. +struct dmnsn_map { + size_t obj_size; ///< @internal The size of the mapped objects. + dmnsn_array *array; ///< @internal The map entries. +}; + +/// An [index, object] pair. +typedef struct dmnsn_map_entry { + double n; + char object[]; +} dmnsn_map_entry; + +dmnsn_map * +dmnsn_new_map(dmnsn_pool *pool, size_t size) +{ + dmnsn_map *map = DMNSN_PALLOC(pool, dmnsn_map); + map->obj_size = size; + map->array = dmnsn_palloc_array(pool, sizeof(dmnsn_map_entry) + size); + return map; +} + +void +dmnsn_map_add_entry(dmnsn_map *map, double n, const void *obj) +{ + dmnsn_map_entry *entry; + DMNSN_ALLOCA(entry, sizeof(dmnsn_map_entry) + map->obj_size); + + entry->n = n; + memcpy(entry->object, obj, map->obj_size); + + // Sorted insertion + size_t i; + for (i = dmnsn_array_size(map->array); i-- > 0;) { + dmnsn_map_entry *other = dmnsn_array_at(map->array, i); + if (other->n <= n) { + break; + } + } + + dmnsn_array_insert(map->array, i + 1, entry); +} + +size_t +dmnsn_map_size(const dmnsn_map *map) +{ + return dmnsn_array_size(map->array); +} + +void +dmnsn_map_evaluate(const dmnsn_map *map, double n, + double *val, void *obj1, void *obj2) +{ + dmnsn_assert(dmnsn_array_size(map->array) > 0, + "Attempt to evaluate empty map."); + + const dmnsn_map_entry *entry = dmnsn_array_first(map->array); + + double n1, n2 = 0.0; + const void *o1, *o2 = entry->object; + + if (n < n2) { + *val = 0.0; + memcpy(obj1, o2, map->obj_size); + memcpy(obj2, o2, map->obj_size); + return; + } + + ptrdiff_t skip = sizeof(dmnsn_map_entry) + map->obj_size; + for (const dmnsn_map_entry *last = dmnsn_array_last(map->array); + entry <= last; + entry = (const dmnsn_map_entry *)((const char *)entry + skip)) { + n1 = n2; + o1 = o2; + + n2 = entry->n; + o2 = entry->object; + + if (n < n2) { + *val = (n - n1)/(n2 - n1); + memcpy(obj1, o1, map->obj_size); + memcpy(obj2, o2, map->obj_size); + return; + } + } + + *val = 1.0; + memcpy(obj1, o2, map->obj_size); + memcpy(obj2, o2, map->obj_size); +} + +void +dmnsn_map_apply(dmnsn_map *map, dmnsn_callback_fn *callback) +{ + for (size_t i = 0; i < dmnsn_array_size(map->array); ++i) { + dmnsn_map_entry *entry = dmnsn_array_at(map->array, i); + callback(entry->object); + } +} diff --git a/libdimension/pattern/pattern.c b/libdimension/pattern/pattern.c new file mode 100644 index 0000000..60e1ae7 --- /dev/null +++ b/libdimension/pattern/pattern.c @@ -0,0 +1,45 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Patterns. + */ + +#include "dimension/pattern.h" + +dmnsn_pattern * +dmnsn_new_pattern(dmnsn_pool *pool) +{ + dmnsn_pattern *pattern = DMNSN_PALLOC(pool, dmnsn_pattern); + dmnsn_init_pattern(pattern); + return pattern; +} + +void +dmnsn_init_pattern(dmnsn_pattern *pattern) +{ +} + +double +dmnsn_pattern_value(const dmnsn_pattern *pattern, dmnsn_vector v) +{ + return pattern->pattern_fn(pattern, v); +} diff --git a/libdimension/perspective.c b/libdimension/perspective.c deleted file mode 100644 index 518e52e..0000000 --- a/libdimension/perspective.c +++ /dev/null @@ -1,47 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Perspective cameras. - */ - -#include "dimension.h" -#include - -/// Perspective camera ray callback. -static dmnsn_line -dmnsn_perspective_camera_ray_fn(const dmnsn_camera *camera, double x, double y) -{ - dmnsn_line l = dmnsn_new_line( - dmnsn_zero, - dmnsn_new_vector(x - 0.5, y - 0.5, 1.0) - ); - return l; -} - -// Create a new perspective camera. -dmnsn_camera * -dmnsn_new_perspective_camera(dmnsn_pool *pool) -{ - dmnsn_camera *camera = dmnsn_new_camera(pool); - camera->ray_fn = dmnsn_perspective_camera_ray_fn; - return camera; -} diff --git a/libdimension/phong.c b/libdimension/phong.c deleted file mode 100644 index 10e5b1a..0000000 --- a/libdimension/phong.c +++ /dev/null @@ -1,69 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Phong highlights. - */ - -#include "dimension.h" -#include - -/// Phone specular type. -typedef struct dmnsn_phong { - dmnsn_specular specular; - double coeff; - double exp; -} dmnsn_phong; - -/// Phong specular highlight callback. -static dmnsn_color -dmnsn_phong_specular_fn(const dmnsn_specular *specular, - dmnsn_color light, dmnsn_color color, - dmnsn_vector ray, dmnsn_vector normal, - dmnsn_vector viewer) -{ - const dmnsn_phong *phong = (const dmnsn_phong *)specular; - - dmnsn_vector proj = dmnsn_vector_mul(2*dmnsn_vector_dot(ray, normal), normal); - dmnsn_vector reflected = dmnsn_vector_sub(proj, ray); - - double specular_factor = dmnsn_vector_dot(reflected, viewer); - if (specular_factor < 0.0) { - return dmnsn_black; - } - - specular_factor = pow(specular_factor, phong->exp); - return dmnsn_color_mul(phong->coeff*specular_factor, light); -} - -// A phong finish -dmnsn_specular * -dmnsn_new_phong(dmnsn_pool *pool, double coeff, double exp) -{ - dmnsn_phong *phong = DMNSN_PALLOC(pool, dmnsn_phong); - phong->coeff = coeff; - phong->exp = exp; - - dmnsn_specular *specular = &phong->specular; - dmnsn_init_specular(specular); - specular->specular_fn = dmnsn_phong_specular_fn; - return specular; -} diff --git a/libdimension/pigment.c b/libdimension/pigment.c deleted file mode 100644 index 5fae4f6..0000000 --- a/libdimension/pigment.c +++ /dev/null @@ -1,72 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Pigments. - */ - -#include "dimension-internal.h" - -// Allocate a dummy pigment -dmnsn_pigment * -dmnsn_new_pigment(dmnsn_pool *pool) -{ - dmnsn_pigment *pigment = DMNSN_PALLOC(pool, dmnsn_pigment); - dmnsn_init_pigment(pigment); - return pigment; -} - -// Initialize a pigment -void -dmnsn_init_pigment(dmnsn_pigment *pigment) -{ - pigment->pigment_fn = NULL; - pigment->initialize_fn = NULL; - pigment->trans = dmnsn_identity_matrix(); - pigment->quick_color = DMNSN_TCOLOR(dmnsn_black); - pigment->initialized = false; -} - -// Precompute pigment properties -void -dmnsn_pigment_initialize(dmnsn_pigment *pigment) -{ - dmnsn_assert(!pigment->initialized, "Pigment double-initialized."); - pigment->initialized = true; - - if (pigment->initialize_fn) { - pigment->initialize_fn(pigment); - } - - pigment->trans_inv = dmnsn_matrix_inverse(pigment->trans); -} - -// Evaluate a pigment -dmnsn_tcolor -dmnsn_pigment_evaluate(const dmnsn_pigment *pigment, dmnsn_vector v) -{ - if (pigment->pigment_fn) { - dmnsn_vector v_trans = dmnsn_transform_point(pigment->trans_inv, v); - return pigment->pigment_fn(pigment, v_trans); - } else { - return pigment->quick_color; - } -} diff --git a/libdimension/pigment_map.c b/libdimension/pigment_map.c deleted file mode 100644 index 71de4e0..0000000 --- a/libdimension/pigment_map.c +++ /dev/null @@ -1,96 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Pigment-mapped pigment patterns. - */ - -#include "dimension.h" - -/// Initialize a pigment in a pigment map. -static void -dmnsn_initialize_mapped_pigment(void *ptr) -{ - dmnsn_pigment **pigment = ptr; - dmnsn_pigment_initialize(*pigment); -} - -dmnsn_map * -dmnsn_new_pigment_map(dmnsn_pool *pool) -{ - return dmnsn_new_map(pool, sizeof(dmnsn_pigment *)); -} - -/// Pigment map type. -typedef struct dmnsn_pigment_map { - dmnsn_pigment pigment; - dmnsn_pattern *pattern; - dmnsn_map *map; - dmnsn_pigment_map_flags flags; -} dmnsn_pigment_map; - -/// pigment_map pigment callback. -static dmnsn_tcolor -dmnsn_pigment_map_pigment_fn(const dmnsn_pigment *pigment, dmnsn_vector v) -{ - const dmnsn_pigment_map *pigment_map = (const dmnsn_pigment_map *)pigment; - double n; - dmnsn_pigment *pigment1, *pigment2; - dmnsn_map_evaluate(pigment_map->map, - dmnsn_pattern_value(pigment_map->pattern, v), - &n, &pigment1, &pigment2); - dmnsn_tcolor color1 = dmnsn_pigment_evaluate(pigment1, v); - dmnsn_tcolor color2 = dmnsn_pigment_evaluate(pigment2, v); - - if (pigment_map->flags == DMNSN_PIGMENT_MAP_SRGB) { - color1.c = dmnsn_color_to_sRGB(color1.c); - color2.c = dmnsn_color_to_sRGB(color2.c); - } - dmnsn_tcolor ret = dmnsn_tcolor_gradient(color1, color2, n); - if (pigment_map->flags == DMNSN_PIGMENT_MAP_SRGB) { - ret.c = dmnsn_color_from_sRGB(ret.c); - } - - return ret; -} - -/// pigment_map initialization callback. -static void -dmnsn_pigment_map_initialize_fn(dmnsn_pigment *pigment) -{ - dmnsn_pigment_map *pigment_map = (dmnsn_pigment_map *)pigment; - dmnsn_map_apply(pigment_map->map, dmnsn_initialize_mapped_pigment); -} - -dmnsn_pigment * -dmnsn_new_pigment_map_pigment(dmnsn_pool *pool, dmnsn_pattern *pattern, dmnsn_map *map, dmnsn_pigment_map_flags flags) -{ - dmnsn_pigment_map *pigment_map = DMNSN_PALLOC(pool, dmnsn_pigment_map); - pigment_map->pattern = pattern; - pigment_map->map = map; - pigment_map->flags = flags; - - dmnsn_pigment *pigment = &pigment_map->pigment; - dmnsn_init_pigment(pigment); - pigment->pigment_fn = dmnsn_pigment_map_pigment_fn; - pigment->initialize_fn = dmnsn_pigment_map_initialize_fn; - return pigment; -} diff --git a/libdimension/plane.c b/libdimension/plane.c deleted file mode 100644 index 5428485..0000000 --- a/libdimension/plane.c +++ /dev/null @@ -1,88 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Planes. - */ - -#include "dimension.h" -#include -#include - -/// Plane type. -typedef struct { - dmnsn_object object; - dmnsn_vector normal; -} dmnsn_plane; - -/// Returns the closest intersection of `line' with `plane'. -static bool -dmnsn_plane_intersection_fn(const dmnsn_object *object, dmnsn_line line, - dmnsn_intersection *intersection) -{ - const dmnsn_plane *plane = (const dmnsn_plane *)object; - dmnsn_vector normal = plane->normal; - - double den = dmnsn_vector_dot(line.n, normal); - if (den != 0.0) { - double t = -dmnsn_vector_dot(line.x0, normal)/den; - if (t >= 0.0) { - intersection->t = t; - intersection->normal = normal; - return true; - } - } - return false; -} - -/// Return whether a point is inside a plane. -static bool -dmnsn_plane_inside_fn(const dmnsn_object *object, dmnsn_vector point) -{ - const dmnsn_plane *plane = (const dmnsn_plane *)object; - return dmnsn_vector_dot(point, plane->normal) < 0.0; -} - -/// Plane bounding callback. -static dmnsn_bounding_box -dmnsn_plane_bounding_fn(const dmnsn_object *object, dmnsn_matrix trans) -{ - return dmnsn_infinite_bounding_box(); -} - -/// Plane vtable. -static const dmnsn_object_vtable dmnsn_plane_vtable = { - .intersection_fn = dmnsn_plane_intersection_fn, - .inside_fn = dmnsn_plane_inside_fn, - .bounding_fn = dmnsn_plane_bounding_fn, -}; - -dmnsn_object * -dmnsn_new_plane(dmnsn_pool *pool, dmnsn_vector normal) -{ - dmnsn_plane *plane = DMNSN_PALLOC(pool, dmnsn_plane); - plane->normal = normal; - - dmnsn_object *object = &plane->object; - dmnsn_init_object(object); - object->vtable = &dmnsn_plane_vtable; - return object; -} diff --git a/libdimension/platform.c b/libdimension/platform.c deleted file mode 100644 index b5d23cc..0000000 --- a/libdimension/platform.c +++ /dev/null @@ -1,185 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Platform abstractions. - */ - -#define _GNU_SOURCE -#include "dimension-internal.h" - -#if HAVE_UNISTD_H - #include -#endif -#ifdef _WIN32 - #include -#endif -#if DMNSN_BACKTRACE - #include // For backtrace() etc. -#endif -#if DMNSN_GETTID - #include // For gettid() where supported -#endif -#if DMNSN_SCHED_GETAFFINITY - #include // For sched_getaffinity() -#endif -#if DMNSN_TIMES - #include -#endif -#if DMNSN_GETRUSAGE - #include - #include -#endif - -void -dmnsn_backtrace(FILE *file) -{ -#if DMNSN_BACKTRACE -#define DMNSN_BACKTRACE_SIZE 128 - void *buffer[DMNSN_BACKTRACE_SIZE]; - - int nptrs = backtrace(buffer, DMNSN_BACKTRACE_SIZE); - int fd = fileno(file); - if (fd != -1) { - backtrace_symbols_fd(buffer, nptrs, fd); - } -#endif -} - -bool -dmnsn_is_main_thread(void) -{ -#if DMNSN_GETTID - return getpid() == syscall(SYS_gettid); -#else - return true; -#endif -} - -bool -dmnsn_is_little_endian(void) -{ - // Undefined behaviour, but quite portable - union { - unsigned int i; - unsigned char c; - } u = { .i = 1 }; - return u.c == 1; -} - -size_t -dmnsn_ncpus(void) -{ -#if DMNSN_SCHED_GETAFFINITY - cpu_set_t cpuset; - if (sched_getaffinity(0, sizeof(cpuset), &cpuset) == 0) { - return CPU_COUNT(&cpuset); - } else { - dmnsn_warning("sched_getaffinity() failed."); - return 1; - } -#elif DMNSN_SC_NPROCESSORS_ONLN - long nprocs = sysconf(_SC_NPROCESSORS_ONLN); - if (nprocs > 0) { - return nprocs; - } else { - dmnsn_warning("sysconf(_SC_NPROCESSORS_ONLN) failed."); - return 1; - } -#elif defined(_WIN32) - SYSTEM_INFO sysinfo; - GetSystemInfo(&sysinfo); - return sysinfo.dwNumberOfProcessors; -#else - return 1; -#endif -} - -#if DMNSN_GETRUSAGE -/// Convert a struct timeval to a double. -static inline double -dmnsn_timeval2double(struct timeval tv) -{ - return tv.tv_sec + tv.tv_usec/1.0e6; -} -#endif - -void -dmnsn_get_times(dmnsn_timer *timer) -{ -#if DMNSN_GETRUSAGE - struct timeval real; - gettimeofday(&real, NULL); - - struct rusage usage; - if (getrusage(RUSAGE_SELF, &usage) == 0) { - timer->real = dmnsn_timeval2double(real); - timer->user = dmnsn_timeval2double(usage.ru_utime); - timer->system = dmnsn_timeval2double(usage.ru_stime); - } else { - dmnsn_warning("getrusage() failed."); - timer->real = timer->user = timer->system = 0.0; - } -#elif DMNSN_TIMES - static long clk_tck = 0; - - // Figure out the clock ticks per second - if (!clk_tck) { - clk_tck = sysconf(_SC_CLK_TCK); - if (clk_tck == -1) { - dmnsn_warning("sysconf(_SC_CLK_TCK) failed."); - clk_tck = 1000000L; - } - } - - struct tms buf; - clock_t real = times(&buf); - if (real == (clock_t)-1) { - dmnsn_warning("times() failed."); - timer->real = timer->user = timer->system = 0.0; - } else { - timer->real = (double)real/clk_tck; - timer->user = (double)buf.tms_utime/clk_tck; - timer->system = (double)buf.tms_stime/clk_tck; - } -#elif defined(_WIN32) - FILETIME real; - GetSystemTimeAsFileTime(&real); - - FILETIME user, system, creation, exit; - HANDLE current_process = GetCurrentProcess(); - if (GetProcessTimes(current_process, - &creation, &exit, &system, &user) != 0) - { - timer->real - = (((uint64_t)real.dwHighDateTime << 32) + real.dwLowDateTime)/1.0e7; - timer->user - = (((uint64_t)user.dwHighDateTime << 32) + user.dwLowDateTime)/1.0e7; - timer->system - = (((uint64_t)system.dwHighDateTime << 32) + system.dwLowDateTime)/1.0e7; - } else { - dmnsn_warning("GetProcessTimes() failed."); - timer->real = timer->user = timer->system = 0.0; - } -#else - timer->real = timer->user = timer->system = 0.0; -#endif -} diff --git a/libdimension/platform.h b/libdimension/platform.h deleted file mode 100644 index c03ae52..0000000 --- a/libdimension/platform.h +++ /dev/null @@ -1,60 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2011 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Platform abstractions. - */ - -#include -#include -#include - -/** - * Print a stack trace, if implemented for the current platform. - * @param[in,out] file The file to which to write the stack trace. - */ -DMNSN_INTERNAL void dmnsn_backtrace(FILE *file); - -/** - * Is the calling thread the main thread? - * @return Whether this is the main execution thread, or \c true if we can't - * tell. - */ -DMNSN_INTERNAL bool dmnsn_is_main_thread(void); - -/** - * Are we running on a little-endian computer? - * @return Whether the current architecture is little-endian. - */ -DMNSN_INTERNAL bool dmnsn_is_little_endian(void); - -/** - * How many CPUs are available? - * @return The number of CPUs available to dimension. - */ -DMNSN_INTERNAL size_t dmnsn_ncpus(void); - -/** - * Calculate process times. - * @param[out] timer The timer in which to store the current total process - * times. - */ -DMNSN_INTERNAL void dmnsn_get_times(dmnsn_timer *timer); diff --git a/libdimension/platform/platform.c b/libdimension/platform/platform.c new file mode 100644 index 0000000..84d9bf0 --- /dev/null +++ b/libdimension/platform/platform.c @@ -0,0 +1,187 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Platform abstractions. + */ + +#define _GNU_SOURCE +#include "internal.h" +#include "internal/platform.h" +#include "dimension/platform.h" + +#if HAVE_UNISTD_H + #include +#endif +#ifdef _WIN32 + #include +#endif +#if DMNSN_BACKTRACE + #include // For backtrace() etc. +#endif +#if DMNSN_GETTID + #include // For gettid() where supported +#endif +#if DMNSN_SCHED_GETAFFINITY + #include // For sched_getaffinity() +#endif +#if DMNSN_TIMES + #include +#endif +#if DMNSN_GETRUSAGE + #include + #include +#endif + +void +dmnsn_backtrace(FILE *file) +{ +#if DMNSN_BACKTRACE +#define DMNSN_BACKTRACE_SIZE 128 + void *buffer[DMNSN_BACKTRACE_SIZE]; + + int nptrs = backtrace(buffer, DMNSN_BACKTRACE_SIZE); + int fd = fileno(file); + if (fd != -1) { + backtrace_symbols_fd(buffer, nptrs, fd); + } +#endif +} + +bool +dmnsn_is_main_thread(void) +{ +#if DMNSN_GETTID + return getpid() == syscall(SYS_gettid); +#else + return true; +#endif +} + +bool +dmnsn_is_little_endian(void) +{ + // Undefined behaviour, but quite portable + union { + unsigned int i; + unsigned char c; + } u = { .i = 1 }; + return u.c == 1; +} + +size_t +dmnsn_ncpus(void) +{ +#if DMNSN_SCHED_GETAFFINITY + cpu_set_t cpuset; + if (sched_getaffinity(0, sizeof(cpuset), &cpuset) == 0) { + return CPU_COUNT(&cpuset); + } else { + dmnsn_warning("sched_getaffinity() failed."); + return 1; + } +#elif DMNSN_SC_NPROCESSORS_ONLN + long nprocs = sysconf(_SC_NPROCESSORS_ONLN); + if (nprocs > 0) { + return nprocs; + } else { + dmnsn_warning("sysconf(_SC_NPROCESSORS_ONLN) failed."); + return 1; + } +#elif defined(_WIN32) + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + return sysinfo.dwNumberOfProcessors; +#else + return 1; +#endif +} + +#if DMNSN_GETRUSAGE +/// Convert a struct timeval to a double. +static inline double +dmnsn_timeval2double(struct timeval tv) +{ + return tv.tv_sec + tv.tv_usec/1.0e6; +} +#endif + +void +dmnsn_get_times(dmnsn_timer *timer) +{ +#if DMNSN_GETRUSAGE + struct timeval real; + gettimeofday(&real, NULL); + + struct rusage usage; + if (getrusage(RUSAGE_SELF, &usage) == 0) { + timer->real = dmnsn_timeval2double(real); + timer->user = dmnsn_timeval2double(usage.ru_utime); + timer->system = dmnsn_timeval2double(usage.ru_stime); + } else { + dmnsn_warning("getrusage() failed."); + timer->real = timer->user = timer->system = 0.0; + } +#elif DMNSN_TIMES + static long clk_tck = 0; + + // Figure out the clock ticks per second + if (!clk_tck) { + clk_tck = sysconf(_SC_CLK_TCK); + if (clk_tck == -1) { + dmnsn_warning("sysconf(_SC_CLK_TCK) failed."); + clk_tck = 1000000L; + } + } + + struct tms buf; + clock_t real = times(&buf); + if (real == (clock_t)-1) { + dmnsn_warning("times() failed."); + timer->real = timer->user = timer->system = 0.0; + } else { + timer->real = (double)real/clk_tck; + timer->user = (double)buf.tms_utime/clk_tck; + timer->system = (double)buf.tms_stime/clk_tck; + } +#elif defined(_WIN32) + FILETIME real; + GetSystemTimeAsFileTime(&real); + + FILETIME user, system, creation, exit; + HANDLE current_process = GetCurrentProcess(); + if (GetProcessTimes(current_process, + &creation, &exit, &system, &user) != 0) + { + timer->real + = (((uint64_t)real.dwHighDateTime << 32) + real.dwLowDateTime)/1.0e7; + timer->user + = (((uint64_t)user.dwHighDateTime << 32) + user.dwLowDateTime)/1.0e7; + timer->system + = (((uint64_t)system.dwHighDateTime << 32) + system.dwLowDateTime)/1.0e7; + } else { + dmnsn_warning("GetProcessTimes() failed."); + timer->real = timer->user = timer->system = 0.0; + } +#else + timer->real = timer->user = timer->system = 0.0; +#endif +} diff --git a/libdimension/platform/timer.c b/libdimension/platform/timer.c new file mode 100644 index 0000000..2d91469 --- /dev/null +++ b/libdimension/platform/timer.c @@ -0,0 +1,43 @@ +/************************************************************************* + * Copyright (C) 2010-2011 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Performance counter. + */ + +#include "internal/platform.h" +#include "dimension/platform.h" + +void +dmnsn_timer_start(dmnsn_timer *timer) +{ + dmnsn_get_times(timer); +} + +void +dmnsn_timer_stop(dmnsn_timer *timer) +{ + dmnsn_timer now; + dmnsn_get_times(&now); + timer->real = now.real - timer->real; + timer->user = now.user - timer->user; + timer->system = now.system - timer->system; +} diff --git a/libdimension/png-stubs.c b/libdimension/png-stubs.c deleted file mode 100644 index 9c752a5..0000000 --- a/libdimension/png-stubs.c +++ /dev/null @@ -1,62 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Stubs for PNG functions when compiled with --disable-png. - */ - -#include "dimension.h" -#include - -int -dmnsn_png_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas) -{ - errno = ENOSYS; - return -1; -} - -int -dmnsn_png_write_canvas(const dmnsn_canvas *canvas, FILE *file) -{ - errno = ENOSYS; - return -1; -} - -dmnsn_future * -dmnsn_png_write_canvas_async(const dmnsn_canvas *canvas, FILE *file) -{ - errno = ENOSYS; - return NULL; -} - -dmnsn_canvas * -dmnsn_png_read_canvas(dmnsn_pool *pool, FILE *file) -{ - errno = ENOSYS; - return NULL; -} - -dmnsn_future * -dmnsn_png_read_canvas_async(dmnsn_canvas **canvas, dmnsn_pool *pool, FILE *file) -{ - errno = ENOSYS; - return NULL; -} diff --git a/libdimension/png.c b/libdimension/png.c deleted file mode 100644 index 6b73738..0000000 --- a/libdimension/png.c +++ /dev/null @@ -1,390 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * PNG import/export. - */ - -#include "dimension-internal.h" -#include -#include -#include -#include -#include - -int -dmnsn_png_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas) -{ - dmnsn_rgba16_optimize_canvas(pool, canvas); - return 0; -} - -/// Payload type for PNG write thread callback. -typedef struct { - dmnsn_future *future; - const dmnsn_canvas *canvas; - FILE *file; -} dmnsn_png_write_payload; - -/// Payload type for PNG read thread callback. -typedef struct { - dmnsn_future *future; - dmnsn_canvas **canvas; - dmnsn_pool *pool; - FILE *file; -} dmnsn_png_read_payload; - -/// PNG write thread callback. -static int dmnsn_png_write_canvas_thread(void *ptr); -/// PNG read thread callback. -static int dmnsn_png_read_canvas_thread(void *ptr); - -int -dmnsn_png_write_canvas(const dmnsn_canvas *canvas, FILE *file) -{ - dmnsn_future *future = dmnsn_png_write_canvas_async(canvas, file); - return dmnsn_future_join(future); -} - -dmnsn_future * -dmnsn_png_write_canvas_async(const dmnsn_canvas *canvas, FILE *file) -{ - dmnsn_future *future = dmnsn_new_future(); - - dmnsn_png_write_payload *payload = DMNSN_MALLOC(dmnsn_png_write_payload); - payload->future = future; - payload->canvas = canvas; - payload->file = file; - - // Create the worker thread - dmnsn_new_thread(future, dmnsn_png_write_canvas_thread, payload); - - return future; -} - -// Read a canvas from the PNG file `file'. Return NULL on error. -dmnsn_canvas * -dmnsn_png_read_canvas(dmnsn_pool *pool, FILE *file) -{ - dmnsn_canvas *canvas; - dmnsn_future *future = dmnsn_png_read_canvas_async(&canvas, pool, file); - dmnsn_future_join(future); - return canvas; -} - -// Read a canvas from a png file in the background -dmnsn_future * -dmnsn_png_read_canvas_async(dmnsn_canvas **canvas, dmnsn_pool *pool, FILE *file) -{ - dmnsn_future *future = dmnsn_new_future(); - dmnsn_png_read_payload *payload = DMNSN_MALLOC(dmnsn_png_read_payload); - - payload->future = future; - payload->canvas = canvas; - *payload->canvas = NULL; - payload->pool = pool; - payload->file = file; - - // Create the worker thread - dmnsn_new_thread(future, dmnsn_png_read_canvas_thread, payload); - - return future; -} - -////////////////////// -// Thread callbacks // -////////////////////// - -// Write a PNG file -static int -dmnsn_png_write_canvas_thread(void *ptr) -{ - dmnsn_png_write_payload *payload = ptr; - - png_uint_32 width = payload->canvas->width; - png_uint_32 height = payload->canvas->height; - - dmnsn_future_set_total(payload->future, height); - - png_structp png_ptr - = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png_ptr) { - // Couldn't create libpng write struct - dmnsn_free(payload); - return -1; - } - - png_infop info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) { - // Couldn't create libpng info struct - png_destroy_write_struct(&png_ptr, NULL); - dmnsn_free(payload); - return -1; - } - - // libpng will longjmp here if it encounters an error from here on - uint16_t *row = NULL; - if (setjmp(png_jmpbuf(png_ptr))) { - // libpng error - dmnsn_free(row); - png_destroy_write_struct(&png_ptr, &info_ptr); - dmnsn_free(payload); - return -1; - } - - // Associate file with the libpng write struct - png_init_io(png_ptr, payload->file); - - // Set header correctly for 16-bit sRGB image - png_set_IHDR(png_ptr, info_ptr, width, height, 16, - PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE); - - // We think of transparency in the opposite way that PNG does - png_set_invert_alpha(png_ptr); - - // Write the info struct - png_write_info(png_ptr, info_ptr); - - if (dmnsn_is_little_endian()) { - // We are little-endian; swap the byte order of the pixels - png_set_swap(png_ptr); - } - - // Check if we can optimize this - dmnsn_rgba16_optimizer *rgba16 = (dmnsn_rgba16_optimizer *)dmnsn_canvas_find_optimizer(payload->canvas, dmnsn_rgba16_optimizer_fn); - if (rgba16) { - for (size_t y = 0; y < height; ++y) { - // Invert the rows. PNG coordinates are fourth quadrant. - uint16_t *row_opt = rgba16->data + 4*(height - y - 1)*width; - png_write_row(png_ptr, (png_bytep)row_opt); - dmnsn_future_increment(payload->future); - } - - // Finish the PNG file - png_write_end(png_ptr, info_ptr); - png_destroy_write_struct(&png_ptr, &info_ptr); - dmnsn_free(payload); - return 0; - } - - // Allocate the temporary row of RGBA values - row = dmnsn_malloc(4*sizeof(uint16_t)*width); - - // Write the pixels - for (size_t y = 0; y < height; ++y) { - for (size_t x = 0; x < width; ++x) { - // Invert the rows. PNG coordinates are fourth quadrant. - dmnsn_tcolor tcolor = dmnsn_canvas_get_pixel(payload->canvas, - x, height - y - 1); - tcolor = dmnsn_tcolor_remove_filter(tcolor); - tcolor.c = dmnsn_color_to_sRGB(tcolor.c); - tcolor = dmnsn_tcolor_clamp(tcolor); - - row[4*x] = lround(tcolor.c.R*UINT16_MAX); - row[4*x + 1] = lround(tcolor.c.G*UINT16_MAX); - row[4*x + 2] = lround(tcolor.c.B*UINT16_MAX); - row[4*x + 3] = lround(tcolor.T*UINT16_MAX); - } - - // Write the row - png_write_row(png_ptr, (png_bytep)row); - dmnsn_future_increment(payload->future); - } - - // Finish the PNG file - png_write_end(png_ptr, info_ptr); - - dmnsn_free(row); - png_destroy_write_struct(&png_ptr, &info_ptr); - dmnsn_free(payload); - return 0; -} - -/// Thread-specific pointer to the appropriate dmnsn_future* for -/// dmnsn_png_read_row_callback. -static __thread dmnsn_future *dmnsn_tl_png_read_future; - -/// Callback to increment the progress after a row has been read. -static void -dmnsn_png_read_row_callback(png_structp png_ptr, png_uint_32 row, int pass) -{ - dmnsn_future_increment(dmnsn_tl_png_read_future); -} - -// Read a PNG file -static int -dmnsn_png_read_canvas_thread(void *ptr) -{ - dmnsn_png_read_payload *payload = ptr; - dmnsn_tl_png_read_future = payload->future; - - png_byte header[8]; - if (fread(header, 1, 8, payload->file) != 8) { - dmnsn_free(payload); - return -1; - } - if (png_sig_cmp(header, 0, 8)) { - // payload->file is not a PNG file - dmnsn_free(payload); - errno = EINVAL; - return -1; - } - - // Create the libpng read struct - png_structp png_ptr - = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png_ptr) { - dmnsn_free(payload); - return -1; - } - - // Create the libpng info struct - png_infop info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) { - png_destroy_read_struct(&png_ptr, NULL, NULL); - dmnsn_free(payload); - return -1; - } - - // libpng will longjmp here if it encounters an error from here on - png_bytep image = NULL; - png_bytep *row_pointers = NULL; - if (setjmp(png_jmpbuf(png_ptr))) { - // libpng error - dmnsn_free(row_pointers); - dmnsn_free(image); - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - dmnsn_free(payload); - return -1; - } - - // Associate the read struct with the file, and tell it we've already checked - // 8 bytes of signature - png_init_io(png_ptr, payload->file); - png_set_sig_bytes(png_ptr, 8); - - // Read the PNG header into info struct - png_read_info(png_ptr, info_ptr); - - // Get useful information from the info struct - png_uint_32 width, height; - int bit_depth, color_type, interlace_type, compression_type, filter_method; - png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, - &interlace_type, &compression_type, &filter_method); - int number_of_passes = png_set_interlace_handling(png_ptr); - - dmnsn_future_set_total(payload->future, (number_of_passes + 1)*height); - png_set_read_status_fn(png_ptr, dmnsn_png_read_row_callback); - - // - Convert paletted images to RGB. - // - Convert a tRNS chunk to an alpha channel - // - Convert grayscale to RGB - // - Invert the alpha channel - if (color_type == PNG_COLOR_TYPE_PALETTE) { - png_set_palette_to_rgb(png_ptr); - } - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { - png_set_tRNS_to_alpha(png_ptr); - } - if (color_type == PNG_COLOR_TYPE_GRAY - || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - { - png_set_gray_to_rgb(png_ptr); - } - png_set_invert_alpha(png_ptr); - - // Update the info struct - png_read_update_info(png_ptr, info_ptr); - - // Get bytes/image row - png_uint_32 rowbytes = png_get_rowbytes(png_ptr, info_ptr); - - // Allocate the temporary image buffer - image = dmnsn_malloc(rowbytes*height); - - // Allocate and set an array of pointers to rows in image - row_pointers = dmnsn_malloc(sizeof(png_bytep)*height); - - for (size_t y = 0; y < height; ++y) { - row_pointers[y] = image + y*rowbytes; - } - - // Read the image to memory all at once. At the expense of greater memory - // use, this handles interlacing for us. - png_read_image(png_ptr, row_pointers); - - // Allocate the canvas - *payload->canvas = dmnsn_new_canvas(payload->pool, width, height); - - // Now we convert the image to our canvas format. This depends on the image - // bit depth (which has been scaled up to at least 8 or 16), and the presence - // of an alpha channel. - for (size_t y = 0; y < height; ++y) { - for (size_t x = 0; x < width; ++x) { - dmnsn_tcolor tcolor; - tcolor.F = 0.0; - - if (color_type & PNG_COLOR_MASK_ALPHA) { - if (bit_depth == 16) { - png_bytep png_pixel = image + 8*(y*width + x); - tcolor.c.R = (double)((png_pixel[0] << 8) + png_pixel[1])/UINT16_MAX; - tcolor.c.G = (double)((png_pixel[2] << 8) + png_pixel[3])/UINT16_MAX; - tcolor.c.B = (double)((png_pixel[4] << 8) + png_pixel[5])/UINT16_MAX; - tcolor.T = (double)((png_pixel[6] << 8) + png_pixel[7])/UINT16_MAX; - } else { - png_bytep png_pixel = image + 4*(y*width + x); - tcolor.c.R = (double)png_pixel[0]/UINT8_MAX; - tcolor.c.G = (double)png_pixel[1]/UINT8_MAX; - tcolor.c.B = (double)png_pixel[2]/UINT8_MAX; - tcolor.T = (double)png_pixel[3]/UINT8_MAX; - } - } else { - tcolor.T = 0.0; - - if (bit_depth == 16) { - png_bytep png_pixel = image + 6*(y*width + x); - tcolor.c.R = (double)((png_pixel[0] << 8) + png_pixel[1])/UINT16_MAX; - tcolor.c.G = (double)((png_pixel[2] << 8) + png_pixel[3])/UINT16_MAX; - tcolor.c.B = (double)((png_pixel[4] << 8) + png_pixel[5])/UINT16_MAX; - } else { - png_bytep png_pixel = image + 3*(y*width + x); - tcolor.c.R = (double)png_pixel[0]/UINT8_MAX; - tcolor.c.G = (double)png_pixel[1]/UINT8_MAX; - tcolor.c.B = (double)png_pixel[2]/UINT8_MAX; - } - } - - tcolor.c = dmnsn_color_from_sRGB(tcolor.c); - dmnsn_canvas_set_pixel(*payload->canvas, x, height - y - 1, tcolor); - } - - dmnsn_future_increment(payload->future); - } - - dmnsn_free(row_pointers); - dmnsn_free(image); - png_read_end(png_ptr, NULL); - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - dmnsn_free(payload); - return 0; -} diff --git a/libdimension/point_light.c b/libdimension/point_light.c deleted file mode 100644 index b8c2cfb..0000000 --- a/libdimension/point_light.c +++ /dev/null @@ -1,72 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Point lights. - */ - -#include "dimension.h" -#include - -/// Point light type. -typedef struct dmnsn_point_light { - dmnsn_light light; - dmnsn_vector origin; - dmnsn_color color; -} dmnsn_point_light; - -/// Point light direction callback. -static dmnsn_vector -dmnsn_point_light_direction_fn(const dmnsn_light *light, dmnsn_vector v) -{ - const dmnsn_point_light *point_light = (const dmnsn_point_light *)light; - return dmnsn_vector_sub(point_light->origin, v); -} - -/// Point light illumination callback. -static dmnsn_color -dmnsn_point_light_illumination_fn(const dmnsn_light *light, dmnsn_vector v) -{ - const dmnsn_point_light *point_light = (const dmnsn_point_light *)light; - return point_light->color; -} - -/// Point light illumination callback. -static bool -dmnsn_point_light_shadow_fn(const dmnsn_light *light, double t) -{ - return t < 1.0; -} - -dmnsn_light * -dmnsn_new_point_light(dmnsn_pool *pool, dmnsn_vector x0, dmnsn_color color) -{ - dmnsn_point_light *point_light = DMNSN_PALLOC(pool, dmnsn_point_light); - point_light->origin = x0; - point_light->color = color; - - dmnsn_light *light = &point_light->light; - dmnsn_init_light(light); - light->direction_fn = dmnsn_point_light_direction_fn; - light->illumination_fn = dmnsn_point_light_illumination_fn; - light->shadow_fn = dmnsn_point_light_shadow_fn; - return light; -} diff --git a/libdimension/polynomial.c b/libdimension/polynomial.c deleted file mode 100644 index 609364a..0000000 --- a/libdimension/polynomial.c +++ /dev/null @@ -1,441 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2011 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Real root isolation algorithm based on work by Vincent, Uspensky, Collins and - * Akritas, Johnson, Krandick, and Rouillier and Zimmerman. - */ - -#include "dimension-internal.h" -#include - -/// Get the real degree of a polynomial, ignoring leading zeros. -static inline size_t -dmnsn_real_degree(const double poly[], size_t degree) -{ - for (size_t i = degree + 1; i-- > 0;) { - if (dmnsn_likely(fabs(poly[i]) >= dmnsn_epsilon)) { - return i; - } - } - - return 0; -} - -/// Divide each coefficient by the leading coefficient. -static inline void -dmnsn_polynomial_normalize(double poly[], size_t degree) -{ - for (size_t i = 0; i < degree; ++i) { - poly[i] /= poly[degree]; - } - poly[degree] = 1.0; -} - -/// Eliminate trivial zero roots from \p poly[]. -static inline void -dmnsn_eliminate_zero_roots(double **poly, size_t *degree) -{ - size_t i; - for (i = 0; i <= *degree; ++i) { - if (dmnsn_likely(fabs((*poly)[i]) >= dmnsn_epsilon)) { - break; - } - } - - *poly += i; - *degree -= i; -} - -/// Calculate a finite upper bound on the roots of a normalized polynomial. -static inline double -dmnsn_root_bound(const double poly[], size_t degree) -{ - double bound = fabs(poly[0]); - for (size_t i = 1; i < degree; ++i) { - bound = dmnsn_max(bound, fabs(poly[i])); - } - bound += 1.0; - return bound; -} - -/// Copy a polynomial. -static inline void -dmnsn_polynomial_copy(double dest[], const double src[], size_t degree) -{ - for (size_t i = 0; i <= degree; ++i) { - dest[i] = src[i]; - } -} - -/// Transform a polynomial by P'(x) = P(x + 1). -static inline void -dmnsn_polynomial_translate(double poly[], size_t degree) -{ - for (size_t i = 0; i <= degree; ++i) { - for (size_t j = degree - i; j <= degree - 1; ++j) { - poly[j] += poly[j + 1]; - } - } -} - -/// Transform a polynomial by P'(x) = P(c*x). -static inline void -dmnsn_polynomial_scale(double poly[], size_t degree, double c) -{ - double factor = c; - for (size_t i = 1; i <= degree; ++i) { - poly[i] *= factor; - factor *= c; - } -} - -/// Returns the result of Descartes' rule on x^degree * poly(1/(x + 1)). -static size_t -dmnsn_descartes_bound(const double poly[], size_t degree) -{ - // Copy the polynomial so we can be destructive - double p[degree + 1]; - dmnsn_polynomial_copy(p, poly, degree); - - // Calculate poly(1/(1/x + 1)) which avoids reversal - for (size_t i = 1; i <= degree; ++i) { - for (size_t j = i; j >= 1; --j) { - p[j] += p[j - 1]; - } - } - - // Find the number of sign changes in p[] - size_t changes = 0; - int lastsign = dmnsn_sgn(p[0]); - for (size_t i = 1; changes <= 1 && i <= degree; ++i) { - int sign = dmnsn_sgn(p[i]); - if (sign != 0 && sign != lastsign) { - ++changes; - lastsign = sign; - } - } - - return changes; -} - -/// Depth-first search of possible isolating intervals. -static size_t -dmnsn_root_bounds_recursive(double poly[], size_t degree, double *c, double *k, - double bounds[][2], size_t nbounds) -{ - size_t s = dmnsn_descartes_bound(poly, degree); - if (s >= 2) { - // Get the left child - dmnsn_polynomial_scale(poly, degree, 1.0/2.0); - *c *= 2.0; - *k /= 2.0; - double currc = *c, currk = *k; - - // Test the left child - size_t n = dmnsn_root_bounds_recursive(poly, degree, c, k, bounds, nbounds); - if (nbounds == n) { - return n; - } - bounds += n; - nbounds -= n; - - // Get the right child from the last tested polynomial - dmnsn_polynomial_translate(poly, degree); - dmnsn_polynomial_scale(poly, degree, currk/(*k)); - *c = currc + 1.0; - *k = currk; - - // Test the right child - n += dmnsn_root_bounds_recursive(poly, degree, c, k, bounds, nbounds); - return n; - } else if (s == 1) { - bounds[0][0] = (*c)*(*k); - bounds[0][1] = (*c + 1.0)*(*k); - return 1; - } else { - return 0; - } -} - -/// Find ranges that contain a single root. -static size_t -dmnsn_root_bounds(const double poly[], size_t degree, double bounds[][2], - size_t nbounds) -{ - // Copy the polynomial so we can be destructive - double p[degree + 1]; - dmnsn_polynomial_copy(p, poly, degree); - - // Scale the roots to within (0, 1] - double bound = dmnsn_root_bound(p, degree); - dmnsn_polynomial_scale(p, degree, bound); - - // Bounding intervals are of the form (c*k, (c + 1)*k) - double c = 0.0, k = 1.0; - - // Isolate the roots - size_t n = dmnsn_root_bounds_recursive(p, degree, &c, &k, bounds, nbounds); - - // Scale the roots back to within (0, bound] - for (size_t i = 0; i < n; ++i) { - bounds[i][0] *= bound; - bounds[i][1] *= bound; - } - - return n; -} - -/// Maximum number of iterations in dmnsn_bisect_root() before bailout. -#define DMNSN_BISECT_ITERATIONS 64 - -/// Use the false position method to find a root in a range that contains -/// exactly one root. -static inline double -dmnsn_bisect_root(const double poly[], size_t degree, double min, double max) -{ - double evmin = dmnsn_polynomial_evaluate(poly, degree, min); - double evmax = dmnsn_polynomial_evaluate(poly, degree, max); - - // Handle equal bounds, and equal values at the bounds. - if (dmnsn_unlikely(fabs(evmax - evmin) < dmnsn_epsilon)) { - return (min + max)/2.0; - } - - double evinitial = dmnsn_min(fabs(evmin), fabs(evmax)); - double mid, evmid; - int lastsign = 0; - - for (size_t i = 0; i < DMNSN_BISECT_ITERATIONS; ++i) { - mid = (min*evmax - max*evmin)/(evmax - evmin); - evmid = dmnsn_polynomial_evaluate(poly, degree, mid); - int sign = dmnsn_sgn(evmid); - - if ((fabs(evmid) < fabs(mid)*dmnsn_epsilon - // This condition improves stability when one of the bounds is close to - // a different root than we are trying to find - && fabs(evmid) <= evinitial) - || max - min < fabs(mid)*dmnsn_epsilon) - { - break; - } - - if (mid < min) { - // This can happen due to numerical instability in the root bounding - // algorithm, so behave like the normal secant method - max = min; - evmax = evmin; - min = mid; - evmin = evmid; - } else if (mid > max) { - min = max; - evmin = evmax; - max = mid; - evmax = evmid; - } else if (sign == dmnsn_sgn(evmax)) { - max = mid; - evmax = evmid; - if (sign == lastsign) { - // Don't allow the algorithm to keep the same endpoint for three - // iterations in a row; this ensures superlinear convergence - evmin /= 2.0; - } - } else { - min = mid; - evmin = evmid; - if (sign == lastsign) { - evmax /= 2.0; - } - } - - lastsign = sign; - } - - return mid; -} - -/// Use synthetic division to eliminate the root \p r from \p poly[]. -static inline size_t -dmnsn_eliminate_root(double poly[], size_t degree, double r) -{ - double rem = poly[degree]; - for (size_t i = degree; i-- > 0;) { - double temp = poly[i]; - poly[i] = rem; - rem = temp + r*rem; - } - return degree - 1; -} - -/// Solve a normalized linear polynomial algebraically. -static inline size_t -dmnsn_solve_linear(const double poly[2], double x[1]) -{ - x[0] = -poly[0]; - if (x[0] >= dmnsn_epsilon) - return 1; - else - return 0; -} - -/// Solve a normalized quadratic polynomial algebraically. -static inline size_t -dmnsn_solve_quadratic(const double poly[3], double x[2]) -{ - double disc = poly[1]*poly[1] - 4.0*poly[0]; - if (disc >= 0.0) { - double s = sqrt(disc); - x[0] = (-poly[1] + s)/2.0; - x[1] = (-poly[1] - s)/2.0; - - if (x[1] >= dmnsn_epsilon) - return 2; - else if (x[0] >= dmnsn_epsilon) - return 1; - else - return 0; - } else { - return 0; - } -} - -/// Solve a normalized cubic polynomial algebraically. -static inline size_t -dmnsn_solve_cubic(double poly[4], double x[3]) -{ - // Reduce to a monic trinomial (t^3 + p*t + q, t = x + b/3) - double b2 = poly[2]*poly[2]; - double p = poly[1] - b2/3.0; - double q = poly[0] - poly[2]*(9.0*poly[1] - 2.0*b2)/27.0; - - double disc = 4.0*p*p*p + 27.0*q*q; - double bdiv3 = poly[2]/3.0; - - if (disc < 0.0) { - // Three real roots -- this implies p < 0 - double msqrtp3 = -sqrt(-p/3.0); - double theta = acos(3*q/(2*p*msqrtp3))/3.0; - - // Store the roots in order from largest to smallest - x[2] = 2.0*msqrtp3*cos(theta) - bdiv3; - x[0] = -2.0*msqrtp3*cos(4.0*atan(1.0)/3.0 - theta) - bdiv3; - x[1] = -(x[0] + x[2] + poly[2]); - - if (x[2] >= dmnsn_epsilon) - return 3; - else if (x[1] >= dmnsn_epsilon) - return 2; - } else if (disc > 0.0) { - // One real root - double cbrtdiscq = cbrt(sqrt(disc/108.0) + fabs(q)/2.0); - double abst = cbrtdiscq - p/(3.0*cbrtdiscq); - - if (q >= 0) { - x[0] = -abst - bdiv3; - } else { - x[0] = abst - bdiv3; - } - } else if (fabs(p) < dmnsn_epsilon) { - // Equation is a perfect cube - x[0] = -bdiv3; - } else { - // Two real roots; one duplicate - double t1 = -(3.0*q)/(2.0*p), t2 = -2.0*t1; - x[0] = dmnsn_max(t1, t2) - bdiv3; - x[1] = dmnsn_min(t1, t2) - bdiv3; - if (x[1] >= dmnsn_epsilon) - return 2; - } - - if (x[0] >= dmnsn_epsilon) - return 1; - else - return 0; -} - -// Solve a polynomial -DMNSN_HOT size_t -dmnsn_polynomial_solve(const double poly[], size_t degree, double x[]) -{ - // Copy the polynomial so we can be destructive - double copy[degree + 1], *p = copy; - dmnsn_polynomial_copy(p, poly, degree); - - // Index into x[] - size_t i = 0; - - // Account for leading zero coefficients - degree = dmnsn_real_degree(p, degree); - // Normalize the leading coefficient to 1.0 - dmnsn_polynomial_normalize(p, degree); - // Eliminate simple zero roots - dmnsn_eliminate_zero_roots(&p, °ree); - - static const size_t max_algebraic = 3; - if (degree > max_algebraic) { - // Find isolating intervals for (degree - max_algebraic) roots of p[] - double ranges[degree - max_algebraic][2]; - size_t n = dmnsn_root_bounds(p, degree, ranges, degree - max_algebraic); - - for (size_t j = 0; j < n; ++j) { - // Bisect within the found range - double r = dmnsn_bisect_root(p, degree, ranges[j][0], ranges[j][1]); - - // Use synthetic division to eliminate the root `r' - degree = dmnsn_eliminate_root(p, degree, r); - - // Store the found root - x[i] = r; - ++i; - } - } - - switch (degree) { - case 1: - i += dmnsn_solve_linear(p, x + i); - break; - case 2: - i += dmnsn_solve_quadratic(p, x + i); - break; - case 3: - i += dmnsn_solve_cubic(p, x + i); - break; - } - - return i; -} - -// Print a polynomial -void -dmnsn_polynomial_print(FILE *file, const double poly[], size_t degree) -{ - for (size_t i = degree + 1; i-- > 0;) { - if (i < degree) { - fprintf(file, (poly[i] >= 0.0) ? " + " : " - "); - } - fprintf(file, "%.17g", fabs(poly[i])); - if (i >= 2) { - fprintf(file, "*x^%zu", i); - } else if (i == 1) { - fprintf(file, "*x"); - } - } -} diff --git a/libdimension/pool.c b/libdimension/pool.c deleted file mode 100644 index a7d6b4f..0000000 --- a/libdimension/pool.c +++ /dev/null @@ -1,176 +0,0 @@ -/************************************************************************* - * Copyright (C) 2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Memory pool implementation. - */ - -#include "dimension-internal.h" -#include - -/// Number of pointers per block, we want a block to fit in a single page. -#define DMNSN_POOL_BLOCK_SIZE (4096/sizeof(void *) - 4) -/// Number of pointers per tidy block -#define DMNSN_TIDY_BLOCK_SIZE ((4096 - 4*sizeof(void *))/(sizeof(void *) + sizeof(dmnsn_callback_fn *))) - -/// A single block in a thread-specific pool. -typedef struct dmnsn_pool_block { - /// Current index into allocs[]. - size_t i; - /// All allocations in the current block. - void *allocs[DMNSN_POOL_BLOCK_SIZE]; - /// Tail pointer to the previous block in the global chain. - struct dmnsn_pool_block *prev; -} dmnsn_pool_block; - -/// A single tidy block in a thread-specific pool. -typedef struct dmnsn_tidy_block { - /// Current index into allocs[]. - size_t i; - /// All allocations in the current block. - void *allocs[DMNSN_TIDY_BLOCK_SIZE]; - /// All cleanup callbacks in the current block. - dmnsn_callback_fn *cleanup_fns[DMNSN_TIDY_BLOCK_SIZE]; - /// Tail pointer to the previous tidy block in the global chain. - struct dmnsn_tidy_block *prev; -} dmnsn_tidy_block; - -/// dmnsn_pool implementation. -struct dmnsn_pool { - /// Thread-local regular block. - pthread_key_t thread_block; - /// Thread-local tidy block. - pthread_key_t thread_tidy_block; - - /// Global chain of regular blocks. - atomic(dmnsn_pool_block *) chain; - /// Global chain of tidy blocks. - atomic(dmnsn_tidy_block *) tidy_chain; -}; - -dmnsn_pool * -dmnsn_new_pool(void) -{ - dmnsn_pool *pool = DMNSN_MALLOC(dmnsn_pool); - - dmnsn_key_create(&pool->thread_block, NULL); - dmnsn_key_create(&pool->thread_tidy_block, NULL); - - atomic_store_explicit(&pool->chain, NULL, memory_order_relaxed); - atomic_store_explicit(&pool->tidy_chain, NULL, memory_order_relaxed); - - return pool; -} - -void * -dmnsn_palloc(dmnsn_pool *pool, size_t size) -{ - dmnsn_pool_block *old_block = pthread_getspecific(pool->thread_block); - - dmnsn_pool_block *new_block = old_block; - if (dmnsn_unlikely(!old_block || old_block->i == DMNSN_POOL_BLOCK_SIZE)) { - new_block = DMNSN_MALLOC(dmnsn_pool_block); - new_block->i = 0; - } - - void *result = dmnsn_malloc(size); - new_block->allocs[new_block->i++] = result; - - if (dmnsn_unlikely(new_block != old_block)) { - dmnsn_setspecific(pool->thread_block, new_block); - - // Atomically update pool->chain - dmnsn_pool_block *old_chain = atomic_exchange(&pool->chain, new_block); - new_block->prev = old_chain; - } - - return result; -} - -void * -dmnsn_palloc_tidy(dmnsn_pool *pool, size_t size, dmnsn_callback_fn *cleanup_fn) -{ - dmnsn_assert(cleanup_fn != NULL, "NULL cleanup_fn"); - - dmnsn_tidy_block *old_block = pthread_getspecific(pool->thread_tidy_block); - - dmnsn_tidy_block *new_block = old_block; - if (dmnsn_unlikely(!old_block || old_block->i == DMNSN_TIDY_BLOCK_SIZE)) { - new_block = DMNSN_MALLOC(dmnsn_tidy_block); - new_block->i = 0; - } - - void *result = dmnsn_malloc(size); - - size_t i = new_block->i; - new_block->allocs[i] = result; - new_block->cleanup_fns[i] = cleanup_fn; - ++new_block->i; - - if (dmnsn_unlikely(new_block != old_block)) { - dmnsn_setspecific(pool->thread_tidy_block, new_block); - - // Atomically update pool->tidy_chain - dmnsn_tidy_block *old_chain = atomic_exchange(&pool->tidy_chain, new_block); - new_block->prev = old_chain; - } - - return result; -} - -void -dmnsn_delete_pool(dmnsn_pool *pool) -{ - if (!pool) { - return; - } - - dmnsn_pool_block *block = atomic_load_explicit(&pool->chain, memory_order_relaxed); - while (block) { - // Free all the allocations - for (size_t i = block->i; i-- > 0;) { - dmnsn_free(block->allocs[i]); - } - - // Free the block itself and go to the previous one - dmnsn_pool_block *saved = block; - block = block->prev; - dmnsn_free(saved); - } - - dmnsn_tidy_block *tidy_block = atomic_load_explicit(&pool->tidy_chain, memory_order_relaxed); - while (tidy_block) { - // Free all the allocations - for (size_t i = tidy_block->i; i-- > 0;) { - void *ptr = tidy_block->allocs[i]; - tidy_block->cleanup_fns[i](ptr); - dmnsn_free(ptr); - } - - // Free the block itself and go to the previous one - dmnsn_tidy_block *saved = tidy_block; - tidy_block = tidy_block->prev; - dmnsn_free(saved); - } - - dmnsn_key_delete(pool->thread_tidy_block); - dmnsn_free(pool); -} diff --git a/libdimension/profile.c b/libdimension/profile.c deleted file mode 100644 index 164e002..0000000 --- a/libdimension/profile.c +++ /dev/null @@ -1,167 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2011 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Branch profile accounting. - */ - -#include "dimension-internal.h" -#include -#include -#include - -/// Information on one predicted branch. -typedef struct { - char *location; - uint64_t predicted, branches; -} dmnsn_branch; - -/// Count of mispredicted branches. -static dmnsn_dictionary *dmnsn_profile = NULL; -/// Mutex which protects \c dmnsn_profile. -static pthread_mutex_t dmnsn_profile_mutex = PTHREAD_MUTEX_INITIALIZER; - -/// Thread-local count of mispredicted branches. -static pthread_key_t dmnsn_thread_profile; -/// Initialize the thread-specific pointer exactly once. -static pthread_once_t dmnsn_thread_profile_once = PTHREAD_ONCE_INIT; - -/// Add thread-specific profile data to the global counts. -static void -dmnsn_profile_globalize(void *ptr) -{ - dmnsn_branch *branch = ptr; - dmnsn_branch *global = dmnsn_dictionary_at(dmnsn_profile, branch->location); - if (global) { - global->predicted += branch->predicted; - global->branches += branch->branches; - dmnsn_free(branch->location); - } else { - dmnsn_dictionary_insert(dmnsn_profile, branch->location, branch); - } -} - -/// Destructor function for thread-specific profile data. -static void -dmnsn_delete_thread_profile(void *ptr) -{ - dmnsn_dictionary *thread_profile = ptr; - - dmnsn_lock_mutex(&dmnsn_profile_mutex); - dmnsn_dictionary_apply(thread_profile, dmnsn_profile_globalize); - dmnsn_unlock_mutex(&dmnsn_profile_mutex); - - dmnsn_delete_dictionary(thread_profile); -} - -/// Initialize the thread-specific pointer. -static void -dmnsn_initialize_thread_profile(void) -{ - dmnsn_key_create(&dmnsn_thread_profile, dmnsn_delete_thread_profile); - - dmnsn_lock_mutex(&dmnsn_profile_mutex); - dmnsn_profile = dmnsn_new_dictionary(sizeof(dmnsn_branch)); - dmnsn_unlock_mutex(&dmnsn_profile_mutex); -} - -/// Get the thread-specific profile data. -static dmnsn_dictionary * -dmnsn_get_thread_profile(void) -{ - dmnsn_once(&dmnsn_thread_profile_once, dmnsn_initialize_thread_profile); - return pthread_getspecific(dmnsn_thread_profile); -} - -/// Set the thread-specific profile data. -static void -dmnsn_set_thread_profile(dmnsn_dictionary *thread_profile) -{ - dmnsn_setspecific(dmnsn_thread_profile, thread_profile); -} - -bool -dmnsn_expect(bool result, bool expected, const char *func, const char *file, - unsigned int line) -{ - int size = snprintf(NULL, 0, "%s:%s:%u", file, func, line) + 1; - if (size < 1) { - dmnsn_error("sprintf() failed."); - } - - char key[size]; - if (snprintf(key, size, "%s:%s:%u", file, func, line) < 0) { - dmnsn_error("sprintf() failed."); - } - - dmnsn_dictionary *thread_profile = dmnsn_get_thread_profile(); - if (!thread_profile) { - thread_profile = dmnsn_new_dictionary(sizeof(dmnsn_branch)); - dmnsn_set_thread_profile(thread_profile); - } - - dmnsn_branch *branch = dmnsn_dictionary_at(thread_profile, key); - if (branch) { - ++branch->branches; - if (result == expected) { - ++branch->predicted; - } - } else { - dmnsn_branch new_branch = { - .location = dmnsn_strdup(key), - .predicted = (result == expected) ? 1 : 0, - .branches = 1 - }; - dmnsn_dictionary_insert(thread_profile, key, &new_branch); - } - - return result; -} - -static void -dmnsn_print_bad_prediction(void *ptr) -{ - dmnsn_branch *branch = ptr; - double rate = ((double)branch->predicted)/branch->branches; - if (rate < 0.75 || branch->branches < 100000) { - fprintf(stderr, - "Bad branch prediction: %s: %" PRIu64 "/%" PRIu64 " (%g%%)\n", - branch->location, branch->predicted, branch->branches, 100.0*rate); - } - - dmnsn_free(branch->location); -} - -DMNSN_DESTRUCTOR static void -dmnsn_print_bad_predictions(void) -{ - dmnsn_dictionary *thread_profile = dmnsn_get_thread_profile(); - if (thread_profile) { - dmnsn_delete_thread_profile(thread_profile); - dmnsn_set_thread_profile(NULL); - } - - dmnsn_lock_mutex(&dmnsn_profile_mutex); - dmnsn_dictionary_apply(dmnsn_profile, dmnsn_print_bad_prediction); - dmnsn_delete_dictionary(dmnsn_profile); - dmnsn_profile = NULL; - dmnsn_unlock_mutex(&dmnsn_profile_mutex); -} diff --git a/libdimension/profile.h b/libdimension/profile.h deleted file mode 100644 index 4817482..0000000 --- a/libdimension/profile.h +++ /dev/null @@ -1,39 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2011 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Built-in branch profiler. - */ - -#include - -/** - * Record a test and its expected result. Called by dmnsn_[un]likely(); - * don't call directly. - * @param[in] result The result of the test. - * @param[in] expected The expected result of the test. - * @param[in] func The name of the function the test occurs in. - * @param[in] file The name of the file the test occurs in. - * @param[in] line The line number on which the test occurs. - * @return \p result. - */ -DMNSN_INTERNAL bool dmnsn_expect(bool result, bool expected, const char *func, - const char *file, unsigned int line); diff --git a/libdimension/prtree.c b/libdimension/prtree.c deleted file mode 100644 index bb7beec..0000000 --- a/libdimension/prtree.c +++ /dev/null @@ -1,370 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Priority R-tree implementation. - */ - -#include "dimension-internal.h" -#include - -/// Number of children per PR-node. -#define DMNSN_PRTREE_B 8 -/// Number of priority leaves per pseudo-PR-node (must be 2*ndimensions). -#define DMNSN_PSEUDO_B 6 - -/// The side of the split that a node ended up on. -typedef enum dmnsn_prnode_color { - DMNSN_PRTREE_LEAF, ///< Priority leaf. - DMNSN_PRTREE_LEFT, ///< Left child. - DMNSN_PRTREE_RIGHT ///< Right child. -} dmnsn_prnode_color; - -/** - * A BVH node with associated color. Compared to storing the color in the - * \p dmnsn_bvh_node itself, this method has decreased cache performance during - * sorting (due to an extra pointer chase), but increased performance during - * tree building (because it's much smaller than a full \p dmnsn_bvh_node). - * Overall it gives about a 25% improvement. - */ -typedef struct dmnsn_colored_prnode { - dmnsn_prnode_color color; - dmnsn_bvh_node *node; -} dmnsn_colored_prnode; - -/// Construct an empty PR-node. -static inline dmnsn_bvh_node * -dmnsn_new_prnode(void) -{ - return dmnsn_new_bvh_node(DMNSN_PRTREE_B); -} - -/// Comparator types. -enum { - DMNSN_XMIN, - DMNSN_YMIN, - DMNSN_ZMIN, - DMNSN_XMAX, - DMNSN_YMAX, - DMNSN_ZMAX -}; - -// List sorting comparators - -static int -dmnsn_xmin_comp(const void *l, const void *r) -{ - double lval = (*(const dmnsn_colored_prnode **)l)->node->bounding_box.min.x; - double rval = (*(const dmnsn_colored_prnode **)r)->node->bounding_box.min.x; - return (lval > rval) - (lval < rval); -} - -static int -dmnsn_ymin_comp(const void *l, const void *r) -{ - double lval = (*(const dmnsn_colored_prnode **)l)->node->bounding_box.min.y; - double rval = (*(const dmnsn_colored_prnode **)r)->node->bounding_box.min.y; - return (lval > rval) - (lval < rval); -} - -static int -dmnsn_zmin_comp(const void *l, const void *r) -{ - double lval = (*(const dmnsn_colored_prnode **)l)->node->bounding_box.min.z; - double rval = (*(const dmnsn_colored_prnode **)r)->node->bounding_box.min.z; - return (lval > rval) - (lval < rval); -} - -static int -dmnsn_xmax_comp(const void *l, const void *r) -{ - double lval = (*(const dmnsn_colored_prnode **)l)->node->bounding_box.max.x; - double rval = (*(const dmnsn_colored_prnode **)r)->node->bounding_box.max.x; - return (lval < rval) - (lval > rval); -} - -static int -dmnsn_ymax_comp(const void *l, const void *r) -{ - double lval = (*(const dmnsn_colored_prnode **)l)->node->bounding_box.max.y; - double rval = (*(const dmnsn_colored_prnode **)r)->node->bounding_box.max.y; - return (lval < rval) - (lval > rval); -} - -static int -dmnsn_zmax_comp(const void *l, const void *r) -{ - double lval = (*(const dmnsn_colored_prnode **)l)->node->bounding_box.max.z; - double rval = (*(const dmnsn_colored_prnode **)r)->node->bounding_box.max.z; - return (lval < rval) - (lval > rval); -} - -/// All comparators. -static dmnsn_array_comparator_fn *const dmnsn_comparators[DMNSN_PSEUDO_B] = { - [DMNSN_XMIN] = dmnsn_xmin_comp, - [DMNSN_YMIN] = dmnsn_ymin_comp, - [DMNSN_ZMIN] = dmnsn_zmin_comp, - [DMNSN_XMAX] = dmnsn_xmax_comp, - [DMNSN_YMAX] = dmnsn_ymax_comp, - [DMNSN_ZMAX] = dmnsn_zmax_comp, -}; - -/// Add the priority leaves for this level. -static void -dmnsn_add_priority_leaves(dmnsn_colored_prnode **sorted_leaves[DMNSN_PSEUDO_B], - size_t start, size_t end, - dmnsn_array *new_leaves) -{ - for (size_t i = 0; i < DMNSN_PSEUDO_B; ++i) { - dmnsn_bvh_node *leaf = NULL; - dmnsn_colored_prnode **leaves = sorted_leaves[i]; - - for (size_t j = start; - j < end && (!leaf || leaf->nchildren < DMNSN_PRTREE_B); - ++j) { - // Skip all the previously found extreme nodes - if (leaves[j]->color == DMNSN_PRTREE_LEAF) { - continue; - } - - if (!leaf) { - leaf = dmnsn_new_prnode(); - } - leaves[j]->color = DMNSN_PRTREE_LEAF; - dmnsn_bvh_node_add(leaf, leaves[j]->node); - } - - if (leaf) { - dmnsn_array_push(new_leaves, &leaf); - } else { - return; - } - } -} - -/// Get rid of the extreme nodes. -static void -dmnsn_filter_priority_leaves(dmnsn_colored_prnode **leaves, size_t start, size_t *endp) -{ - size_t i, skip, end; - for (i = start, skip = 0, end = *endp; i < end; ++i) { - if (leaves[i]->color == DMNSN_PRTREE_LEAF) { - ++skip; - } else { - leaves[i - skip] = leaves[i]; - } - } - - *endp -= skip; -} - -/// Split the leaves and mark the left and right child nodes. -static void -dmnsn_split_sorted_leaves_easy(dmnsn_colored_prnode **leaves, size_t start, size_t *midp, size_t end) -{ - size_t i, mid = start + (end - start + 1)/2; - for (i = start; i < mid; ++i) { - leaves[i]->color = DMNSN_PRTREE_LEFT; - } - for (; i < end; ++i) { - leaves[i]->color = DMNSN_PRTREE_RIGHT; - } - - *midp = mid; -} - -/// Split the leaves using the coloring from dmnsn_split_sorted_leaves_easy(). -static void -dmnsn_split_sorted_leaves_hard(dmnsn_colored_prnode **leaves, dmnsn_colored_prnode **buffer, size_t start, size_t end) -{ - size_t i, j, skip; - for (i = start, j = 0, skip = 0; i < end; ++i) { - if (leaves[i]->color == DMNSN_PRTREE_LEFT) { - leaves[i - skip] = leaves[i]; - } else { - if (leaves[i]->color == DMNSN_PRTREE_RIGHT) { - buffer[j] = leaves[i]; - ++j; - } - ++skip; - } - } - - size_t mid = i - skip; - for (i = 0; i < j; ++i) { - leaves[mid + i] = buffer[i]; - } -} - -/// Split the sorted lists into the left and right subtrees. -static void -dmnsn_split_sorted_leaves(dmnsn_colored_prnode **sorted_leaves[DMNSN_PSEUDO_B], - size_t start, size_t *midp, size_t *endp, - dmnsn_colored_prnode **buffer, int i) -{ - size_t orig_end = *endp; - - // Filter the extreme nodes in the ith list - dmnsn_filter_priority_leaves(sorted_leaves[i], start, endp); - - // Split the ith list - dmnsn_split_sorted_leaves_easy(sorted_leaves[i], start, midp, *endp); - - // Split the rest of the lists - for (size_t j = 0; j < DMNSN_PSEUDO_B; ++j) { - if (j == i) { - continue; - } - - dmnsn_split_sorted_leaves_hard(sorted_leaves[j], buffer, start, orig_end); - } -} - -/// Recursively constructs an implicit pseudo-PR-tree and collects the priority -/// leaves. -static void -dmnsn_priority_leaves_recursive(dmnsn_colored_prnode **sorted_leaves[DMNSN_PSEUDO_B], - size_t start, size_t end, - dmnsn_colored_prnode **buffer, - dmnsn_array *new_leaves, - int comparator) -{ - dmnsn_add_priority_leaves(sorted_leaves, start, end, new_leaves); - - size_t mid; - dmnsn_split_sorted_leaves(sorted_leaves, start, &mid, &end, buffer, comparator); - - int next = (comparator + 1)%DMNSN_PSEUDO_B; - - if (start < mid) { - dmnsn_priority_leaves_recursive(sorted_leaves, start, mid, buffer, new_leaves, next); - } - - if (mid < end) { - dmnsn_priority_leaves_recursive(sorted_leaves, mid, end, buffer, new_leaves, next); - } -} - -/// Sort each dimension in parallel with more than this many leaves. -#define DMNSN_PARALLEL_SORT_THRESHOLD 1024 - -typedef struct { - dmnsn_colored_prnode *colored_leaves; - dmnsn_colored_prnode ***sorted_leaves; - size_t nleaves; -} dmnsn_sort_leaves_payload; - -static dmnsn_colored_prnode ** -dmnsn_sort_leaf_array(dmnsn_colored_prnode *colored_leaves, size_t nleaves, int comparator) -{ - dmnsn_colored_prnode **sorted_leaves = dmnsn_malloc(nleaves*sizeof(dmnsn_colored_prnode *)); - - for (size_t i = 0; i < nleaves; ++i) { - sorted_leaves[i] = colored_leaves + i; - } - - qsort(sorted_leaves, nleaves, sizeof(dmnsn_colored_prnode *), dmnsn_comparators[comparator]); - - return sorted_leaves; -} - -static int -dmnsn_sort_leaves(void *ptr, unsigned int thread, unsigned int nthreads) -{ - dmnsn_sort_leaves_payload *payload = ptr; - - for (unsigned int i = thread; i < DMNSN_PSEUDO_B; i += nthreads) { - payload->sorted_leaves[i] = dmnsn_sort_leaf_array(payload->colored_leaves, payload->nleaves, i); - } - - return 0; -} - -/// Constructs an implicit pseudo-PR-tree and returns the priority leaves. -static dmnsn_array * -dmnsn_priority_leaves(const dmnsn_array *leaves, unsigned int nthreads) -{ - dmnsn_bvh_node **leaves_arr = dmnsn_array_first(leaves); - size_t nleaves = dmnsn_array_size(leaves); - - dmnsn_colored_prnode *colored_leaves = dmnsn_malloc(nleaves*sizeof(dmnsn_colored_prnode)); - for (size_t i = 0; i < nleaves; ++i) { - colored_leaves[i].color = DMNSN_PRTREE_LEFT; // Mustn't be _LEAF - colored_leaves[i].node = leaves_arr[i]; - } - - dmnsn_colored_prnode **sorted_leaves[DMNSN_PSEUDO_B]; - - if (nleaves >= DMNSN_PARALLEL_SORT_THRESHOLD && nthreads > 1) { - dmnsn_sort_leaves_payload payload = { - .colored_leaves = colored_leaves, - .sorted_leaves = sorted_leaves, - .nleaves = nleaves, - }; - dmnsn_execute_concurrently(NULL, dmnsn_sort_leaves, &payload, nthreads); - } else { - for (size_t i = 0; i < DMNSN_PSEUDO_B; ++i) { - sorted_leaves[i] = dmnsn_sort_leaf_array(colored_leaves, nleaves, i); - } - } - - size_t buffer_size = nleaves/2; - dmnsn_colored_prnode **buffer = dmnsn_malloc(buffer_size*sizeof(dmnsn_colored_prnode *)); - - dmnsn_array *new_leaves = DMNSN_NEW_ARRAY(dmnsn_bvh_node *); - - dmnsn_priority_leaves_recursive(sorted_leaves, 0, nleaves, buffer, new_leaves, 0); - - dmnsn_free(buffer); - for (size_t i = 0; i < DMNSN_PSEUDO_B; ++i) { - dmnsn_free(sorted_leaves[i]); - } - dmnsn_free(colored_leaves); - - return new_leaves; -} - -dmnsn_bvh_node * -dmnsn_new_prtree(const dmnsn_array *objects) -{ - if (dmnsn_array_size(objects) == 0) { - return NULL; - } - - // Make the initial array of leaves - dmnsn_array *leaves = DMNSN_NEW_ARRAY(dmnsn_bvh_node *); - DMNSN_ARRAY_FOREACH (dmnsn_object **, object, objects) { - dmnsn_bvh_node *node = dmnsn_new_bvh_leaf_node(*object); - dmnsn_array_push(leaves, &node); - } - - unsigned int ncpus = dmnsn_ncpus(); - unsigned int nthreads = ncpus < DMNSN_PSEUDO_B ? ncpus : DMNSN_PSEUDO_B; - while (dmnsn_array_size(leaves) > 1) { - dmnsn_array *new_leaves = dmnsn_priority_leaves(leaves, nthreads); - dmnsn_delete_array(leaves); - leaves = new_leaves; - } - - dmnsn_bvh_node *root = *(dmnsn_bvh_node **)dmnsn_array_first(leaves); - dmnsn_delete_array(leaves); - return root; -} diff --git a/libdimension/prtree.h b/libdimension/prtree.h deleted file mode 100644 index 76a4051..0000000 --- a/libdimension/prtree.h +++ /dev/null @@ -1,30 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2012 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file. - * Priority R-trees. PR-trees are a data structure introduced by Arge, de Berg, - * Haverkort, and Yi, which provides asymptotically optimal worst-case lookup, - * while remaining efficient with real-world data. Their structure is derived - * from B-trees. - */ - -/// Create a PR-tree. -DMNSN_INTERNAL dmnsn_bvh_node *dmnsn_new_prtree(const dmnsn_array *objects); diff --git a/libdimension/ray_trace.c b/libdimension/ray_trace.c deleted file mode 100644 index 6f4ce33..0000000 --- a/libdimension/ray_trace.c +++ /dev/null @@ -1,547 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * The ray-tracing algorithm. - */ - -#include "dimension-internal.h" -#include - -//////////////////////////////////// -// Boilerplate for multithreading // -//////////////////////////////////// - -/// Payload type for passing arguments to worker threads. -typedef struct { - dmnsn_future *future; - dmnsn_scene *scene; - dmnsn_bvh *bvh; -} dmnsn_ray_trace_payload; - -// Ray-trace a scene -void -dmnsn_ray_trace(dmnsn_scene *scene) -{ - dmnsn_future *future = dmnsn_ray_trace_async(scene); - if (dmnsn_future_join(future) != 0) { - dmnsn_error("Error occured while ray-tracing."); - } -} - -/// Background thread callback. -static int dmnsn_ray_trace_scene_thread(void *ptr); - -// Ray-trace a scene in the background -dmnsn_future * -dmnsn_ray_trace_async(dmnsn_scene *scene) -{ - dmnsn_future *future = dmnsn_new_future(); - - dmnsn_ray_trace_payload *payload = DMNSN_MALLOC(dmnsn_ray_trace_payload); - payload->future = future; - payload->scene = scene; - - dmnsn_new_thread(future, dmnsn_ray_trace_scene_thread, payload); - - return future; -} - -/// Worker thread callback. -static int dmnsn_ray_trace_scene_concurrent(void *ptr, unsigned int thread, - unsigned int nthreads); - -// Thread callback -- set up the multithreaded engine -static int -dmnsn_ray_trace_scene_thread(void *ptr) -{ - dmnsn_ray_trace_payload *payload = ptr; - - // Pre-calculate bounding box transformations, etc. - dmnsn_scene_initialize(payload->scene); - - // Time the bounding tree construction - dmnsn_timer_start(&payload->scene->bounding_timer); - payload->bvh = dmnsn_new_bvh(payload->scene->objects, DMNSN_BVH_PRTREE); - dmnsn_timer_stop(&payload->scene->bounding_timer); - - // Set up the future object - dmnsn_future_set_total(payload->future, payload->scene->canvas->height); - - // Time the render itself - dmnsn_timer_start(&payload->scene->render_timer); - int ret = dmnsn_execute_concurrently(payload->future, - dmnsn_ray_trace_scene_concurrent, - payload, payload->scene->nthreads); - dmnsn_timer_stop(&payload->scene->render_timer); - - dmnsn_delete_bvh(payload->bvh); - dmnsn_free(payload); - - return ret; -} - -/////////////////////////// -// Ray-tracing algorithm // -/////////////////////////// - -/// The current state of the ray-tracing engine. -typedef struct dmnsn_rtstate { - const struct dmnsn_rtstate *parent; - - const dmnsn_scene *scene; - const dmnsn_intersection *intersection; - const dmnsn_texture *texture; - const dmnsn_interior *interior; - const dmnsn_bvh *bvh; - unsigned int reclevel; - - dmnsn_vector r; - dmnsn_vector pigment_r; - dmnsn_vector viewer; - dmnsn_vector reflected; - - bool is_shadow_ray; - dmnsn_vector light_ray; - dmnsn_color light_color; - - dmnsn_tcolor pigment; - dmnsn_tcolor color; - - double ior; - - dmnsn_color adc_value; -} dmnsn_rtstate; - -/// Compute a ray-tracing state from an intersection. -static inline void -dmnsn_rtstate_initialize(dmnsn_rtstate *state, - const dmnsn_intersection *intersection); -/// Main helper for dmnsn_ray_trace_scene_concurrent - shoot a ray. -static dmnsn_tcolor dmnsn_ray_shoot(dmnsn_rtstate *state, dmnsn_line ray); - -// Actually ray-trace a scene -static int -dmnsn_ray_trace_scene_concurrent(void *ptr, unsigned int thread, - unsigned int nthreads) -{ - const dmnsn_ray_trace_payload *payload = ptr; - dmnsn_future *future = payload->future; - dmnsn_scene *scene = payload->scene; - dmnsn_bvh *bvh = payload->bvh; - - dmnsn_rtstate state = { - .parent = NULL, - .scene = scene, - .bvh = bvh, - }; - - // Iterate through each pixel - for (size_t y = thread; y < scene->canvas->height; y += nthreads) { - for (size_t x = 0; x < scene->canvas->width; ++x) { - // Get the ray corresponding to the (x,y)'th pixel - dmnsn_line ray = dmnsn_camera_ray( - scene->camera, - ((double)(x + scene->region_x))/(scene->outer_width - 1), - ((double)(y + scene->region_y))/(scene->outer_height - 1) - ); - - // Shoot a ray - state.reclevel = scene->reclimit; - state.ior = 1.0; - state.adc_value = dmnsn_white; - dmnsn_tcolor tcolor = dmnsn_ray_shoot(&state, ray); - dmnsn_canvas_set_pixel(scene->canvas, x, y, tcolor); - } - - dmnsn_future_increment(future); - } - - return 0; -} - -// Compute rtstate fields -static inline void -dmnsn_rtstate_initialize(dmnsn_rtstate *state, - const dmnsn_intersection *intersection) -{ - state->intersection = intersection; - state->texture = intersection->object->texture; - state->interior = intersection->object->interior; - - state->r = dmnsn_line_point(intersection->ray, intersection->t); - state->pigment_r = dmnsn_transform_point( - intersection->object->pigment_trans, - state->r - ); - state->viewer = dmnsn_vector_normalized( - dmnsn_vector_negate(intersection->ray.n) - ); - state->reflected = dmnsn_vector_sub( - dmnsn_vector_mul( - 2*dmnsn_vector_dot(state->viewer, intersection->normal), - intersection->normal), - state->viewer - ); - - state->is_shadow_ray = false; -} - -/// Calculate the background color. -static void dmnsn_trace_background(dmnsn_rtstate *state, dmnsn_line ray); -/// Calculate the base pigment at the intersection. -static void dmnsn_trace_pigment(dmnsn_rtstate *state); -/// Handle light, shadow, and shading. -static void dmnsn_trace_lighting(dmnsn_rtstate *state); -/// Trace a reflected ray. -static void dmnsn_trace_reflection(dmnsn_rtstate *state); -/// Trace a transmitted ray. -static void dmnsn_trace_transparency(dmnsn_rtstate *state); - -// Shoot a ray, and calculate the color -static dmnsn_tcolor -dmnsn_ray_shoot(dmnsn_rtstate *state, dmnsn_line ray) -{ - if (state->reclevel == 0 - || dmnsn_color_intensity(state->adc_value) < state->scene->adc_bailout) - { - return DMNSN_TCOLOR(dmnsn_black); - } - - --state->reclevel; - - dmnsn_intersection intersection; - bool reset = state->reclevel == state->scene->reclimit - 1; - dmnsn_bvh_intersection(state->bvh, ray, &intersection, reset); - if (dmnsn_bvh_intersection(state->bvh, ray, &intersection, reset)) { - // Found an intersection - dmnsn_rtstate_initialize(state, &intersection); - - dmnsn_trace_pigment(state); - if (state->scene->quality & DMNSN_RENDER_LIGHTS) { - dmnsn_trace_lighting(state); - } - if (state->scene->quality & DMNSN_RENDER_REFLECTION) { - dmnsn_trace_reflection(state); - } - if (state->scene->quality & DMNSN_RENDER_TRANSPARENCY) { - dmnsn_trace_transparency(state); - } - } else { - // No intersection, return the background color - dmnsn_trace_background(state, ray); - } - - return state->color; -} - -static void -dmnsn_trace_background(dmnsn_rtstate *state, dmnsn_line ray) -{ - dmnsn_pigment *background = state->scene->background; - if (state->scene->quality & DMNSN_RENDER_PIGMENT) { - dmnsn_vector r = dmnsn_vector_normalized(ray.n); - state->color = dmnsn_pigment_evaluate(background, r); - } else { - state->color = background->quick_color; - } -} - -static void -dmnsn_trace_pigment(dmnsn_rtstate *state) -{ - dmnsn_pigment *pigment = state->texture->pigment; - if (state->scene->quality & DMNSN_RENDER_PIGMENT) { - state->pigment = dmnsn_pigment_evaluate(pigment, state->pigment_r); - } else { - state->pigment = pigment->quick_color; - } - state->color = state->pigment; -} - -/// Determine the amount of specular highlight. -static inline dmnsn_color -dmnsn_evaluate_specular(const dmnsn_rtstate *state) -{ - const dmnsn_finish *finish = &state->texture->finish; - if (finish->specular) { - return finish->specular->specular_fn( - finish->specular, state->light_color, state->pigment.c, - state->light_ray, state->intersection->normal, state->viewer - ); - } else { - return dmnsn_black; - } -} - -/// Determine the amount of reflected light. -static inline dmnsn_color -dmnsn_evaluate_reflection(const dmnsn_rtstate *state, - dmnsn_color light, dmnsn_vector direction) -{ - const dmnsn_reflection *reflection = state->texture->finish.reflection; - if (reflection && (state->scene->quality & DMNSN_RENDER_REFLECTION)) { - return reflection->reflection_fn( - reflection, light, state->pigment.c, direction, - state->intersection->normal - ); - } else { - return dmnsn_black; - } -} - -/// Determine the amount of transmitted light. -static inline dmnsn_color -dmnsn_evaluate_transparency(const dmnsn_rtstate *state, dmnsn_color light) -{ - if (state->pigment.T >= dmnsn_epsilon - && (state->scene->quality & DMNSN_RENDER_TRANSPARENCY)) - { - return dmnsn_tcolor_filter(light, state->pigment); - } else { - return dmnsn_black; - } -} - -/// Get a light's diffuse contribution to the object -static inline dmnsn_color -dmnsn_evaluate_diffuse(const dmnsn_rtstate *state) -{ - const dmnsn_finish *finish = &state->texture->finish; - if (finish->diffuse) { - return finish->diffuse->diffuse_fn( - finish->diffuse, state->light_color, state->pigment.c, - state->light_ray, state->intersection->normal - ); - } else { - return dmnsn_black; - } -} - -/// Get the color of a light ray at an intersection point. -static bool -dmnsn_trace_light_ray(dmnsn_rtstate *state, const dmnsn_light *light) -{ - dmnsn_line shadow_ray = dmnsn_new_line( - state->r, - light->direction_fn(light, state->r) - ); - // Add epsilon to avoid hitting ourselves with the shadow ray - shadow_ray = dmnsn_line_add_epsilon(shadow_ray); - - // Check if we're casting a shadow on ourself - if ((dmnsn_vector_dot(shadow_ray.n, state->intersection->normal) - * dmnsn_vector_dot(state->viewer, state->intersection->normal) < 0.0) - && (!state->is_shadow_ray || state->pigment.T < dmnsn_epsilon)) - { - return false; - } - - state->light_ray = dmnsn_vector_normalized(shadow_ray.n); - state->light_color = light->illumination_fn(light, state->r); - - // Test for shadow ray intersections - dmnsn_intersection shadow_caster; - bool in_shadow = dmnsn_bvh_intersection(state->bvh, shadow_ray, - &shadow_caster, false); - if (!in_shadow || !light->shadow_fn(light, shadow_caster.t)) { - return true; - } - - if (state->reclevel > 0 - && dmnsn_color_intensity(state->adc_value) >= state->scene->adc_bailout - && (state->scene->quality & DMNSN_RENDER_TRANSPARENCY)) { - dmnsn_rtstate shadow_state = *state; - dmnsn_rtstate_initialize(&shadow_state, &shadow_caster); - dmnsn_trace_pigment(&shadow_state); - - if (shadow_state.pigment.T >= dmnsn_epsilon) { - --shadow_state.reclevel; - shadow_state.adc_value = dmnsn_evaluate_transparency( - &shadow_state, shadow_state.adc_value - ); - shadow_state.is_shadow_ray = true; - if (dmnsn_trace_light_ray(&shadow_state, light)) { - state->light_color = shadow_state.light_color; - - // Handle reflection - dmnsn_color reflected = dmnsn_evaluate_reflection( - &shadow_state, state->light_color, state->light_ray - ); - state->light_color = dmnsn_color_sub(state->light_color, reflected); - - // Handle transparency - state->light_color = dmnsn_evaluate_transparency( - &shadow_state, state->light_color - ); - - return true; - } - } - } - - return false; -} - -static void -dmnsn_trace_lighting(dmnsn_rtstate *state) -{ - // Calculate the ambient color - state->color = DMNSN_TCOLOR(dmnsn_black); - const dmnsn_finish *finish = &state->texture->finish; - if (finish->ambient) { - dmnsn_color ambient = finish->ambient->ambient; - - // Handle reflection and transmittance of the ambient light - dmnsn_color reflected = dmnsn_evaluate_reflection( - state, ambient, state->intersection->normal - ); - ambient = dmnsn_color_sub(ambient, reflected); - dmnsn_color transmitted = dmnsn_evaluate_transparency(state, ambient); - ambient = dmnsn_color_sub(ambient, transmitted); - - state->color.c = dmnsn_color_illuminate(ambient, state->pigment.c); - } - - // Iterate over each light - DMNSN_ARRAY_FOREACH (dmnsn_light **, light, state->scene->lights) { - if (dmnsn_trace_light_ray(state, *light)) { - if (state->scene->quality & DMNSN_RENDER_FINISH) { - dmnsn_color specular = dmnsn_evaluate_specular(state); - state->light_color = dmnsn_color_sub(state->light_color, specular); - - dmnsn_color reflected = dmnsn_evaluate_reflection( - state, state->light_color, state->reflected - ); - state->light_color = dmnsn_color_sub(state->light_color, reflected); - - dmnsn_color transmitted = dmnsn_evaluate_transparency( - state, state->light_color - ); - state->light_color = dmnsn_color_sub(state->light_color, transmitted); - - dmnsn_color diffuse = dmnsn_evaluate_diffuse(state); - - state->color.c = dmnsn_color_add(state->color.c, specular); - state->color.c = dmnsn_color_add(state->color.c, diffuse); - } else { - state->color.c = state->pigment.c; - break; - } - } - } -} - -static void -dmnsn_trace_reflection(dmnsn_rtstate *state) -{ - const dmnsn_reflection *reflection = state->texture->finish.reflection; - if (reflection) { - dmnsn_line refl_ray = dmnsn_new_line(state->r, state->reflected); - refl_ray = dmnsn_line_add_epsilon(refl_ray); - - dmnsn_rtstate recursive_state = *state; - - // Calculate ADC value - recursive_state.adc_value = dmnsn_evaluate_reflection( - state, state->adc_value, state->reflected - ); - - // Shoot the reflected ray - dmnsn_color rec = dmnsn_ray_shoot(&recursive_state, refl_ray).c; - dmnsn_color reflected = dmnsn_evaluate_reflection( - state, rec, state->reflected - ); - - state->color.c = dmnsn_color_add(state->color.c, reflected); - } -} - -static void -dmnsn_trace_transparency(dmnsn_rtstate *state) -{ - if (state->pigment.T >= dmnsn_epsilon) { - const dmnsn_interior *interior = state->interior; - - dmnsn_line trans_ray = dmnsn_new_line(state->r, state->intersection->ray.n); - trans_ray = dmnsn_line_add_epsilon(trans_ray); - - dmnsn_vector r = dmnsn_vector_normalized(trans_ray.n); - dmnsn_vector n = state->intersection->normal; - - dmnsn_rtstate recursive_state = *state; - - // Calculate new refractive index - if (dmnsn_vector_dot(r, n) < 0.0) { - // We are entering an object - recursive_state.ior = interior->ior; - recursive_state.parent = state; - } else { - // We are leaving an object - recursive_state.ior = state->parent ? state->parent->ior : 1.0; - recursive_state.parent = state->parent ? state->parent->parent : NULL; - } - - // Calculate transmitted ray direction - double iorr = state->ior/recursive_state.ior; // ior ratio - double c1 = -dmnsn_vector_dot(r, n); - double c2 = 1.0 - iorr*iorr*(1.0 - c1*c1); - if (c2 <= 0.0) { - // Total internal reflection - return; - } - c2 = sqrt(c2); - if (c1 >= 0.0) { - trans_ray.n = dmnsn_vector_add( - dmnsn_vector_mul(iorr, r), - dmnsn_vector_mul(iorr*c1 - c2, n) - ); - } else { - trans_ray.n = dmnsn_vector_add( - dmnsn_vector_mul(iorr, r), - dmnsn_vector_mul(iorr*c1 + c2, n) - ); - } - - // Calculate ADC value - recursive_state.adc_value = dmnsn_evaluate_transparency( - state, state->adc_value - ); - dmnsn_color adc_reflected = dmnsn_evaluate_reflection( - state, recursive_state.adc_value, state->reflected - ); - recursive_state.adc_value = dmnsn_color_sub( - recursive_state.adc_value, adc_reflected - ); - - // Shoot the transmitted ray - dmnsn_color rec = dmnsn_ray_shoot(&recursive_state, trans_ray).c; - dmnsn_color filtered = dmnsn_evaluate_transparency(state, rec); - - // Conserve energy - dmnsn_color reflected = dmnsn_evaluate_reflection( - state, filtered, state->reflected - ); - filtered = dmnsn_color_sub(filtered, reflected); - - state->color.c = dmnsn_color_add(state->color.c, filtered); - } -} diff --git a/libdimension/reflection.c b/libdimension/reflection.c deleted file mode 100644 index 6765bef..0000000 --- a/libdimension/reflection.c +++ /dev/null @@ -1,64 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Reflective finish. - */ - -#include "dimension.h" -#include -#include - -/// Basic reflective finish type. -typedef struct dmnsn_basic_reflection { - dmnsn_reflection reflection; - dmnsn_color min, max; - double falloff; -} dmnsn_basic_reflection; - -/// Reflective finish callback. -static dmnsn_color -dmnsn_basic_reflection_fn(const dmnsn_reflection *reflection, - dmnsn_color reflect, dmnsn_color color, - dmnsn_vector ray, dmnsn_vector normal) -{ - const dmnsn_basic_reflection *basic = (const dmnsn_basic_reflection *)reflection; - double coeff = pow(fabs(dmnsn_vector_dot(ray, normal)), basic->falloff); - - return dmnsn_color_illuminate( - dmnsn_color_gradient(basic->min, basic->max, coeff), - reflect - ); -} - -dmnsn_reflection * -dmnsn_new_basic_reflection(dmnsn_pool *pool, dmnsn_color min, dmnsn_color max, double falloff) -{ - dmnsn_basic_reflection *basic = DMNSN_PALLOC(pool, dmnsn_basic_reflection); - basic->min = min; - basic->max = max; - basic->falloff = falloff; - - dmnsn_reflection *reflection = &basic->reflection; - dmnsn_init_reflection(reflection); - reflection->reflection_fn = dmnsn_basic_reflection_fn; - return reflection; -} diff --git a/libdimension/render/render.c b/libdimension/render/render.c new file mode 100644 index 0000000..842b41e --- /dev/null +++ b/libdimension/render/render.c @@ -0,0 +1,548 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * The ray-tracing algorithm. + */ + +#include "internal/bvh.h" +#include "internal/concurrency.h" +#include "dimension/render.h" +#include + +//////////////////////////////////// +// Boilerplate for multithreading // +//////////////////////////////////// + +/// Payload type for passing arguments to worker threads. +typedef struct { + dmnsn_future *future; + dmnsn_scene *scene; + dmnsn_bvh *bvh; +} dmnsn_render_payload; + +// Ray-trace a scene +void +dmnsn_render(dmnsn_scene *scene) +{ + dmnsn_future *future = dmnsn_render_async(scene); + if (dmnsn_future_join(future) != 0) { + dmnsn_error("Error occured while ray-tracing."); + } +} + +/// Background thread callback. +static int dmnsn_render_scene_thread(void *ptr); + +// Ray-trace a scene in the background +dmnsn_future * +dmnsn_render_async(dmnsn_scene *scene) +{ + dmnsn_future *future = dmnsn_new_future(); + + dmnsn_render_payload *payload = DMNSN_MALLOC(dmnsn_render_payload); + payload->future = future; + payload->scene = scene; + + dmnsn_new_thread(future, dmnsn_render_scene_thread, payload); + + return future; +} + +/// Worker thread callback. +static int dmnsn_render_scene_concurrent(void *ptr, unsigned int thread, + unsigned int nthreads); + +// Thread callback -- set up the multithreaded engine +static int +dmnsn_render_scene_thread(void *ptr) +{ + dmnsn_render_payload *payload = ptr; + + // Pre-calculate bounding box transformations, etc. + dmnsn_scene_initialize(payload->scene); + + // Time the bounding tree construction + dmnsn_timer_start(&payload->scene->bounding_timer); + payload->bvh = dmnsn_new_bvh(payload->scene->objects, DMNSN_BVH_PRTREE); + dmnsn_timer_stop(&payload->scene->bounding_timer); + + // Set up the future object + dmnsn_future_set_total(payload->future, payload->scene->canvas->height); + + // Time the render itself + dmnsn_timer_start(&payload->scene->render_timer); + int ret = dmnsn_execute_concurrently(payload->future, + dmnsn_render_scene_concurrent, + payload, payload->scene->nthreads); + dmnsn_timer_stop(&payload->scene->render_timer); + + dmnsn_delete_bvh(payload->bvh); + dmnsn_free(payload); + + return ret; +} + +/////////////////////////// +// Ray-tracing algorithm // +/////////////////////////// + +/// The current state of the ray-tracing engine. +typedef struct dmnsn_rtstate { + const struct dmnsn_rtstate *parent; + + const dmnsn_scene *scene; + const dmnsn_intersection *intersection; + const dmnsn_texture *texture; + const dmnsn_interior *interior; + const dmnsn_bvh *bvh; + unsigned int reclevel; + + dmnsn_vector r; + dmnsn_vector pigment_r; + dmnsn_vector viewer; + dmnsn_vector reflected; + + bool is_shadow_ray; + dmnsn_vector light_ray; + dmnsn_color light_color; + + dmnsn_tcolor pigment; + dmnsn_tcolor color; + + double ior; + + dmnsn_color adc_value; +} dmnsn_rtstate; + +/// Compute a ray-tracing state from an intersection. +static inline void +dmnsn_rtstate_initialize(dmnsn_rtstate *state, + const dmnsn_intersection *intersection); +/// Main helper for dmnsn_render_scene_concurrent - shoot a ray. +static dmnsn_tcolor dmnsn_ray_shoot(dmnsn_rtstate *state, dmnsn_ray ray); + +// Actually ray-trace a scene +static int +dmnsn_render_scene_concurrent(void *ptr, unsigned int thread, unsigned int nthreads) +{ + const dmnsn_render_payload *payload = ptr; + dmnsn_future *future = payload->future; + dmnsn_scene *scene = payload->scene; + dmnsn_bvh *bvh = payload->bvh; + + dmnsn_rtstate state = { + .parent = NULL, + .scene = scene, + .bvh = bvh, + }; + + // Iterate through each pixel + for (size_t y = thread; y < scene->canvas->height; y += nthreads) { + for (size_t x = 0; x < scene->canvas->width; ++x) { + // Get the ray corresponding to the (x,y)'th pixel + dmnsn_ray ray = dmnsn_camera_ray( + scene->camera, + ((double)(x + scene->region_x))/(scene->outer_width - 1), + ((double)(y + scene->region_y))/(scene->outer_height - 1) + ); + + // Shoot a ray + state.reclevel = scene->reclimit; + state.ior = 1.0; + state.adc_value = dmnsn_white; + dmnsn_tcolor tcolor = dmnsn_ray_shoot(&state, ray); + dmnsn_canvas_set_pixel(scene->canvas, x, y, tcolor); + } + + dmnsn_future_increment(future); + } + + return 0; +} + +// Compute rtstate fields +static inline void +dmnsn_rtstate_initialize(dmnsn_rtstate *state, + const dmnsn_intersection *intersection) +{ + state->intersection = intersection; + state->texture = intersection->object->texture; + state->interior = intersection->object->interior; + + state->r = dmnsn_ray_point(intersection->ray, intersection->t); + state->pigment_r = dmnsn_transform_point( + intersection->object->pigment_trans, + state->r + ); + state->viewer = dmnsn_vector_normalized( + dmnsn_vector_negate(intersection->ray.n) + ); + state->reflected = dmnsn_vector_sub( + dmnsn_vector_mul( + 2*dmnsn_vector_dot(state->viewer, intersection->normal), + intersection->normal), + state->viewer + ); + + state->is_shadow_ray = false; +} + +/// Calculate the background color. +static void dmnsn_trace_background(dmnsn_rtstate *state, dmnsn_ray ray); +/// Calculate the base pigment at the intersection. +static void dmnsn_trace_pigment(dmnsn_rtstate *state); +/// Handle light, shadow, and shading. +static void dmnsn_trace_lighting(dmnsn_rtstate *state); +/// Trace a reflected ray. +static void dmnsn_trace_reflection(dmnsn_rtstate *state); +/// Trace a transmitted ray. +static void dmnsn_trace_transparency(dmnsn_rtstate *state); + +// Shoot a ray, and calculate the color +static dmnsn_tcolor +dmnsn_ray_shoot(dmnsn_rtstate *state, dmnsn_ray ray) +{ + if (state->reclevel == 0 + || dmnsn_color_intensity(state->adc_value) < state->scene->adc_bailout) + { + return DMNSN_TCOLOR(dmnsn_black); + } + + --state->reclevel; + + dmnsn_intersection intersection; + bool reset = state->reclevel == state->scene->reclimit - 1; + dmnsn_bvh_intersection(state->bvh, ray, &intersection, reset); + if (dmnsn_bvh_intersection(state->bvh, ray, &intersection, reset)) { + // Found an intersection + dmnsn_rtstate_initialize(state, &intersection); + + dmnsn_trace_pigment(state); + if (state->scene->quality & DMNSN_RENDER_LIGHTS) { + dmnsn_trace_lighting(state); + } + if (state->scene->quality & DMNSN_RENDER_REFLECTION) { + dmnsn_trace_reflection(state); + } + if (state->scene->quality & DMNSN_RENDER_TRANSPARENCY) { + dmnsn_trace_transparency(state); + } + } else { + // No intersection, return the background color + dmnsn_trace_background(state, ray); + } + + return state->color; +} + +static void +dmnsn_trace_background(dmnsn_rtstate *state, dmnsn_ray ray) +{ + dmnsn_pigment *background = state->scene->background; + if (state->scene->quality & DMNSN_RENDER_PIGMENT) { + dmnsn_vector r = dmnsn_vector_normalized(ray.n); + state->color = dmnsn_pigment_evaluate(background, r); + } else { + state->color = background->quick_color; + } +} + +static void +dmnsn_trace_pigment(dmnsn_rtstate *state) +{ + dmnsn_pigment *pigment = state->texture->pigment; + if (state->scene->quality & DMNSN_RENDER_PIGMENT) { + state->pigment = dmnsn_pigment_evaluate(pigment, state->pigment_r); + } else { + state->pigment = pigment->quick_color; + } + state->color = state->pigment; +} + +/// Determine the amount of specular highlight. +static inline dmnsn_color +dmnsn_evaluate_specular(const dmnsn_rtstate *state) +{ + const dmnsn_finish *finish = &state->texture->finish; + if (finish->specular) { + return finish->specular->specular_fn( + finish->specular, state->light_color, state->pigment.c, + state->light_ray, state->intersection->normal, state->viewer + ); + } else { + return dmnsn_black; + } +} + +/// Determine the amount of reflected light. +static inline dmnsn_color +dmnsn_evaluate_reflection(const dmnsn_rtstate *state, + dmnsn_color light, dmnsn_vector direction) +{ + const dmnsn_reflection *reflection = state->texture->finish.reflection; + if (reflection && (state->scene->quality & DMNSN_RENDER_REFLECTION)) { + return reflection->reflection_fn( + reflection, light, state->pigment.c, direction, + state->intersection->normal + ); + } else { + return dmnsn_black; + } +} + +/// Determine the amount of transmitted light. +static inline dmnsn_color +dmnsn_evaluate_transparency(const dmnsn_rtstate *state, dmnsn_color light) +{ + if (state->pigment.T >= dmnsn_epsilon + && (state->scene->quality & DMNSN_RENDER_TRANSPARENCY)) + { + return dmnsn_tcolor_filter(light, state->pigment); + } else { + return dmnsn_black; + } +} + +/// Get a light's diffuse contribution to the object +static inline dmnsn_color +dmnsn_evaluate_diffuse(const dmnsn_rtstate *state) +{ + const dmnsn_finish *finish = &state->texture->finish; + if (finish->diffuse) { + return finish->diffuse->diffuse_fn( + finish->diffuse, state->light_color, state->pigment.c, + state->light_ray, state->intersection->normal + ); + } else { + return dmnsn_black; + } +} + +/// Get the color of a light ray at an intersection point. +static bool +dmnsn_trace_light_ray(dmnsn_rtstate *state, const dmnsn_light *light) +{ + dmnsn_ray shadow_ray = dmnsn_new_ray( + state->r, + light->direction_fn(light, state->r) + ); + // Add epsilon to avoid hitting ourselves with the shadow ray + shadow_ray = dmnsn_ray_add_epsilon(shadow_ray); + + // Check if we're casting a shadow on ourself + if ((dmnsn_vector_dot(shadow_ray.n, state->intersection->normal) + * dmnsn_vector_dot(state->viewer, state->intersection->normal) < 0.0) + && (!state->is_shadow_ray || state->pigment.T < dmnsn_epsilon)) + { + return false; + } + + state->light_ray = dmnsn_vector_normalized(shadow_ray.n); + state->light_color = light->illumination_fn(light, state->r); + + // Test for shadow ray intersections + dmnsn_intersection shadow_caster; + bool in_shadow = dmnsn_bvh_intersection(state->bvh, shadow_ray, + &shadow_caster, false); + if (!in_shadow || !light->shadow_fn(light, shadow_caster.t)) { + return true; + } + + if (state->reclevel > 0 + && dmnsn_color_intensity(state->adc_value) >= state->scene->adc_bailout + && (state->scene->quality & DMNSN_RENDER_TRANSPARENCY)) { + dmnsn_rtstate shadow_state = *state; + dmnsn_rtstate_initialize(&shadow_state, &shadow_caster); + dmnsn_trace_pigment(&shadow_state); + + if (shadow_state.pigment.T >= dmnsn_epsilon) { + --shadow_state.reclevel; + shadow_state.adc_value = dmnsn_evaluate_transparency( + &shadow_state, shadow_state.adc_value + ); + shadow_state.is_shadow_ray = true; + if (dmnsn_trace_light_ray(&shadow_state, light)) { + state->light_color = shadow_state.light_color; + + // Handle reflection + dmnsn_color reflected = dmnsn_evaluate_reflection( + &shadow_state, state->light_color, state->light_ray + ); + state->light_color = dmnsn_color_sub(state->light_color, reflected); + + // Handle transparency + state->light_color = dmnsn_evaluate_transparency( + &shadow_state, state->light_color + ); + + return true; + } + } + } + + return false; +} + +static void +dmnsn_trace_lighting(dmnsn_rtstate *state) +{ + // Calculate the ambient color + state->color = DMNSN_TCOLOR(dmnsn_black); + const dmnsn_finish *finish = &state->texture->finish; + if (finish->ambient) { + dmnsn_color ambient = finish->ambient->ambient; + + // Handle reflection and transmittance of the ambient light + dmnsn_color reflected = dmnsn_evaluate_reflection( + state, ambient, state->intersection->normal + ); + ambient = dmnsn_color_sub(ambient, reflected); + dmnsn_color transmitted = dmnsn_evaluate_transparency(state, ambient); + ambient = dmnsn_color_sub(ambient, transmitted); + + state->color.c = dmnsn_color_illuminate(ambient, state->pigment.c); + } + + // Iterate over each light + DMNSN_ARRAY_FOREACH (dmnsn_light **, light, state->scene->lights) { + if (dmnsn_trace_light_ray(state, *light)) { + if (state->scene->quality & DMNSN_RENDER_FINISH) { + dmnsn_color specular = dmnsn_evaluate_specular(state); + state->light_color = dmnsn_color_sub(state->light_color, specular); + + dmnsn_color reflected = dmnsn_evaluate_reflection( + state, state->light_color, state->reflected + ); + state->light_color = dmnsn_color_sub(state->light_color, reflected); + + dmnsn_color transmitted = dmnsn_evaluate_transparency( + state, state->light_color + ); + state->light_color = dmnsn_color_sub(state->light_color, transmitted); + + dmnsn_color diffuse = dmnsn_evaluate_diffuse(state); + + state->color.c = dmnsn_color_add(state->color.c, specular); + state->color.c = dmnsn_color_add(state->color.c, diffuse); + } else { + state->color.c = state->pigment.c; + break; + } + } + } +} + +static void +dmnsn_trace_reflection(dmnsn_rtstate *state) +{ + const dmnsn_reflection *reflection = state->texture->finish.reflection; + if (reflection) { + dmnsn_ray refl_ray = dmnsn_new_ray(state->r, state->reflected); + refl_ray = dmnsn_ray_add_epsilon(refl_ray); + + dmnsn_rtstate recursive_state = *state; + + // Calculate ADC value + recursive_state.adc_value = dmnsn_evaluate_reflection( + state, state->adc_value, state->reflected + ); + + // Shoot the reflected ray + dmnsn_color rec = dmnsn_ray_shoot(&recursive_state, refl_ray).c; + dmnsn_color reflected = dmnsn_evaluate_reflection( + state, rec, state->reflected + ); + + state->color.c = dmnsn_color_add(state->color.c, reflected); + } +} + +static void +dmnsn_trace_transparency(dmnsn_rtstate *state) +{ + if (state->pigment.T >= dmnsn_epsilon) { + const dmnsn_interior *interior = state->interior; + + dmnsn_ray trans_ray = dmnsn_new_ray(state->r, state->intersection->ray.n); + trans_ray = dmnsn_ray_add_epsilon(trans_ray); + + dmnsn_vector r = dmnsn_vector_normalized(trans_ray.n); + dmnsn_vector n = state->intersection->normal; + + dmnsn_rtstate recursive_state = *state; + + // Calculate new refractive index + if (dmnsn_vector_dot(r, n) < 0.0) { + // We are entering an object + recursive_state.ior = interior->ior; + recursive_state.parent = state; + } else { + // We are leaving an object + recursive_state.ior = state->parent ? state->parent->ior : 1.0; + recursive_state.parent = state->parent ? state->parent->parent : NULL; + } + + // Calculate transmitted ray direction + double iorr = state->ior/recursive_state.ior; // ior ratio + double c1 = -dmnsn_vector_dot(r, n); + double c2 = 1.0 - iorr*iorr*(1.0 - c1*c1); + if (c2 <= 0.0) { + // Total internal reflection + return; + } + c2 = sqrt(c2); + if (c1 >= 0.0) { + trans_ray.n = dmnsn_vector_add( + dmnsn_vector_mul(iorr, r), + dmnsn_vector_mul(iorr*c1 - c2, n) + ); + } else { + trans_ray.n = dmnsn_vector_add( + dmnsn_vector_mul(iorr, r), + dmnsn_vector_mul(iorr*c1 + c2, n) + ); + } + + // Calculate ADC value + recursive_state.adc_value = dmnsn_evaluate_transparency( + state, state->adc_value + ); + dmnsn_color adc_reflected = dmnsn_evaluate_reflection( + state, recursive_state.adc_value, state->reflected + ); + recursive_state.adc_value = dmnsn_color_sub( + recursive_state.adc_value, adc_reflected + ); + + // Shoot the transmitted ray + dmnsn_color rec = dmnsn_ray_shoot(&recursive_state, trans_ray).c; + dmnsn_color filtered = dmnsn_evaluate_transparency(state, rec); + + // Conserve energy + dmnsn_color reflected = dmnsn_evaluate_reflection( + state, filtered, state->reflected + ); + filtered = dmnsn_color_sub(filtered, reflected); + + state->color.c = dmnsn_color_add(state->color.c, filtered); + } +} diff --git a/libdimension/rgba.c b/libdimension/rgba.c deleted file mode 100644 index 9889189..0000000 --- a/libdimension/rgba.c +++ /dev/null @@ -1,95 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * 16-bit RGBA canvas optimizer. - */ - -#include "dimension-internal.h" -#include - -void -dmnsn_rgba8_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas) -{ - if (dmnsn_canvas_find_optimizer(canvas, dmnsn_rgba8_optimizer_fn)) { - return; - } - - size_t ndata = 4*canvas->width*canvas->height; - dmnsn_rgba8_optimizer *rgba8 = dmnsn_palloc(pool, sizeof(dmnsn_rgba8_optimizer) + ndata*sizeof(uint8_t)); - - dmnsn_canvas_optimizer *optimizer = &rgba8->optimizer; - dmnsn_init_canvas_optimizer(optimizer); - optimizer->optimizer_fn = dmnsn_rgba8_optimizer_fn; - - dmnsn_canvas_optimize(canvas, optimizer); -} - -void -dmnsn_rgba16_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas) -{ - if (dmnsn_canvas_find_optimizer(canvas, dmnsn_rgba16_optimizer_fn)) { - return; - } - - size_t ndata = 4*canvas->width*canvas->height; - dmnsn_rgba16_optimizer *rgba16 = dmnsn_palloc(pool, sizeof(dmnsn_rgba16_optimizer) + ndata*sizeof(uint16_t)); - - dmnsn_canvas_optimizer *optimizer = &rgba16->optimizer; - dmnsn_init_canvas_optimizer(optimizer); - optimizer->optimizer_fn = dmnsn_rgba16_optimizer_fn; - - dmnsn_canvas_optimize(canvas, optimizer); -} - -void -dmnsn_rgba8_optimizer_fn(dmnsn_canvas_optimizer *optimizer, const dmnsn_canvas *canvas, size_t x, size_t y) -{ - dmnsn_rgba8_optimizer *rgba8 = (dmnsn_rgba8_optimizer *)optimizer; - - uint8_t *pixel = rgba8->data + 4*(y*canvas->width + x); - dmnsn_tcolor tcolor = dmnsn_canvas_get_pixel(canvas, x, y); - tcolor = dmnsn_tcolor_remove_filter(tcolor); - tcolor.c = dmnsn_color_to_sRGB(tcolor.c); - tcolor = dmnsn_tcolor_clamp(tcolor); - - pixel[0] = lround(tcolor.c.R*UINT8_MAX); - pixel[1] = lround(tcolor.c.G*UINT8_MAX); - pixel[2] = lround(tcolor.c.B*UINT8_MAX); - pixel[3] = lround(tcolor.T*UINT8_MAX); -} - -void -dmnsn_rgba16_optimizer_fn(dmnsn_canvas_optimizer *optimizer, const dmnsn_canvas *canvas, size_t x, size_t y) -{ - dmnsn_rgba16_optimizer *rgba16 = (dmnsn_rgba16_optimizer *)optimizer; - - uint16_t *pixel = rgba16->data + 4*(y*canvas->width + x); - dmnsn_tcolor tcolor = dmnsn_canvas_get_pixel(canvas, x, y); - tcolor = dmnsn_tcolor_remove_filter(tcolor); - tcolor.c = dmnsn_color_to_sRGB(tcolor.c); - tcolor = dmnsn_tcolor_clamp(tcolor); - - pixel[0] = lround(tcolor.c.R*UINT16_MAX); - pixel[1] = lround(tcolor.c.G*UINT16_MAX); - pixel[2] = lround(tcolor.c.B*UINT16_MAX); - pixel[3] = lround(tcolor.T*UINT16_MAX); -} diff --git a/libdimension/rgba.h b/libdimension/rgba.h deleted file mode 100644 index b43ca3f..0000000 --- a/libdimension/rgba.h +++ /dev/null @@ -1,48 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * RGBA canvas optimizer interface, used by image optimizers. - */ - -#include - -/// RGBA8 optimizer type. -typedef struct dmnsn_rgba8_optimizer { - dmnsn_canvas_optimizer optimizer; - uint8_t data[]; -} dmnsn_rgba8_optimizer; - -/// RGBA16 optimizer type. -typedef struct dmnsn_rgba16_optimizer { - dmnsn_canvas_optimizer optimizer; - uint16_t data[]; -} dmnsn_rgba16_optimizer; - -/// Apply the RGBA8 optimizer to a canvas. -DMNSN_INTERNAL void dmnsn_rgba8_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas); -/// Apply the RGBA16 optimizer to a canvas. -DMNSN_INTERNAL void dmnsn_rgba16_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas); - -/// RGBA8 optimizer callback. -DMNSN_INTERNAL void dmnsn_rgba8_optimizer_fn(dmnsn_canvas_optimizer *optimizer, const dmnsn_canvas *canvas, size_t x, size_t y); -/// RGBA16 optimizer callback. -DMNSN_INTERNAL void dmnsn_rgba16_optimizer_fn(dmnsn_canvas_optimizer *optimizer, const dmnsn_canvas *canvas, size_t x, size_t y); diff --git a/libdimension/scene.c b/libdimension/scene.c deleted file mode 100644 index 6c9c495..0000000 --- a/libdimension/scene.c +++ /dev/null @@ -1,77 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Scenes. - */ - -#include "dimension-internal.h" -#include - -// Allocate an empty scene -dmnsn_scene * -dmnsn_new_scene(dmnsn_pool *pool) -{ - dmnsn_scene *scene = DMNSN_PALLOC(pool, dmnsn_scene); - - scene->background = NULL; - scene->default_texture = dmnsn_new_texture(pool); - scene->default_interior = dmnsn_new_interior(pool); - scene->canvas = NULL; - scene->region_x = 0; - scene->region_y = 0; - scene->outer_width = 0; - scene->outer_height = 0; - scene->objects = DMNSN_PALLOC_ARRAY(pool, dmnsn_object *); - scene->lights = DMNSN_PALLOC_ARRAY(pool, dmnsn_light *); - scene->camera = NULL; - scene->quality = DMNSN_RENDER_FULL; - scene->reclimit = 5; - scene->adc_bailout = 1.0/255.0; - scene->nthreads = dmnsn_ncpus(); - scene->initialized = false; - - return scene; -} - -void -dmnsn_scene_initialize(dmnsn_scene *scene) -{ - dmnsn_assert(!scene->initialized, "Scene double-initialized."); - scene->initialized = true; - - if (scene->outer_width == 0) { - scene->outer_width = scene->canvas->width; - } - if (scene->outer_height == 0) { - scene->outer_height = scene->canvas->height; - } - - dmnsn_pigment_initialize(scene->background); - - dmnsn_texture_initialize(scene->default_texture); - - DMNSN_ARRAY_FOREACH (dmnsn_object **, object, scene->objects) { - dmnsn_texture_cascade(scene->default_texture, &(*object)->texture); - dmnsn_interior_cascade(scene->default_interior, &(*object)->interior); - dmnsn_object_precompute(*object); - } -} diff --git a/libdimension/solid_pigment.c b/libdimension/solid_pigment.c deleted file mode 100644 index 8c94e1c..0000000 --- a/libdimension/solid_pigment.c +++ /dev/null @@ -1,36 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Solid color pigments. - */ - -#include "dimension.h" -#include - -// Create a solid color -dmnsn_pigment * -dmnsn_new_solid_pigment(dmnsn_pool *pool, dmnsn_tcolor color) -{ - dmnsn_pigment *pigment = dmnsn_new_pigment(pool); - pigment->quick_color = color; - return pigment; -} diff --git a/libdimension/sphere.c b/libdimension/sphere.c deleted file mode 100644 index d8fae05..0000000 --- a/libdimension/sphere.c +++ /dev/null @@ -1,114 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Spheres. - */ - -#include "dimension-internal.h" - -/// Sphere intersection callback. -static bool -dmnsn_sphere_intersection_fn(const dmnsn_object *sphere, dmnsn_line l, - dmnsn_intersection *intersection) -{ - // Solve (x0 + nx*t)^2 + (y0 + ny*t)^2 + (z0 + nz*t)^2 == 1 - double poly[3], x[2]; - poly[2] = dmnsn_vector_dot(l.n, l.n); - poly[1] = 2.0*dmnsn_vector_dot(l.n, l.x0); - poly[0] = dmnsn_vector_dot(l.x0, l.x0) - 1.0; - - size_t n = dmnsn_polynomial_solve(poly, 2, x); - if (n == 0) { - return false; - } - - double t = x[0]; - // Optimize for the case where we're outside the sphere - if (dmnsn_likely(n == 2)) { - t = dmnsn_min(t, x[1]); - } - - intersection->t = t; - intersection->normal = dmnsn_line_point(l, t); - return true; -} - -/// Sphere inside callback. -static bool -dmnsn_sphere_inside_fn(const dmnsn_object *sphere, dmnsn_vector point) -{ - return point.x*point.x + point.y*point.y + point.z*point.z < 1.0; -} - -/// Helper for sphere bounding box calculation. -static inline double -dmnsn_implicit_dot(double row[4]) -{ - double ret = 0.0; - for (int i = 0; i < 3; ++i) { - ret += row[i]*row[i]; - } - return ret; -} - -/// Sphere bounding callback. -static dmnsn_bounding_box -dmnsn_sphere_bounding_fn(const dmnsn_object *object, dmnsn_matrix trans) -{ - // Get a tight bound using the quadric representation of a sphere. For - // details, see - // http://tavianator.com/2014/06/exact-bounding-boxes-for-spheres-ellipsoids - - dmnsn_bounding_box box; - - double cx = trans.n[0][3]; - double dx = sqrt(dmnsn_implicit_dot(trans.n[0])); - box.min.x = cx - dx; - box.max.x = cx + dx; - - double cy = trans.n[1][3]; - double dy = sqrt(dmnsn_implicit_dot(trans.n[1])); - box.min.y = cy - dy; - box.max.y = cy + dy; - - double cz = trans.n[2][3]; - double dz = sqrt(dmnsn_implicit_dot(trans.n[2])); - box.min.z = cz - dz; - box.max.z = cz + dz; - - return box; -} - -/// Sphere vtable. -static const dmnsn_object_vtable dmnsn_sphere_vtable = { - .intersection_fn = dmnsn_sphere_intersection_fn, - .inside_fn = dmnsn_sphere_inside_fn, - .bounding_fn = dmnsn_sphere_bounding_fn, -}; - -dmnsn_object * -dmnsn_new_sphere(dmnsn_pool *pool) -{ - dmnsn_object *sphere = dmnsn_new_object(pool); - sphere->vtable = &dmnsn_sphere_vtable; - return sphere; -} diff --git a/libdimension/tests/future.c b/libdimension/tests/future.c index ee2511a..a223a44 100644 --- a/libdimension/tests/future.c +++ b/libdimension/tests/future.c @@ -22,9 +22,9 @@ * Tests for dmnsn_future. */ -#include "../platform.c" -#include "../future.c" -#include "../threads.c" +#include "../platform/platform.c" +#include "../concurrency/future.c" +#include "../concurrency/threads.c" #include "tests.h" #include diff --git a/libdimension/tests/polynomial.c b/libdimension/tests/polynomial.c index 492c22c..a983bd4 100644 --- a/libdimension/tests/polynomial.c +++ b/libdimension/tests/polynomial.c @@ -22,8 +22,8 @@ * Basic tests of the polynomial root-finder. */ +#include "../math/polynomial.c" #include "tests.h" -#include "../polynomial.c" #include #define DMNSN_CLOSE_ENOUGH 1.0e-6 diff --git a/libdimension/tests/pool.c b/libdimension/tests/pool.c index 1722d66..6090c36 100644 --- a/libdimension/tests/pool.c +++ b/libdimension/tests/pool.c @@ -22,10 +22,10 @@ * Tests for memory pools. */ -#include "../dimension-internal.h" +#include "../concurrency/future.c" +#include "../concurrency/threads.c" +#include "../internal.h" #include "tests.h" -#include "../future.c" -#include "../threads.c" static dmnsn_pool *pool; diff --git a/libdimension/tests/prtree.c b/libdimension/tests/prtree.c index acdd426..8824e93 100644 --- a/libdimension/tests/prtree.c +++ b/libdimension/tests/prtree.c @@ -22,28 +22,28 @@ * Basic tests of PR-trees. */ -#include "../platform.c" -#include "../threads.c" -#include "../future.c" -#include "../bvh.c" -#include "../prtree.c" +#include "../platform/platform.c" +#include "../concurrency/threads.c" +#include "../concurrency/future.c" +#include "../bvh/bvh.c" +#include "../bvh/prtree.c" #include #include unsigned int calls = 0; static bool -dmnsn_fake_intersection_fn(const dmnsn_object *object, dmnsn_line line, +dmnsn_fake_intersection_fn(const dmnsn_object *object, dmnsn_ray ray, dmnsn_intersection *intersection) { - intersection->t = (object->bounding_box.min.z - line.x0.z)/line.n.z; + intersection->t = (object->aabb.min.z - ray.x0.z)/ray.n.z; intersection->normal = dmnsn_x; ++calls; return true; } static void -dmnsn_randomize_bounding_box(dmnsn_object *object) +dmnsn_randomize_aabb(dmnsn_object *object) { dmnsn_vector a, b; @@ -55,8 +55,8 @@ dmnsn_randomize_bounding_box(dmnsn_object *object) b.y = 2.0*((double)rand())/RAND_MAX - 1.0; b.z = 2.0*((double)rand())/RAND_MAX - 1.0; - object->bounding_box.min = dmnsn_vector_min(a, b); - object->bounding_box.max = dmnsn_vector_max(a, b); + object->aabb.min = dmnsn_vector_min(a, b); + object->aabb.max = dmnsn_vector_max(a, b); } static const dmnsn_object_vtable dmnsn_fake_vtable = { @@ -67,7 +67,7 @@ static dmnsn_object * dmnsn_new_fake_object(dmnsn_pool *pool) { dmnsn_object *object = dmnsn_new_object(pool); - dmnsn_randomize_bounding_box(object); + dmnsn_randomize_aabb(object); object->vtable = &dmnsn_fake_vtable; object->trans_inv = dmnsn_identity_matrix(); return object; @@ -92,7 +92,7 @@ main(void) dmnsn_bvh *bvh = dmnsn_new_bvh(objects, DMNSN_BVH_PRTREE); dmnsn_intersection intersection; - dmnsn_line ray = dmnsn_new_line( + dmnsn_ray ray = dmnsn_new_ray( dmnsn_new_vector(0.0, 0.0, -2.0), dmnsn_new_vector(0.0, 0.0, 1.0) ); diff --git a/libdimension/tests/render.c b/libdimension/tests/render.c index 56a80d3..fd1e96d 100644 --- a/libdimension/tests/render.c +++ b/libdimension/tests/render.c @@ -350,7 +350,7 @@ main(void) // Render the scene printf("Rendering scene\n"); - dmnsn_future *future = dmnsn_ray_trace_async(scene); + dmnsn_future *future = dmnsn_render_async(scene); // Display the scene as it's rendered if (display) { diff --git a/libdimension/texture.c b/libdimension/texture.c deleted file mode 100644 index 515e260..0000000 --- a/libdimension/texture.c +++ /dev/null @@ -1,68 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Textures. - */ - -#include "dimension-internal.h" - -dmnsn_texture * -dmnsn_new_texture(dmnsn_pool *pool) -{ - dmnsn_texture *texture = DMNSN_PALLOC(pool, dmnsn_texture); - texture->pigment = NULL; - texture->finish = dmnsn_new_finish(); - texture->trans = dmnsn_identity_matrix(); - texture->initialized = false; - return texture; -} - -void -dmnsn_texture_initialize(dmnsn_texture *texture) -{ - dmnsn_assert(!texture->initialized, "Texture double-initialized."); - texture->initialized = true; - - texture->trans_inv = dmnsn_matrix_inverse(texture->trans); - - if (!texture->pigment->initialized) { - texture->pigment->trans = dmnsn_matrix_mul(texture->trans, - texture->pigment->trans); - dmnsn_pigment_initialize(texture->pigment); - } -} - -void -dmnsn_texture_cascade(dmnsn_texture *default_texture, dmnsn_texture **texturep) -{ - if (!*texturep) { - *texturep = default_texture; - } - - dmnsn_texture *texture = *texturep; - - if (!texture->pigment) { - texture->pigment = default_texture->pigment; - } - - dmnsn_finish_cascade(&default_texture->finish, &texture->finish); -} diff --git a/libdimension/threads.c b/libdimension/threads.c deleted file mode 100644 index 1fee01d..0000000 --- a/libdimension/threads.c +++ /dev/null @@ -1,324 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Background threading. - */ - -#include "dimension-internal.h" -#include - -/// The payload to pass to the pthread callback. -typedef struct dmnsn_thread_payload { - dmnsn_thread_fn *thread_fn; - void *arg; - dmnsn_future *future; -} dmnsn_thread_payload; - -/// Clean up after a thread. -static void -dmnsn_thread_cleanup(void *arg) -{ - dmnsn_thread_payload *payload = arg; - dmnsn_future *future = payload->future; - dmnsn_free(payload); - - dmnsn_future_finish(future); -} - -/// pthread callback -- call the real thread callback. -static void * -dmnsn_thread(void *arg) -{ - dmnsn_thread_payload *payload = arg; - int *ret; - - pthread_cleanup_push(dmnsn_thread_cleanup, payload); - ret = DMNSN_MALLOC(int); - *ret = payload->thread_fn(payload->arg); - pthread_cleanup_pop(true); - return ret; -} - -void -dmnsn_new_thread(dmnsn_future *future, dmnsn_thread_fn *thread_fn, void *arg) -{ - dmnsn_thread_payload *payload = DMNSN_MALLOC(dmnsn_thread_payload); - payload->thread_fn = thread_fn; - payload->arg = arg; - payload->future = future; - - if (pthread_create(&future->thread, NULL, dmnsn_thread, payload) != 0) { - dmnsn_error("Couldn't start thread."); - } -} - -/// Payload for threads executed by dmnsn_execute_concurrently(). -typedef struct dmnsn_ccthread_payload { - dmnsn_future *future; - dmnsn_ccthread_fn *ccthread_fn; - void *arg; - unsigned int thread, nthreads; - int ret; - bool running; -} dmnsn_ccthread_payload; - -static void * -dmnsn_concurrent_thread(void *ptr) -{ - dmnsn_ccthread_payload *payload = ptr; - payload->ret = payload->ccthread_fn(payload->arg, payload->thread, - payload->nthreads); - if (payload->future) { - dmnsn_future_finish_thread(payload->future); - } - return NULL; -} - -typedef struct dmnsn_ccthread_cleanup_payload { - dmnsn_future *future; - pthread_t *threads; - dmnsn_ccthread_payload *payloads; - unsigned int nthreads; -} dmnsn_ccthread_cleanup_payload; - -static void -dmnsn_ccthread_cleanup(void *ptr) -{ - dmnsn_ccthread_cleanup_payload *payload = ptr; - - for (unsigned int i = 0; i < payload->nthreads; ++i) { - if (payload->payloads[i].running) { - pthread_cancel(payload->threads[i]); - } - } - - for (unsigned int i = 0; i < payload->nthreads; ++i) { - if (payload->payloads[i].running) { - dmnsn_join_thread(payload->threads[i], NULL); - } - } - - if (payload->future) { - dmnsn_future_set_nthreads(payload->future, 1); - } -} - -int -dmnsn_execute_concurrently(dmnsn_future *future, dmnsn_ccthread_fn *ccthread_fn, - void *arg, unsigned int nthreads) -{ - dmnsn_assert(nthreads > 0, "Attempt to execute using 0 concurrent threads."); - - if (future) { - dmnsn_future_set_nthreads(future, nthreads); - } - - pthread_t threads[nthreads]; - dmnsn_ccthread_payload payloads[nthreads]; - for (unsigned int i = 0; i < nthreads; ++i) { - payloads[i].running = false; - } - - int ret = 0; - dmnsn_ccthread_cleanup_payload cleanup_payload = { - .future = future, - .threads = threads, - .payloads = payloads, - .nthreads = nthreads, - }; - pthread_cleanup_push(dmnsn_ccthread_cleanup, &cleanup_payload); - for (unsigned int i = 0; i < nthreads; ++i) { - payloads[i].future = future; - payloads[i].ccthread_fn = ccthread_fn; - payloads[i].arg = arg; - payloads[i].thread = i; - payloads[i].nthreads = nthreads; - payloads[i].ret = -1; - if (pthread_create(&threads[i], NULL, dmnsn_concurrent_thread, - &payloads[i]) != 0) - { - dmnsn_error("Couldn't start worker thread."); - } - payloads[i].running = true; - } - - for (unsigned int i = 0; i < nthreads; ++i) { - dmnsn_join_thread(threads[i], NULL); - payloads[i].running = false; - if (payloads[i].ret != 0) { - ret = payloads[i].ret; - } - } - pthread_cleanup_pop(false); - - if (future) { - dmnsn_future_set_nthreads(future, 1); - } - - return ret; -} - -// pthread wrappers - -void -dmnsn_initialize_mutex(pthread_mutex_t *mutex) -{ - if (pthread_mutex_init(mutex, NULL) != 0) { - dmnsn_error("Couldn't initialize mutex."); - } -} - -void -dmnsn_lock_mutex_impl(pthread_mutex_t *mutex) -{ - if (pthread_mutex_lock(mutex) != 0) { - dmnsn_error("Couldn't lock mutex."); - } -} - -void -dmnsn_unlock_mutex_impl(void *mutex) -{ - if (pthread_mutex_unlock(mutex) != 0) { - dmnsn_error("Couldn't unlock mutex."); - } -} - -void -dmnsn_destroy_mutex(pthread_mutex_t *mutex) -{ - if (pthread_mutex_destroy(mutex) != 0) { - dmnsn_warning("Couldn't destroy mutex."); - } -} - -void -dmnsn_initialize_rwlock(pthread_rwlock_t *rwlock) -{ - if (pthread_rwlock_init(rwlock, NULL) != 0) { - dmnsn_error("Couldn't initialize read-write lock."); - } -} - -void -dmnsn_read_lock_impl(pthread_rwlock_t *rwlock) -{ - if (pthread_rwlock_rdlock(rwlock) != 0) { - dmnsn_error("Couldn't acquire read lock."); - } -} - -void -dmnsn_write_lock_impl(pthread_rwlock_t *rwlock) -{ - if (pthread_rwlock_wrlock(rwlock) != 0) { - dmnsn_error("Couldn't acquire write lock."); - } -} - -void -dmnsn_unlock_rwlock_impl(pthread_rwlock_t *rwlock) -{ - if (pthread_rwlock_unlock(rwlock) != 0) { - dmnsn_error("Couldn't unlock read-write lock."); - } -} - -void -dmnsn_destroy_rwlock(pthread_rwlock_t *rwlock) -{ - if (pthread_rwlock_destroy(rwlock) != 0) { - dmnsn_warning("Couldn't destroy read-write lock."); - } -} - -void -dmnsn_initialize_cond(pthread_cond_t *cond) -{ - if (pthread_cond_init(cond, NULL) != 0) { - dmnsn_error("Couldn't initialize condition variable."); - } -} - -void -dmnsn_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) -{ - if (pthread_cond_wait(cond, mutex) != 0) { - dmnsn_error("Couldn't wait on condition variable."); - } -} - -void -dmnsn_cond_broadcast(pthread_cond_t *cond) -{ - if (pthread_cond_broadcast(cond) != 0) { - dmnsn_error("Couldn't signal condition variable."); - } -} - -void -dmnsn_destroy_cond(pthread_cond_t *cond) -{ - if (pthread_cond_destroy(cond) != 0) { - dmnsn_warning("Couldn't destroy condition variable."); - } -} - -void -dmnsn_once(pthread_once_t *once, dmnsn_once_fn *once_fn) -{ - if (pthread_once(once, once_fn) != 0) { - dmnsn_error("Couldn't call one-shot function."); - } -} - -void -dmnsn_key_create(pthread_key_t *key, dmnsn_callback_fn *destructor) -{ - if (pthread_key_create(key, destructor) != 0) { - dmnsn_error("Couldn't initialize thread-specific pointer."); - } -} - -void -dmnsn_setspecific(pthread_key_t key, const void *value) -{ - if (pthread_setspecific(key, value) != 0) { - dmnsn_error("Couldn't set thread-specific pointer."); - } -} - -void -dmnsn_key_delete(pthread_key_t key) -{ - if (pthread_key_delete(key) != 0) { - dmnsn_warning("Couldn't destroy thread-specific pointer."); - } -} - -void -dmnsn_join_thread(pthread_t thread, void **retval) -{ - if (pthread_join(thread, retval) != 0) { - dmnsn_error("Couldn't join thread."); - } -} diff --git a/libdimension/threads.h b/libdimension/threads.h deleted file mode 100644 index 839299e..0000000 --- a/libdimension/threads.h +++ /dev/null @@ -1,216 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Background threading interface. - */ - -#include - -/** - * Thread callback type. - * @param[in,out] ptr An arbitrary pointer. - * @return 0 on success, non-zero on failure. - */ -typedef int dmnsn_thread_fn(void *ptr); - -/** - * Create a thread that cleans up after itself on errors. - * @param[in,out] future The future object to associate with the thread. - * @param[in] thread_fn The thread callback. - * @param[in,out] arg The pointer to pass to the thread callback. - */ -DMNSN_INTERNAL void dmnsn_new_thread(dmnsn_future *future, - dmnsn_thread_fn *thread_fn, void *arg); - -/** - * Thread callback type for parallel tasks. - * @param[in,out] ptr An arbitrary pointer. - * @param[in] thread An ID for this thread, in [0, \p nthreads). - * @param[in] nthreads The number of concurrent threads. - * @return 0 on success, non-zero on failure. - */ -typedef int dmnsn_ccthread_fn(void *ptr, unsigned int thread, - unsigned int nthreads); - -/** - * Run \p nthreads threads in parallel. - * @param[in,out] future The future object to associate with the threads, - * possibly NULL. - * @param[in] ccthread_fn The routine to run in each concurrent thread. - * @param[in,out] arg The pointer to pass to the thread callbacks. - * @param[in] nthreads The number of concurrent threads to run. - * @return 0 if all threads were successful, and an error code otherwise. - */ -DMNSN_INTERNAL int dmnsn_execute_concurrently(dmnsn_future *future, - dmnsn_ccthread_fn *ccthread_fn, - void *arg, unsigned int nthreads); - -/** - * Initialize a mutex, bailing out on failure. - * @param[out] mutex The mutex to initialize. - */ -DMNSN_INTERNAL void dmnsn_initialize_mutex(pthread_mutex_t *mutex); - -/// dmnsn_lock_mutex() implementation. -DMNSN_INTERNAL void dmnsn_lock_mutex_impl(pthread_mutex_t *mutex); -/// dmnsn_unlock_mutex() implementation. -DMNSN_INTERNAL void dmnsn_unlock_mutex_impl(void *mutex); - -/** - * Lock a mutex, bailing out on failure. - * Contains a {, so must be used in the same block as dmnsn_unlock_mutex(). - * @param[in,out] mutex The mutex to lock. - */ -#define dmnsn_lock_mutex(mutex) do { dmnsn_lock_mutex_impl((mutex)) - -/** - * Unlock a mutex, bailing out on failure. - * Contains a }, so must be used in the same block as dmnsn_lock_mutex(). - * @param[in,out] mutex The mutex to unlock. - */ -#define dmnsn_unlock_mutex(mutex) dmnsn_unlock_mutex_impl((mutex)); } while (0) - -/** - * Destroy a mutex, warning on failure. - * @param[in,out] mutex The mutex to destroy. - */ -DMNSN_INTERNAL void dmnsn_destroy_mutex(pthread_mutex_t *mutex); - -/** - * Initialize a read-write lock, bailing out on failure. - * @param[out] rwlock The read-write lock to initialize. - */ -DMNSN_INTERNAL void dmnsn_initialize_rwlock(pthread_rwlock_t *rwlock); - -/// dmnsn_read_lock() implementation. -DMNSN_INTERNAL void dmnsn_read_lock_impl(pthread_rwlock_t *rwlock); -/// dmnsn_write_lock() implementation. -DMNSN_INTERNAL void dmnsn_write_lock_impl(pthread_rwlock_t *rwlock); -/// dmnsn_unlock_rwlock() implementation. -DMNSN_INTERNAL void dmnsn_unlock_rwlock_impl(pthread_rwlock_t *rwlock); - -/** - * Read-lock a read-write lock, bailing out on failure. - * Contains a {, so must be used in the same block as dmnsn_unlock_rwlock(). - * @param[in,out] rwlock The read-write lock to lock. - */ -#define dmnsn_read_lock(rwlock) do { dmnsn_read_lock_impl((rwlock)) - -/** - * Write-lock a read-write lock, bailing out on failure. - * Contains a {, so must be used in the same block as dmnsn_unlock_rwlock(). - * @param[in,out] rwlock The read-write lock to lock. - */ -#define dmnsn_write_lock(rwlock) do { dmnsn_write_lock_impl((rwlock)) - -/** - * Unlock a read-write lock, bailing out on failure. - * Contains a }, so must be used in the same block as dmnsn_read_lock() or - * dmnsn_write_lock(). - * @param[in,out] rwlock The read-write lock to lock. - */ -#define dmnsn_unlock_rwlock(rwlock) \ - dmnsn_unlock_rwlock_impl((rwlock)); } while (0) - -/** - * Destroy a read-write lock, warning on failure. - * @param[in,out] rwlock The read-write lock to destroy. - */ -DMNSN_INTERNAL void dmnsn_destroy_rwlock(pthread_rwlock_t *rwlock); - -/** - * Initialize a condition variable, bailing out on failure. - * @param[out] cond The condition variable to initialize. - */ -DMNSN_INTERNAL void dmnsn_initialize_cond(pthread_cond_t *cond); - -/** - * Wait on a condition variable, bailing out on error. - * @param[in] cond The condition variable to wait on. - * @param[in] mutex The associated mutex. - */ -DMNSN_INTERNAL void dmnsn_cond_wait(pthread_cond_t *cond, - pthread_mutex_t *mutex); - -/** - * Wait on a condition variable, bailing out on error, and unlock the mutex if - * cancelled. - * @param[in] cond The condition variable to wait on. - * @param[in] mutex The associated mutex. - */ -#define dmnsn_cond_wait_safely(cond, mutex) \ - do { \ - pthread_cleanup_push(dmnsn_unlock_mutex_impl, (mutex)); \ - dmnsn_cond_wait((cond), (mutex)); \ - pthread_cleanup_pop(false); \ - } while (0) - -/** - * Signal a condition variable, bailing out on error. - * @param[in] cond The condition variable to signal. - */ -DMNSN_INTERNAL void dmnsn_cond_broadcast(pthread_cond_t *cond); - -/** - * Destroy a condition variable, warning on failure. - * @param[in,out] cond The condition variable to destroy. - */ -DMNSN_INTERNAL void dmnsn_destroy_cond(pthread_cond_t *cond); - -/** - * Once-called callback type. - */ -typedef void dmnsn_once_fn(void); - -/** - * Call a function exactly once, bailing out on failure. - * @param[in,out] once The once control. - * @param[in] once_fn The function to call. - */ -DMNSN_INTERNAL void dmnsn_once(pthread_once_t *once, dmnsn_once_fn *once_fn); - -/** - * Initialize a thread-local storage key, bailing out on failure. - * @param[out] key The key to initialize. - * @param[in] destructor An optional destructor callback. - */ -DMNSN_INTERNAL void dmnsn_key_create(pthread_key_t *key, - dmnsn_callback_fn *destructor); - -/** - * Set a thread-specific pointer, bailing out on failure. - * @param[in] key The thread-local storage key. - * @param[in] value The value to set. - */ -DMNSN_INTERNAL void dmnsn_setspecific(pthread_key_t key, const void *value); - -/** - * Destroy a thread-local storage key, warning on failure. - * @param[out] key The key to destroy. - */ -DMNSN_INTERNAL void dmnsn_key_delete(pthread_key_t key); - -/** - * Join a thread, bailing out on failure. - * @param[in,out] thread The thread to join. - */ -DMNSN_INTERNAL void dmnsn_join_thread(pthread_t thread, void **retval); diff --git a/libdimension/timer.c b/libdimension/timer.c deleted file mode 100644 index 35bf05d..0000000 --- a/libdimension/timer.c +++ /dev/null @@ -1,42 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2011 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Performance counter. - */ - -#include "dimension-internal.h" - -void -dmnsn_timer_start(dmnsn_timer *timer) -{ - dmnsn_get_times(timer); -} - -void -dmnsn_timer_stop(dmnsn_timer *timer) -{ - dmnsn_timer now; - dmnsn_get_times(&now); - timer->real = now.real - timer->real; - timer->user = now.user - timer->user; - timer->system = now.system - timer->system; -} diff --git a/libdimension/torus.c b/libdimension/torus.c deleted file mode 100644 index 30acbd8..0000000 --- a/libdimension/torus.c +++ /dev/null @@ -1,172 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Torii. A special case of a quartic. - */ - -#include "dimension.h" - -/// Torus type. -typedef struct { - dmnsn_object object; - double major, minor; -} dmnsn_torus; - -/// Bound the torus in a cylindrical shell. -static inline bool -dmnsn_torus_bound_intersection(const dmnsn_torus *torus, dmnsn_line l) -{ - double R = torus->major, r = torus->minor; - double rmax = R + r, rmin = R - r; - double rmax2 = rmax*rmax, rmin2 = rmin*rmin; - - // Try the caps first - double tlower = (-r - l.x0.y)/l.n.y; - double tupper = (+r - l.x0.y)/l.n.y; - dmnsn_vector lower = dmnsn_line_point(l, tlower); - dmnsn_vector upper = dmnsn_line_point(l, tupper); - double ldist2 = lower.x*lower.x + lower.z*lower.z; - double udist2 = upper.x*upper.x + upper.z*upper.z; - if ((ldist2 < rmin2 || ldist2 > rmax2) && (udist2 < rmin2 || udist2 > rmax2)) { - // No valid intersection with the caps, try the cylinder walls - double dist2 = l.x0.x*l.x0.x + l.x0.z*l.x0.z; - double bigcyl[3], smallcyl[3]; - bigcyl[2] = smallcyl[2] = l.n.x*l.n.x + l.n.z*l.n.z; - bigcyl[1] = smallcyl[1] = 2.0*(l.n.x*l.x0.x + l.n.z*l.x0.z); - bigcyl[0] = dist2 - rmax2; - smallcyl[0] = dist2 - rmin2; - - double x[4]; - size_t n = dmnsn_polynomial_solve(bigcyl, 2, x); - n += dmnsn_polynomial_solve(smallcyl, 2, x + n); - - size_t i; - for (i = 0; i < n; ++i) { - dmnsn_vector p = dmnsn_line_point(l, x[i]); - if (p.y >= -r && p.y <= r) - break; - } - - if (i == n) { - // No valid intersection found - return false; - } - } - - return true; -} - -/// Torus intersection callback. -static bool -dmnsn_torus_intersection_fn(const dmnsn_object *object, dmnsn_line l, - dmnsn_intersection *intersection) -{ - const dmnsn_torus *torus = (const dmnsn_torus *)object; - double R = torus->major, r = torus->minor; - double RR = R*R, rr = r*r; - - if (!dmnsn_torus_bound_intersection(torus, l)) { - return false; - } - - // This bit of algebra here is correct - dmnsn_vector x0mod = dmnsn_new_vector(l.x0.x, -l.x0.y, l.x0.z); - dmnsn_vector nmod = dmnsn_new_vector(l.n.x, -l.n.y, l.n.z); - double nn = dmnsn_vector_dot(l.n, l.n); - double nx0 = dmnsn_vector_dot(l.n, l.x0); - double x0x0 = dmnsn_vector_dot(l.x0, l.x0); - double x0x0mod = dmnsn_vector_dot(l.x0, x0mod); - double nx0mod = dmnsn_vector_dot(l.n, x0mod); - double nnmod = dmnsn_vector_dot(l.n, nmod); - - double poly[5]; - poly[4] = nn*nn; - poly[3] = 4*nn*nx0; - poly[2] = 2.0*(nn*(x0x0 - rr) + 2.0*nx0*nx0 - RR*nnmod); - poly[1] = 4.0*(nx0*(x0x0 - rr) - RR*nx0mod); - poly[0] = x0x0*x0x0 + RR*(RR - 2.0*x0x0mod) - rr*(2.0*(RR + x0x0) - rr); - - double x[4]; - size_t n = dmnsn_polynomial_solve(poly, 4, x); - if (n == 0) - return false; - - double t = x[0]; - for (size_t i = 1; i < n; ++i) { - t = dmnsn_min(t, x[i]); - } - - if (t < 0.0) { - return false; - } - - dmnsn_vector p = dmnsn_line_point(l, t); - dmnsn_vector center = dmnsn_vector_mul( - R, - dmnsn_vector_normalized(dmnsn_new_vector(p.x, 0.0, p.z)) - ); - dmnsn_vector normal = dmnsn_vector_sub(p, center); - - intersection->t = t; - intersection->normal = normal; - return true; -} - -/// Torus inside callback. -static bool -dmnsn_torus_inside_fn(const dmnsn_object *object, dmnsn_vector point) -{ - const dmnsn_torus *torus = (const dmnsn_torus *)object; - double dmajor = torus->major - sqrt(point.x*point.x + point.z*point.z); - return dmajor*dmajor + point.y*point.y < torus->minor*torus->minor; -} - -/// Torus bounding callback. -static dmnsn_bounding_box -dmnsn_torus_bounding_fn(const dmnsn_object *object, dmnsn_matrix trans) -{ - const dmnsn_torus *torus = (const dmnsn_torus *)object; - - double extent = torus->major + torus->minor; - dmnsn_bounding_box box = dmnsn_symmetric_bounding_box(dmnsn_new_vector(extent, torus->minor, extent)); - return dmnsn_transform_bounding_box(trans, box); -} - -/// Torus vtable. -static const dmnsn_object_vtable dmnsn_torus_vtable = { - .intersection_fn = dmnsn_torus_intersection_fn, - .inside_fn = dmnsn_torus_inside_fn, - .bounding_fn = dmnsn_torus_bounding_fn, -}; - -dmnsn_object * -dmnsn_new_torus(dmnsn_pool *pool, double major, double minor) -{ - dmnsn_torus *torus = DMNSN_PALLOC(pool, dmnsn_torus); - torus->major = major; - torus->minor = minor; - - dmnsn_object *object = &torus->object; - dmnsn_init_object(object); - object->vtable = &dmnsn_torus_vtable; - return object; -} diff --git a/libdimension/triangle.c b/libdimension/triangle.c deleted file mode 100644 index fdd2b96..0000000 --- a/libdimension/triangle.c +++ /dev/null @@ -1,162 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Triangles. See - * http://tavianator.com/2014/05/a-beautiful-raytriangle-intersection-method/ - * for a description of the intersection algorithm. - */ - -#include "dimension-internal.h" - -/// Optimized ray/triangle intersection test. -static inline bool -dmnsn_ray_triangle_intersection(dmnsn_line l, double *t, double *u, double *v) -{ - // See the change of basis in dmnsn_triangle_basis() - *t = -l.x0.z/l.n.z; - *u = l.x0.x + (*t)*l.n.x; - *v = l.x0.y + (*t)*l.n.y; - return *t >= 0.0 && *u >= 0.0 && *v >= 0.0 && *u + *v <= 1.0; -} - -/// Triangle intersection callback. -DMNSN_HOT static bool -dmnsn_triangle_intersection_fn(const dmnsn_object *object, dmnsn_line l, - dmnsn_intersection *intersection) -{ - double t, u, v; - if (dmnsn_ray_triangle_intersection(l, &t, &u, &v)) { - intersection->t = t; - intersection->normal = dmnsn_z; - return true; - } - - return false; -} - -/// Triangle inside callback. -static bool -dmnsn_triangle_inside_fn(const dmnsn_object *object, dmnsn_vector point) -{ - return false; -} - -/// Triangle bounding callback. -static dmnsn_bounding_box -dmnsn_triangle_bounding_fn(const dmnsn_object *object, dmnsn_matrix trans) -{ - dmnsn_vector a = dmnsn_transform_point(trans, dmnsn_zero); - dmnsn_vector b = dmnsn_transform_point(trans, dmnsn_x); - dmnsn_vector c = dmnsn_transform_point(trans, dmnsn_y); - - dmnsn_bounding_box box = dmnsn_new_bounding_box(a, a); - box = dmnsn_bounding_box_swallow(box, b); - box = dmnsn_bounding_box_swallow(box, c); - return box; -} - -/// Triangle vtable. -static const dmnsn_object_vtable dmnsn_triangle_vtable = { - .intersection_fn = dmnsn_triangle_intersection_fn, - .inside_fn = dmnsn_triangle_inside_fn, - .bounding_fn = dmnsn_triangle_bounding_fn, -}; - -/// Smooth triangle type. -typedef struct { - dmnsn_object object; - dmnsn_vector na, nab, nac; -} dmnsn_smooth_triangle; - -/// Smooth triangle intersection callback. -DMNSN_HOT static bool -dmnsn_smooth_triangle_intersection_fn(const dmnsn_object *object, dmnsn_line l, - dmnsn_intersection *intersection) -{ - const dmnsn_smooth_triangle *triangle = (const dmnsn_smooth_triangle *)object; - - double t, u, v; - if (dmnsn_ray_triangle_intersection(l, &t, &u, &v)) { - intersection->t = t; - intersection->normal = dmnsn_vector_add( - triangle->na, - dmnsn_vector_add( - dmnsn_vector_mul(u, triangle->nab), - dmnsn_vector_mul(v, triangle->nac) - ) - ); - return true; - } - - return false; -} - -/// Smooth triangle vtable. -static const dmnsn_object_vtable dmnsn_smooth_triangle_vtable = { - .intersection_fn = dmnsn_smooth_triangle_intersection_fn, - .inside_fn = dmnsn_triangle_inside_fn, - .bounding_fn = dmnsn_triangle_bounding_fn, -}; - -/// Make a change-of-basis matrix. -static inline dmnsn_matrix -dmnsn_triangle_basis(dmnsn_vector vertices[3]) -{ - // The new vector space has corners at <1, 0, 0>, <0, 1, 0>, and 0, - // corresponding to the basis (ab, ac, ab X ac). - dmnsn_vector ab = dmnsn_vector_sub(vertices[1], vertices[0]); - dmnsn_vector ac = dmnsn_vector_sub(vertices[2], vertices[0]); - dmnsn_vector normal = dmnsn_vector_cross(ab, ac); - return dmnsn_new_matrix4(ab, ac, normal, vertices[0]); -} - -dmnsn_object * -dmnsn_new_triangle(dmnsn_pool *pool, dmnsn_vector vertices[3]) -{ - dmnsn_object *object = dmnsn_new_object(pool); - object->vtable = &dmnsn_triangle_vtable; - object->intrinsic_trans = dmnsn_triangle_basis(vertices); - return object; -} - -dmnsn_object * -dmnsn_new_smooth_triangle(dmnsn_pool *pool, dmnsn_vector vertices[3], dmnsn_vector normals[3]) -{ - dmnsn_matrix P = dmnsn_triangle_basis(vertices); - - // Transform the given normals. - dmnsn_vector na = dmnsn_vector_normalized(dmnsn_transform_normal(P, normals[0])); - dmnsn_vector nb = dmnsn_vector_normalized(dmnsn_transform_normal(P, normals[1])); - dmnsn_vector nc = dmnsn_vector_normalized(dmnsn_transform_normal(P, normals[2])); - - dmnsn_smooth_triangle *triangle = DMNSN_PALLOC(pool, dmnsn_smooth_triangle); - triangle->na = na; - triangle->nab = dmnsn_vector_sub(nb, na); - triangle->nac = dmnsn_vector_sub(nc, na); - - dmnsn_object *object = &triangle->object; - dmnsn_init_object(object); - object->vtable = &dmnsn_smooth_triangle_vtable; - object->intrinsic_trans = P; - - return object; -} diff --git a/libdimension/triangle_fan.c b/libdimension/triangle_fan.c deleted file mode 100644 index 9940614..0000000 --- a/libdimension/triangle_fan.c +++ /dev/null @@ -1,346 +0,0 @@ -/************************************************************************* - * Copyright (C) 2014 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Triangle fans. See - * http://tavianator.com/2014/05/a-beautiful-raymesh-intersection-algorithm/ - * for a description of the intersection algorithm. - */ - -#include "dimension-internal.h" - -/// Triangle fan type. -typedef struct { - dmnsn_object object; - size_t ncoeffs; - double coeffs[][6]; -} dmnsn_triangle_fan; - -/// Change basis from one triangle to the next. -static inline dmnsn_vector -dmnsn_change_basis(const double coeffs[6], dmnsn_vector v) -{ - return dmnsn_new_vector( - coeffs[0]*v.x + coeffs[1]*v.z + v.y, - coeffs[2]*v.x + coeffs[3]*v.z, - coeffs[4]*v.x + coeffs[5]*v.z - ); -} - -/// Change basis from one triangle to the next for a normal vector. -static inline dmnsn_vector -dmnsn_change_normal_basis(const double coeffs[6], dmnsn_vector n) -{ - return dmnsn_new_vector( - coeffs[0]*n.x + coeffs[2]*n.y + coeffs[4]*n.z, - n.x, - coeffs[1]*n.x + coeffs[3]*n.y + coeffs[5]*n.z - ); -} - -/// Change basis from one triangle to the next for a line -static inline dmnsn_line -dmnsn_change_line_basis(const double coeffs[6], dmnsn_line l) -{ - return dmnsn_new_line(dmnsn_change_basis(coeffs, l.x0), dmnsn_change_basis(coeffs, l.n)); -} - -/// Store the compressed incremental matrix. -static inline void -dmnsn_compress_coeffs(double coeffs[6], dmnsn_matrix incremental) -{ - coeffs[0] = incremental.n[0][0]; - coeffs[1] = incremental.n[0][2]; - coeffs[2] = incremental.n[1][0]; - coeffs[3] = incremental.n[1][2]; - coeffs[4] = incremental.n[2][0]; - coeffs[5] = incremental.n[2][2]; -} - -/// Decompress the incremental matrix. -static inline dmnsn_matrix -dmnsn_decompress_coeffs(const double coeffs[6]) -{ - dmnsn_matrix incremental = dmnsn_new_matrix( - coeffs[0], 1.0, coeffs[1], 0.0, - coeffs[2], 0.0, coeffs[3], 0.0, - coeffs[4], 0.0, coeffs[5], 0.0 - ); - return incremental; -} - -/// Make a change-of-basis matrix for a triangle. -static inline dmnsn_matrix -dmnsn_triangle_basis(dmnsn_vector a, dmnsn_vector ab, dmnsn_vector ac) -{ - dmnsn_vector normal = dmnsn_vector_cross(ab, ac); - return dmnsn_new_matrix4(ab, ac, normal, a); -} - -/// Optimized ray/triangle intersection test. -static inline bool -dmnsn_ray_triangle_intersection(dmnsn_line l, double *t, double *u, double *v) -{ - *t = -l.x0.z/l.n.z; - *u = l.x0.x + (*t)*l.n.x; - *v = l.x0.y + (*t)*l.n.y; - return *t >= 0.0 && *u >= 0.0 && *v >= 0.0 && *u + *v <= 1.0; -} - -/// Triangle fan intersection callback. -DMNSN_HOT static bool -dmnsn_triangle_fan_intersection_fn(const dmnsn_object *object, dmnsn_line l, dmnsn_intersection *intersection) -{ - const dmnsn_triangle_fan *fan = (const dmnsn_triangle_fan *)object; - - double t, u, v; - - double best_t = INFINITY; - if (dmnsn_ray_triangle_intersection(l, &t, &u, &v)) { - best_t = t; - } - - dmnsn_vector normal = dmnsn_z; - dmnsn_vector best_normal = normal; - - for (size_t i = 0; i < fan->ncoeffs; ++i) { - const double *coeffs = fan->coeffs[i]; - l = dmnsn_change_line_basis(coeffs, l); - normal = dmnsn_change_normal_basis(coeffs, normal); - - if (dmnsn_ray_triangle_intersection(l, &t, &u, &v) && t < best_t) { - best_t = t; - best_normal = normal; - } - } - - if (!isinf(best_t)) { - intersection->t = t; - intersection->normal = best_normal; - return true; - } - - return false; -} - -/// Triangle fan inside callback. -static bool -dmnsn_triangle_fan_inside_fn(const dmnsn_object *object, dmnsn_vector point) -{ - return false; -} - -/// Computes the bounding box for the first triangle -static inline dmnsn_bounding_box -dmnsn_bound_first_triangle(dmnsn_matrix trans) -{ - dmnsn_vector a = dmnsn_transform_point(trans, dmnsn_zero); - dmnsn_vector b = dmnsn_transform_point(trans, dmnsn_x); - dmnsn_vector c = dmnsn_transform_point(trans, dmnsn_y); - - dmnsn_bounding_box box = dmnsn_new_bounding_box(a, a); - box = dmnsn_bounding_box_swallow(box, b); - box = dmnsn_bounding_box_swallow(box, c); - - return box; -} - -/// Triangle fan bounding callback. -static dmnsn_bounding_box -dmnsn_triangle_fan_bounding_fn(const dmnsn_object *object, dmnsn_matrix trans) -{ - const dmnsn_triangle_fan *fan = (const dmnsn_triangle_fan *)object; - - dmnsn_bounding_box box = dmnsn_bound_first_triangle(trans); - - for (size_t i = 0; i < fan->ncoeffs; ++i) { - dmnsn_matrix incremental = dmnsn_decompress_coeffs(fan->coeffs[i]); - trans = dmnsn_matrix_mul(trans, dmnsn_matrix_inverse(incremental)); - dmnsn_vector vertex = dmnsn_transform_point(trans, dmnsn_y); - box = dmnsn_bounding_box_swallow(box, vertex); - } - - return box; -} - -/// Triangle fan vtable. -static dmnsn_object_vtable dmnsn_triangle_fan_vtable = { - .intersection_fn = dmnsn_triangle_fan_intersection_fn, - .inside_fn = dmnsn_triangle_fan_inside_fn, - .bounding_fn = dmnsn_triangle_fan_bounding_fn, -}; - -/// Smooth triangle fan type. -typedef struct dmnsn_smooth_triangle_fan { - dmnsn_object object; - dmnsn_vector na, nab, nac; - size_t ncoeffs; - double coeffs[][9]; ///< 0-6 is same as dmnsn_triangle_fan, 6-9 is the normal -} dmnsn_smooth_triangle_fan; - -/// Smooth triangle fan intersection callback. -DMNSN_HOT static bool -dmnsn_smooth_triangle_fan_intersection_fn(const dmnsn_object *object, dmnsn_line l, dmnsn_intersection *intersection) -{ - const dmnsn_smooth_triangle_fan *fan = (const dmnsn_smooth_triangle_fan *)object; - - dmnsn_vector nab = fan->nab; - dmnsn_vector nac = fan->nac; - - double t, u, v; - - double best_t = INFINITY; - dmnsn_vector best_normal; - if (dmnsn_ray_triangle_intersection(l, &t, &u, &v)) { - best_t = t; - best_normal = dmnsn_vector_add(dmnsn_vector_mul(u, nab), dmnsn_vector_mul(v, nac)); - } - - for (size_t i = 0; i < fan->ncoeffs; ++i) { - const double *coeffs = fan->coeffs[i]; - l = dmnsn_change_line_basis(coeffs, l); - nab = nac; - nac = dmnsn_new_vector(coeffs[6], coeffs[7], coeffs[8]); - - if (dmnsn_ray_triangle_intersection(l, &t, &u, &v) && t < best_t) { - best_t = t; - best_normal = dmnsn_vector_add(dmnsn_vector_mul(u, nab), dmnsn_vector_mul(v, nac)); - } - } - - if (!isinf(best_t)) { - intersection->t = t; - intersection->normal = dmnsn_vector_add(fan->na, best_normal); - return true; - } - - return false; -} - -/// Smooth triangle fan bounding callback. -static dmnsn_bounding_box -dmnsn_smooth_triangle_fan_bounding_fn(const dmnsn_object *object, dmnsn_matrix trans) -{ - const dmnsn_smooth_triangle_fan *fan = (const dmnsn_smooth_triangle_fan *)object; - - dmnsn_bounding_box box = dmnsn_bound_first_triangle(trans); - - for (size_t i = 0; i < fan->ncoeffs; ++i) { - dmnsn_matrix incremental = dmnsn_decompress_coeffs(fan->coeffs[i]); - trans = dmnsn_matrix_mul(trans, dmnsn_matrix_inverse(incremental)); - dmnsn_vector vertex = dmnsn_transform_point(trans, dmnsn_y); - box = dmnsn_bounding_box_swallow(box, vertex); - } - - return box; -} - -/// Smooth triangle fan vtable. -static dmnsn_object_vtable dmnsn_smooth_triangle_fan_vtable = { - .intersection_fn = dmnsn_smooth_triangle_fan_intersection_fn, - .inside_fn = dmnsn_triangle_fan_inside_fn, - .bounding_fn = dmnsn_smooth_triangle_fan_bounding_fn, -}; - -dmnsn_object * -dmnsn_new_triangle_fan(dmnsn_pool *pool, dmnsn_vector vertices[], size_t nvertices) -{ - dmnsn_assert(nvertices >= 3, "Not enough vertices for one triangle"); - - size_t ncoeffs = nvertices - 3; - dmnsn_triangle_fan *fan = dmnsn_palloc(pool, sizeof(dmnsn_triangle_fan) + ncoeffs*sizeof(double[6])); - fan->ncoeffs = ncoeffs; - - dmnsn_object *object = &fan->object; - dmnsn_init_object(object); - object->vtable = &dmnsn_triangle_fan_vtable; - - // Compute the initial matrix and the coefficients - dmnsn_vector a = vertices[0]; - dmnsn_vector ab = dmnsn_vector_sub(vertices[1], a); - dmnsn_vector ac = dmnsn_vector_sub(vertices[2], a); - dmnsn_matrix P = dmnsn_triangle_basis(a, ab, ac); - object->intrinsic_trans = P; - - for (size_t i = 0; i < ncoeffs; ++i) { - ab = ac; - ac = dmnsn_vector_sub(vertices[i + 3], a); - - dmnsn_matrix newP = dmnsn_triangle_basis(a, ab, ac); - dmnsn_matrix incremental = dmnsn_matrix_mul(dmnsn_matrix_inverse(newP), P); - dmnsn_compress_coeffs(fan->coeffs[i], incremental); - - P = newP; - } - - return object; -} - -dmnsn_object * -dmnsn_new_smooth_triangle_fan(dmnsn_pool *pool, dmnsn_vector vertices[], dmnsn_vector normals[], size_t nvertices) -{ - dmnsn_assert(nvertices >= 3, "Not enough vertices for one triangle"); - - size_t ncoeffs = nvertices - 3; - dmnsn_smooth_triangle_fan *fan = dmnsn_palloc(pool, sizeof(dmnsn_smooth_triangle_fan) + ncoeffs*sizeof(double[9])); - fan->ncoeffs = ncoeffs; - - dmnsn_object *object = &fan->object; - dmnsn_init_object(object); - object->vtable = &dmnsn_smooth_triangle_fan_vtable; - - // Compute the initial matrix - dmnsn_vector a = vertices[0]; - dmnsn_vector ab = dmnsn_vector_sub(vertices[1], a); - dmnsn_vector ac = dmnsn_vector_sub(vertices[2], a); - dmnsn_matrix P = dmnsn_triangle_basis(a, ab, ac); - dmnsn_matrix Pabc = P; - object->intrinsic_trans = P; - - // Transform the first three normals - dmnsn_vector na = dmnsn_vector_normalized(dmnsn_transform_normal(P, normals[0])); - dmnsn_vector nb = dmnsn_vector_normalized(dmnsn_transform_normal(P, normals[1])); - dmnsn_vector nc = dmnsn_vector_normalized(dmnsn_transform_normal(P, normals[2])); - fan->na = na; - fan->nab = dmnsn_vector_sub(nb, na); - fan->nac = dmnsn_vector_sub(nc, na); - - // Compute the coefficients - for (size_t i = 0; i < ncoeffs; ++i) { - ab = ac; - ac = dmnsn_vector_sub(vertices[i + 3], a); - - dmnsn_matrix newP = dmnsn_triangle_basis(a, ab, ac); - dmnsn_matrix incremental = dmnsn_matrix_mul(dmnsn_matrix_inverse(newP), P); - double *coeffs = fan->coeffs[i]; - dmnsn_compress_coeffs(coeffs, incremental); - - nc = dmnsn_vector_normalized(dmnsn_transform_normal(Pabc, normals[i + 3])); - dmnsn_vector nac = dmnsn_vector_sub(nc, na); - coeffs[6] = nac.x; - coeffs[7] = nac.y; - coeffs[8] = nac.z; - - P = newP; - } - - return object; -} -- cgit v1.2.3