Browse Source

Merge pull request #14 from Azaezel/Enumnanigans

corrections
Areloch 1 year ago
parent
commit
320d5dd667
100 changed files with 12825 additions and 24736 deletions
  1. 0 20
      .github/actions/upload-artifact/action.yml
  2. 11 4
      .github/workflows/build-linux-gcc.yml
  3. 11 4
      .github/workflows/build-macos-clang.yml
  4. 11 4
      .github/workflows/build-windows-msvc.yml
  5. 0 39
      .github/workflows/test-results-linux.yml
  6. 0 39
      .github/workflows/test-results-mac.yml
  7. 0 39
      .github/workflows/test-results-windows.yml
  8. 82 37
      Engine/lib/CMakeLists.txt
  9. 1 1
      Engine/lib/Torque_postBuild.cmake
  10. 0 7
      Engine/lib/convexDecomp/CMakeLists.txt
  11. 0 252
      Engine/lib/convexDecomp/NvConcavityVolume.cpp
  12. 0 78
      Engine/lib/convexDecomp/NvConcavityVolume.h
  13. 0 788
      Engine/lib/convexDecomp/NvConvexDecomposition.cpp
  14. 0 111
      Engine/lib/convexDecomp/NvConvexDecomposition.h
  15. 0 74
      Engine/lib/convexDecomp/NvFloatMath.cpp
  16. 0 586
      Engine/lib/convexDecomp/NvFloatMath.h
  17. 0 5607
      Engine/lib/convexDecomp/NvFloatMath.inl
  18. 0 1905
      Engine/lib/convexDecomp/NvHashMap.h
  19. 0 783
      Engine/lib/convexDecomp/NvMeshIslandGeneration.cpp
  20. 0 91
      Engine/lib/convexDecomp/NvMeshIslandGeneration.h
  21. 0 153
      Engine/lib/convexDecomp/NvRayCast.cpp
  22. 0 79
      Engine/lib/convexDecomp/NvRayCast.h
  23. 0 713
      Engine/lib/convexDecomp/NvRemoveTjunctions.cpp
  24. 0 110
      Engine/lib/convexDecomp/NvRemoveTjunctions.h
  25. 0 189
      Engine/lib/convexDecomp/NvSimpleTypes.h
  26. 0 224
      Engine/lib/convexDecomp/NvSplitMesh.cpp
  27. 0 88
      Engine/lib/convexDecomp/NvSplitMesh.h
  28. 0 3464
      Engine/lib/convexDecomp/NvStanHull.cpp
  29. 0 201
      Engine/lib/convexDecomp/NvStanHull.h
  30. 0 511
      Engine/lib/convexDecomp/NvThreadConfig.cpp
  31. 0 119
      Engine/lib/convexDecomp/NvThreadConfig.h
  32. 0 81
      Engine/lib/convexDecomp/NvUserMemAlloc.h
  33. 0 38
      Engine/lib/convexDecomp/readme.txt
  34. 0 852
      Engine/lib/convexDecomp/wavefront.cpp
  35. 0 77
      Engine/lib/convexDecomp/wavefront.h
  36. 3 0
      Engine/lib/convexMath/CMakeLists.txt
  37. 17 0
      Engine/lib/convexMath/FloatMath.cpp
  38. 525 0
      Engine/lib/convexMath/FloatMath.h
  39. 5280 0
      Engine/lib/convexMath/FloatMath.inl
  40. 41 5
      Engine/lib/openal-soft/.github/workflows/ci.yml
  41. 1 1
      Engine/lib/openal-soft/.github/workflows/makemhr.yml
  42. 1 0
      Engine/lib/openal-soft/.gitignore
  43. 201 166
      Engine/lib/openal-soft/CMakeLists.txt
  44. 38 0
      Engine/lib/openal-soft/LICENSE-pffft
  45. 1 1
      Engine/lib/openal-soft/OpenALConfig.cmake.in
  46. 27 0
      Engine/lib/openal-soft/README.md
  47. 366 403
      Engine/lib/openal-soft/al/auxeffectslot.cpp
  48. 59 41
      Engine/lib/openal-soft/al/auxeffectslot.h
  49. 410 428
      Engine/lib/openal-soft/al/buffer.cpp
  50. 27 7
      Engine/lib/openal-soft/al/buffer.h
  51. 618 0
      Engine/lib/openal-soft/al/debug.cpp
  52. 70 0
      Engine/lib/openal-soft/al/debug.h
  53. 127 0
      Engine/lib/openal-soft/al/direct_defs.h
  54. 44 15
      Engine/lib/openal-soft/al/eax/api.h
  55. 1 2
      Engine/lib/openal-soft/al/eax/call.cpp
  56. 17 17
      Engine/lib/openal-soft/al/eax/call.h
  57. 182 143
      Engine/lib/openal-soft/al/eax/effect.h
  58. 10 37
      Engine/lib/openal-soft/al/eax/exception.cpp
  59. 10 5
      Engine/lib/openal-soft/al/eax/exception.h
  60. 3 4
      Engine/lib/openal-soft/al/eax/fx_slot_index.h
  61. 4 10
      Engine/lib/openal-soft/al/eax/fx_slots.h
  62. 0 21
      Engine/lib/openal-soft/al/eax/globals.cpp
  63. 2 18
      Engine/lib/openal-soft/al/eax/globals.h
  64. 4 4
      Engine/lib/openal-soft/al/eax/utils.cpp
  65. 6 6
      Engine/lib/openal-soft/al/eax/utils.h
  66. 2 10
      Engine/lib/openal-soft/al/eax/x_ram.h
  67. 336 367
      Engine/lib/openal-soft/al/effect.cpp
  68. 40 12
      Engine/lib/openal-soft/al/effect.h
  69. 72 84
      Engine/lib/openal-soft/al/effects/autowah.cpp
  70. 108 186
      Engine/lib/openal-soft/al/effects/chorus.cpp
  71. 35 44
      Engine/lib/openal-soft/al/effects/compressor.cpp
  72. 54 30
      Engine/lib/openal-soft/al/effects/convolution.cpp
  73. 72 22
      Engine/lib/openal-soft/al/effects/dedicated.cpp
  74. 73 87
      Engine/lib/openal-soft/al/effects/distortion.cpp
  75. 73 87
      Engine/lib/openal-soft/al/effects/echo.cpp
  76. 0 6
      Engine/lib/openal-soft/al/effects/effects.cpp
  77. 40 62
      Engine/lib/openal-soft/al/effects/effects.h
  78. 108 142
      Engine/lib/openal-soft/al/effects/equalizer.cpp
  79. 77 74
      Engine/lib/openal-soft/al/effects/fshifter.cpp
  80. 80 87
      Engine/lib/openal-soft/al/effects/modulator.cpp
  81. 29 35
      Engine/lib/openal-soft/al/effects/null.cpp
  82. 49 57
      Engine/lib/openal-soft/al/effects/pshifter.cpp
  83. 267 391
      Engine/lib/openal-soft/al/effects/reverb.cpp
  84. 92 156
      Engine/lib/openal-soft/al/effects/vmorpher.cpp
  85. 76 25
      Engine/lib/openal-soft/al/error.cpp
  86. 27 0
      Engine/lib/openal-soft/al/error.h
  87. 114 89
      Engine/lib/openal-soft/al/event.cpp
  88. 24 28
      Engine/lib/openal-soft/al/extension.cpp
  89. 394 486
      Engine/lib/openal-soft/al/filter.cpp
  90. 50 26
      Engine/lib/openal-soft/al/filter.h
  91. 203 245
      Engine/lib/openal-soft/al/listener.cpp
  92. 1 3
      Engine/lib/openal-soft/al/listener.h
  93. 969 1211
      Engine/lib/openal-soft/al/source.cpp
  94. 110 83
      Engine/lib/openal-soft/al/source.h
  95. 330 666
      Engine/lib/openal-soft/al/state.cpp
  96. 115 776
      Engine/lib/openal-soft/alc/alc.cpp
  97. 202 193
      Engine/lib/openal-soft/alc/alconfig.cpp
  98. 14 7
      Engine/lib/openal-soft/alc/alconfig.h
  99. 373 252
      Engine/lib/openal-soft/alc/alu.cpp
  100. 5 5
      Engine/lib/openal-soft/alc/alu.h

+ 0 - 20
.github/actions/upload-artifact/action.yml

@@ -1,20 +0,0 @@
-name: Upload Torque Test Report
-description: Upload Torques unit test artifact.
-inputs:
-    name:
-        description: The name of the unit test.
-        default: "${{github.job}}"
-    path:
-        description: The path to the upload.
-        required: true
-        default: "Torque3D/My Projects/Torque3D/game/test_detail.xml"
-runs:
-    using: "composite"
-    steps:
-      - name: Upload Torque Test Report
-        uses: actions/upload-artifact@v4
-        with:
-            name: ${{inputs.name}}
-            path: ${{inputs.path}}
-            retention-days: 1
-            overwrite: true

+ 11 - 4
.github/workflows/build-linux-gcc.yml

@@ -12,6 +12,10 @@ env:
 concurrency:
     group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-linux
     cancel-in-progress: true
+
+permissions:
+  checks: write
+
 jobs:
     build-linux:
         if: github.repository == 'TorqueGameEngines/Torque3D'
@@ -84,8 +88,11 @@ jobs:
                 cd "${{github.workspace}}/My Projects/Torque3D/game"
                 ./Torque3D runTests.tscript
 
-            - name: Upload Artifact
-              uses: ./.github/actions/upload-artifact
+            - name: Test Reporter
+              uses: phoenix-actions/test-reporting@v14
               with:
-                name: torque3dLinuxGCCUnitTest
-                path: "${{github.workspace}}/My Projects/Torque3D/game/test_detail.xml"
+                name: Linux test results
+                path: "**/My Projects/Torque3D/game/test_detail.xml"
+                reporter: java-junit
+                fail-on-error: false
+              if: github.event_name != 'pull_request'

+ 11 - 4
.github/workflows/build-macos-clang.yml

@@ -12,6 +12,10 @@ env:
 concurrency:
     group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-macosx
     cancel-in-progress: true
+
+permissions:
+  checks: write
+
 jobs:
     build-linux:
         if: github.repository == 'TorqueGameEngines/Torque3D'
@@ -65,8 +69,11 @@ jobs:
                 cd "${{github.workspace}}/My Projects/Torque3D/game"
                 ./Torque3D.app/Contents/MacOS/Torque3D runTests.tscript
 
-            - name: Upload Artifact
-              uses: ./.github/actions/upload-artifact
+            - name: Test Reporter
+              uses: phoenix-actions/test-reporting@v14
               with:
-                name: torque3dMacOSXCLANGUnitTest
-                path: "${{github.workspace}}/My Projects/Torque3D/game/test_detail.xml"
+                name: MacOS Test results
+                path: "**/My Projects/Torque3D/game/test_detail.xml"
+                reporter: java-junit
+                fail-on-error: false
+              if: github.event_name != 'pull_request'

+ 11 - 4
.github/workflows/build-windows-msvc.yml

@@ -7,6 +7,10 @@ on:
 concurrency:
     group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-windows
     cancel-in-progress: true
+
+permissions:
+  checks: write
+
 jobs:
     build-windows:
         if: github.repository == 'TorqueGameEngines/Torque3D'
@@ -61,8 +65,11 @@ jobs:
                 cd "${{github.workspace}}/My Projects/Torque3D/game"
                 ./Torque3D.exe runTests.tscript
 
-            - name: Upload Artifact
-              uses: ./.github/actions/upload-artifact
+            - name: Test Reporter
+              uses: phoenix-actions/test-reporting@v14
               with:
-                name: torque3dWindowsMSVCUnitTest
-                path: "${{github.workspace}}/My Projects/Torque3D/game/test_detail.xml"
+                name: Windows test results
+                path: "**/My Projects/Torque3D/game/test_detail.xml"
+                reporter: java-junit
+                fail-on-error: false
+              if: github.event_name != 'pull_request'

+ 0 - 39
.github/workflows/test-results-linux.yml

@@ -1,39 +0,0 @@
-name: Linux Test Results
-on:
-  workflow_run:
-    workflows: ["Linux Build"]
-    types:
-      - completed
-
-permissions:
-    checks: write
-
-jobs:
-    checks:
-        name: ${{matrix.config.name}}
-        runs-on: ${{matrix.config.runos}}
-        strategy:
-            fail-fast: false
-            matrix:
-              config: 
-              - {
-                  name: "Linux Test Results",
-                  runos: ubuntu-latest,
-                  artifact-name: "torque3dLinuxGCCUnitTest"
-                }
-              
-        steps:
-            - name: Download Linux Test Report
-              uses: dawidd6/action-download-artifact@v3
-              with:
-                path: Linux
-                name: ${{matrix.config.artifact-name}}
-                workflow: ${{ github.event.workflow.id }}
-                run_id: ${{ github.event.workflow_run.id }}
-
-            - name: Test Reporter
-              uses: phoenix-actions/test-reporting@v14
-              with:
-                name: ${{matrix.config.name}}
-                path: "**/My Projects/Torque3D/game/test_detail.xml"
-                reporter: java-junit

+ 0 - 39
.github/workflows/test-results-mac.yml

@@ -1,39 +0,0 @@
-name: Mac Test Results
-on:
-  workflow_run:
-    workflows: ["MacOSX Build"]
-    types:
-      - completed
-
-permissions:
-    checks: write
-
-jobs:
-    checks:
-        name: ${{matrix.config.name}}
-        runs-on: ${{matrix.config.runos}}
-        strategy:
-            fail-fast: false
-            matrix:
-              config:
-              - {
-                  name: "Mac Test Results",
-                  runos: macos-13,
-                  artifact-name: "torque3dMacOSXCLANGUnitTest"
-                }
-              
-        steps:
-            - name: Download Mac Test Report
-              uses: dawidd6/action-download-artifact@v3
-              with:
-                path: macOS
-                name: ${{matrix.config.artifact-name}}
-                workflow: ${{ github.event.workflow.id }}
-                run_id: ${{ github.event.workflow_run.id }}
-
-            - name: Test Reporter
-              uses: phoenix-actions/test-reporting@v14
-              with:
-                name: ${{matrix.config.name}}
-                path: "**/My Projects/Torque3D/game/test_detail.xml"
-                reporter: java-junit

+ 0 - 39
.github/workflows/test-results-windows.yml

@@ -1,39 +0,0 @@
-name: Windows Test Results
-on:
-  workflow_run:
-    workflows: ["Windows Build"]
-    types:
-      - completed
-
-permissions:
-    checks: write
-
-jobs:
-    checks:
-        name: ${{matrix.config.name}}
-        runs-on: ${{matrix.config.runos}}
-        strategy:
-            fail-fast: false
-            matrix:
-              config: 
-              - {
-                  name: "Windows Test Results",
-                  runos: windows-latest,
-                  artifact-name: "torque3dWindowsMSVCUnitTest"
-                }
-
-        steps:
-            - name: Download Windows Test Report
-              uses: dawidd6/action-download-artifact@v3
-              with:
-                path: Windows
-                name: ${{matrix.config.artifact-name}}
-                workflow: ${{ github.event.workflow.id }}
-                run_id: ${{ github.event.workflow_run.id }}
-
-            - name: Test Reporter
-              uses: phoenix-actions/test-reporting@v14
-              with:
-                name: ${{matrix.config.name}}
-                path: "**/My Projects/Torque3D/game/test_detail.xml"
-                reporter: java-junit

+ 82 - 37
Engine/lib/CMakeLists.txt

@@ -108,10 +108,11 @@ mark_as_advanced(SDL_WAYLAND)
 mark_as_advanced(SDL_WERROR)
 mark_as_advanced(SDL_X11)
 mark_as_advanced(SDL_XINPUT)
-
+mark_as_advanced(SDL2_DIR)
 add_subdirectory(sdl ${TORQUE_LIB_TARG_DIRECTORY}/sdl2 EXCLUDE_FROM_ALL)
 
 add_subdirectory(nativeFileDialogs ${TORQUE_LIB_TARG_DIRECTORY}/nfd EXCLUDE_FROM_ALL)
+add_subdirectory(convexMath ${TORQUE_LIB_TARG_DIRECTORY}/convexMath EXCLUDE_FROM_ALL)
 
 # Assimp
 advanced_option(ASSIMP_HUNTER_ENABLED "Enable Hunter package manager support" OFF)
@@ -189,7 +190,7 @@ mark_as_advanced(PNG_PREFIX)
 add_subdirectory(tinyxml ${TORQUE_LIB_TARG_DIRECTORY}/tinyxml EXCLUDE_FROM_ALL)
 add_subdirectory(opcode ${TORQUE_LIB_TARG_DIRECTORY}/opcode EXCLUDE_FROM_ALL)
 add_subdirectory(pcre ${TORQUE_LIB_TARG_DIRECTORY}/pcre EXCLUDE_FROM_ALL)
-add_subdirectory(convexDecomp ${TORQUE_LIB_TARG_DIRECTORY}/convexDecomp EXCLUDE_FROM_ALL)
+
 add_subdirectory(squish ${TORQUE_LIB_TARG_DIRECTORY}/squish EXCLUDE_FROM_ALL)
 add_subdirectory(collada ${TORQUE_LIB_TARG_DIRECTORY}/collada EXCLUDE_FROM_ALL)
 add_subdirectory(glad ${TORQUE_LIB_TARG_DIRECTORY}/glad EXCLUDE_FROM_ALL)
@@ -198,64 +199,59 @@ add_subdirectory(glad ${TORQUE_LIB_TARG_DIRECTORY}/glad EXCLUDE_FROM_ALL)
 if(TORQUE_SFX_OPENAL)
     advanced_option(TORQUE_OGGVORBIS "Enable OGG Vorbis" ON)
     advanced_option(ALSOFT_EAX "Enable legacy EAX extensions" ${WIN32})
-    advanced_option(ALSOFT_INSTALL_EXAMPLES "Install example programs (alplay, alstream, ...)" ON)
-    advanced_option(ALSOFT_INSTALL_UTILS "Install utility programs (openal-info, alsoft-config, ...)" ON)
+    advanced_option(ALSOFT_INSTALL_EXAMPLES "Install example programs (alplay, alstream, ...)" OFF)
+    advanced_option(ALSOFT_INSTALL_UTILS "Install utility programs (openal-info, alsoft-config, ...)" OFF)
     advanced_option(ALSOFT_UPDATE_BUILD_VERSION "Update git build version info" ON)
-    mark_as_advanced(ALSOFT_REQUIRE_OBOE)
-    mark_as_advanced(ALSOFT_REQUIRE_PIPEWIRE)
-    mark_as_advanced(ALSOFT_REQUIRE_RTKIT)
-    mark_as_advanced(ALSOFT_REQUIRE_SSE3)
-    #Hide some unnecessary fields as advanced
-    mark_as_advanced(ALSOFT_INSTALL_AMBDEC_PRESETS)
-    mark_as_advanced(ALSOFT_BACKEND_DSOUND)
-    mark_as_advanced(ALSOFT_BACKEND_MMDEVAPI)
+    mark_as_advanced(ALSOFT_BACKEND_COREAUDIO)
+    mark_as_advanced(ALSOFT_BACKEND_DSOUND)    
+    mark_as_advanced(ALSOFT_BACKEND_JACK)
+    mark_as_advanced(ALSOFT_BACKEND_OBOE)
+    mark_as_advanced(ALSOFT_BACKEND_OPENSL)
+    mark_as_advanced(ALSOFT_BACKEND_PIPEWIRE)
+    mark_as_advanced(ALSOFT_BACKEND_PORTAUDIO)
+    mark_as_advanced(ALSOFT_BACKEND_PULSEAUDIO)
+    mark_as_advanced(ALSOFT_BACKEND_SDL2)
+    mark_as_advanced(ALSOFT_BACKEND_WASAPI)
     mark_as_advanced(ALSOFT_BACKEND_WAVE)
     mark_as_advanced(ALSOFT_BACKEND_WINMM)
-    mark_as_advanced(ALSOFT_INSTALL_CONFIG)
+    mark_as_advanced(ALSOFT_BUILD_ROUTER)
+    mark_as_advanced(ALSOFT_CPUEXT_NEON)
     mark_as_advanced(ALSOFT_CPUEXT_SSE)
     mark_as_advanced(ALSOFT_CPUEXT_SSE2)
     mark_as_advanced(ALSOFT_CPUEXT_SSE3)
     mark_as_advanced(ALSOFT_CPUEXT_SSE4_1)
     mark_as_advanced(ALSOFT_DLOPEN)
-    mark_as_advanced(ALSOFT_EMBED_HRTF_DATA)
-    mark_as_advanced(ALSOFT_EXAMPLES)
-    mark_as_advanced(ALSOFT_INSTALL_HRTF_DATA)
+    mark_as_advanced(ALSOFT_EAX)
+    advanced_option(ALSOFT_EMBED_HRTF_DATA "Embed the HRTF data files (increases library footprint)" OFF)
+    advanced_option(ALSOFT_EXAMPLES  "Build example programs"  OFF)
     mark_as_advanced(ALSOFT_INSTALL)
+    advanced_option(ALSOFT_INSTALL_CONFIG "Install alsoft.conf sample configuration file" OFF)
+    advanced_option(ALSOFT_INSTALL_HRTF_DATA "Install HRTF data files" OFF)
+    advanced_option(ALSOFT_INSTALL_AMBDEC_PRESETS "Install AmbDec preset files" OFF)
+    advanced_option(ALSOFT_INSTALL_EXAMPLES "Install example programs (alplay, alstream, ...)" OFF)
+    advanced_option(ALSOFT_INSTALL_UTILS "Install utility programs (openal-info, alsoft-config, ...)" OFF)
+    mark_as_advanced(ALSOFT_UPDATE_BUILD_VERSION)
     mark_as_advanced(ALSOFT_NO_CONFIG_UTIL)
-    mark_as_advanced(ALSOFT_NO_UID_DEFS)
-    mark_as_advanced(ALSOFT_REQUIRE_ALSA)
     mark_as_advanced(ALSOFT_REQUIRE_COREAUDIO)
     mark_as_advanced(ALSOFT_REQUIRE_DSOUND)
     mark_as_advanced(ALSOFT_REQUIRE_JACK)
-    mark_as_advanced(ALSOFT_REQUIRE_MMDEVAPI)
     mark_as_advanced(ALSOFT_REQUIRE_NEON)
+    mark_as_advanced(ALSOFT_REQUIRE_OBOE)
     mark_as_advanced(ALSOFT_REQUIRE_OPENSL)
-    mark_as_advanced(ALSOFT_REQUIRE_OSS)
+    mark_as_advanced(ALSOFT_REQUIRE_PIPEWIRE)
     mark_as_advanced(ALSOFT_REQUIRE_PORTAUDIO)
     mark_as_advanced(ALSOFT_REQUIRE_PULSEAUDIO)
-    mark_as_advanced(ALSOFT_REQUIRE_QSA)
-    mark_as_advanced(ALSOFT_REQUIRE_SNDIO)
-    mark_as_advanced(ALSOFT_REQUIRE_SOLARIS)
+    mark_as_advanced(ALSOFT_REQUIRE_SDL2)
     mark_as_advanced(ALSOFT_REQUIRE_SSE)
     mark_as_advanced(ALSOFT_REQUIRE_SSE2)
+    mark_as_advanced(ALSOFT_REQUIRE_SSE3)
     mark_as_advanced(ALSOFT_REQUIRE_SSE4_1)
+    mark_as_advanced(ALSOFT_REQUIRE_WASAPI)
     mark_as_advanced(ALSOFT_REQUIRE_WINMM)
+    mark_as_advanced(ALSOFT_SEARCH_INSTALL_DATADIR)
     mark_as_advanced(ALSOFT_TESTS)
     mark_as_advanced(ALSOFT_UTILS)
     mark_as_advanced(ALSOFT_WERROR)
-    mark_as_advanced(COREAUDIO_FRAMEWORK)
-    mark_as_advanced(CMAKE_DEBUG_POSTFIX)
-    mark_as_advanced(FORCE_STATIC_VCRT)
-    mark_as_advanced(ALSOFT_BACKEND_WASAPI)
-    mark_as_advanced(ALSOFT_BUILD_ROUTER)
-    mark_as_advanced(ALSOFT_REQUIRE_SDL2)
-    mark_as_advanced(ALSOFT_REQUIRE_WASAPI)
-    mark_as_advanced(ALSOFT_BACKEND_COREAUDIO)
-    mark_as_advanced(ALSOFT_OSX_FRAMEWORK)
-    mark_as_advanced(ALSOFT_STATIC_LIBGCC)
-    mark_as_advanced(ALSOFT_STATIC_STDCXX)
-    mark_as_advanced(AUDIOTOOLBOX_LIBRARY)
-    mark_as_advanced(AUDIOUNIT_INCLUDE_DIR)
 endif()
 
 advanced_option(INSTALL_DOCS "" OFF)
@@ -272,9 +268,33 @@ add_subdirectory(flac ${TORQUE_LIB_TARG_DIRECTORY}/flac EXCLUDE_FROM_ALL)
 advanced_option(INSTALL_DOCS "" OFF)
 advanced_option(OPUS_INSTALL_PKG_CONFIG_MODULE "" OFF)
 advanced_option(OPUS_INSTALL_CMAKE_CONFIG_MODULE "" OFF)
+mark_as_advanced(OPUS_ASSERTIONS)
+mark_as_advanced(OPUS_BUILD_PROGRAMS)
+mark_as_advanced(OPUS_BUILD_SHARED_LIBRARY)
+mark_as_advanced(OPUS_BUILD_TESTING)
+mark_as_advanced(OPUS_CHECK_ASM)
+mark_as_advanced(OPUS_CUSTOM_MODES)
+mark_as_advanced(OPUS_DISABLE_INTRINSICS)
+mark_as_advanced(OPUS_ENABLE_FLOAT_API)
+mark_as_advanced(OPUS_FIXED_POINT)
+mark_as_advanced(OPUS_FLOAT_APPROX)
+mark_as_advanced(OPUS_FUZZING)
+mark_as_advanced(OPUS_HARDENING)
+mark_as_advanced(OPUS_STACK_PROTECTOR)
+mark_as_advanced(OPUS_USE_ALLOCA)
+mark_as_advanced(OPUS_X86_MAY_HAVE_AVX)
+mark_as_advanced(OPUS_X86_MAY_HAVE_SSE)
+mark_as_advanced(OPUS_X86_MAY_HAVE_SSE2)
+mark_as_advanced(OPUS_X86_MAY_HAVE_SSE4_1)
+mark_as_advanced(OPUS_X86_PRESUME_AVX)
+mark_as_advanced(OPUS_X86_PRESUME_SSE)
+mark_as_advanced(OPUS_X86_PRESUME_SSE2)
+mark_as_advanced(OPUS_X86_PRESUME_SSE4_1)
 add_subdirectory(opus ${TORQUE_LIB_TARG_DIRECTORY}/opus EXCLUDE_FROM_ALL)
 add_subdirectory(libtheora ${TORQUE_LIB_TARG_DIRECTORY}/libtheora EXCLUDE_FROM_ALL)
 
+mark_as_advanced(PULSEAUDIO_INCLUDE_DIR)
+mark_as_advanced(PULSEAUDIO_LIBRARY)
 advanced_option(BUILD_PROGRAMS "" OFF)
 advanced_option(BUILD_EXAMPLES "" OFF)
 advanced_option(ENABLE_CPACK "" OFF)
@@ -287,4 +307,29 @@ add_subdirectory(libsndfile ${TORQUE_LIB_TARG_DIRECTORY}/libsndfile EXCLUDE_FROM
 if(TORQUE_TESTING)
     add_subdirectory(gtest ${TORQUE_LIB_TARG_DIRECTORY}/gtest EXCLUDE_FROM_ALL)
 endif()
-#endif()
+#endif()
+
+#misc randoms
+mark_as_advanced(WINDRES)
+mark_as_advanced(AUDIOUNIT_INCLUDE_DIR)
+mark_as_advanced(CCACHE_BINARY)
+mark_as_advanced(COREAUDIO_FRAMEWORK)
+mark_as_advanced(FORCE_STATIC_VCRT)
+mark_as_advanced(HAVE_GIT)
+mark_as_advanced(LIBRARY_SUFFIX)
+mark_as_advanced(USE_STATIC_CRT)
+
+mark_as_advanced(BUILD_CXXLIBS)
+mark_as_advanced(BUILD_DOCS)
+mark_as_advanced(BUILD_REGTEST)
+mark_as_advanced(BUILD_UTILS)
+
+mark_as_advanced(ENABLE_64_BIT_WORDS)
+mark_as_advanced(ENABLE_BOW_DOCS)
+mark_as_advanced(ENABLE_EXTERNAL_LIBS)
+
+mark_as_advanced(WITH_ASM)
+mark_as_advanced(WITH_AVX)
+mark_as_advanced(WITH_FORTIFY_SOURCE)
+mark_as_advanced(WITH_OGG)
+mark_as_advanced(WITH_STACK_PROTECTOR)

+ 1 - 1
Engine/lib/Torque_postBuild.cmake

@@ -2,7 +2,7 @@
 
 # When on Windows, we need to link against winsock and windows codecs
 if (WIN32)
-	set(TORQUE_LINK_WINDOWS ${TORQUE_LINK_WINDOWS} WS2_32.LIB windowscodecs.lib)	
+	set(TORQUE_LINK_WINDOWS ${TORQUE_LINK_WINDOWS} WS2_32.LIB windowscodecs.lib winmm.lib)	
 	if (TORQUE_D3D11)
 		set(TORQUE_LINK_WINDOWS ${TORQUE_LINK_WINDOWS} dxguid.lib)
 	endif (TORQUE_D3D11)

+ 0 - 7
Engine/lib/convexDecomp/CMakeLists.txt

@@ -1,7 +0,0 @@
-file(GLOB CONVEX_DECOMP_SOURCES "*.cpp")
-add_library(convexDecomp STATIC ${CONVEX_DECOMP_SOURCES})
-target_include_directories(convexDecomp PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
-
-if (UNIX AND NOT APPLE)
-    target_compile_definitions(convexDecomp PUBLIC LINUX)
-endif (UNIX AND NOT APPLE)

+ 0 - 252
Engine/lib/convexDecomp/NvConcavityVolume.cpp

@@ -1,252 +0,0 @@
-/*
-
-NvConcavityVolume.cpp : This is a code snippet that computes the volume of concavity of a traingle mesh.
-
-*/
-/*!
-**
-** Copyright (c) 2009 by John W. Ratcliff mailto:[email protected]
-**
-** Portions of this source has been released with the PhysXViewer application, as well as
-** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
-**
-** If you find this code useful or you are feeling particularily generous I would
-** ask that you please go to http://www.amillionpixels.us and make a donation
-** to Troy DeMolay.
-**
-** DeMolay is a youth group for young men between the ages of 12 and 21.
-** It teaches strong moral principles, as well as leadership skills and
-** public speaking.  The donations page uses the 'pay for pixels' paradigm
-** where, in this case, a pixel is only a single penny.  Donations can be
-** made for as small as $4 or as high as a $100 block.  Each person who donates
-** will get a link to their own site as well as acknowledgement on the
-** donations blog located here http://www.amillionpixels.blogspot.com/
-**
-** If you wish to contact me you can use the following methods:
-**
-** Skype ID: jratcliff63367
-** Yahoo: jratcliff63367
-** AOL: jratcliff1961
-** email: [email protected]
-**
-**
-** The MIT license:
-**
-** Permission is hereby granted, free of charge, to any person obtaining a copy
-** of this software and associated documentation files (the "Software"), to deal
-** in the Software without restriction, including without limitation the rights
-** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-** copies of the Software, and to permit persons to whom the Software is furnished
-** to do so, subject to the following conditions:
-**
-** The above copyright notice and this permission notice shall be included in all
-** copies or substantial portions of the Software.
-
-** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#define SHOW_DEBUG 0
-#if SHOW_DEBUG
-#include "RenderDebug.h"
-#endif
-#include "NvConcavityVolume.h"
-#include "NvFloatMath.h"
-#include "NvRayCast.h"
-#include <stdio.h>
-
-#pragma warning(disable:4100 4189 4505 4127 4101)
-
-namespace CONVEX_DECOMPOSITION
-{
-
-bool raycast(const NxF32 *p1,const NxF32 *normal,NxF32 *dest,iRayCast *cast_hull,iRayCast *cast_mesh)
-{
-	bool ret = true;
-
-	NxF32 hit_hull[3];
-	NxF32 hit_hullNormal[3];
-
-	NxF32 hit_mesh[3];
-	NxF32 hit_meshNormal[3];
-
-	bool hitHull = cast_hull->castRay(p1,normal,hit_hull,hit_hullNormal);
-	bool hitMesh = cast_mesh->castRay(p1,normal,hit_mesh,hit_meshNormal);
-
-	if ( hitMesh )
-	{
-		float dot = fm_dot(normal,hit_meshNormal);
-		if ( dot < 0 ) // skip if we hit an internal face of the mesh when projection out towards the convex hull.
-		{
-			ret = false;
-		}
-		else
-		{
-			NxF32 d1 = fm_distanceSquared(p1,hit_mesh);
-			NxF32 d2 = fm_distanceSquared(p1,hit_hull);
-			if ( d1 < d2 )
-			{
-				dest[0] = hit_mesh[0];
-				dest[1] = hit_mesh[1];
-				dest[2] = hit_mesh[2];
-			}
-			else
-			{
-				dest[0] = hit_hull[0];
-				dest[1] = hit_hull[1];
-				dest[2] = hit_hull[2];
-			}
-		}
-	}
-	else if ( hitHull )
-	{
-		dest[0] = hit_hull[0];
-		dest[1] = hit_hull[1];
-		dest[2] = hit_hull[2];
-	}
-	else
-	{
-		ret = false;
-	}
-
-
-	return ret;
-}
-
-void addTri(NxU32 *indices,NxU32 i1,NxU32 i2,NxU32 i3,NxU32 &tcount)
-{
-	indices[tcount*3+0] = i1;
-	indices[tcount*3+1] = i2;
-	indices[tcount*3+2] = i3;
-	tcount++;
-}
-
-NxF32 computeConcavityVolume(NxU32 vcount_hull,
-						     const NxF32 *vertices_hull,
-						     NxU32 tcount_hull,
-						     const NxU32 *indices_hull,
-						     NxU32 vcount_mesh,
-						     const NxF32 *vertices_mesh,
-						     NxU32 tcount_mesh,
-						     const NxU32 *indices_mesh)
-{
-	NxF32 total_volume = 0;
-
-#if SHOW_DEBUG
-	NVSHARE::gRenderDebug->pushRenderState();
-	NVSHARE::gRenderDebug->setCurrentDisplayTime(150.0f);
-#endif
-
-	iRayCast *cast_hull = createRayCast(vertices_hull,tcount_hull,indices_hull);
-	iRayCast *cast_mesh = createRayCast(vertices_mesh,tcount_mesh,indices_mesh);
-
-
-	const NxU32 *indices = indices_mesh;
-#if 0
-	static NxU32 index = 0;
-	NxU32 i = index++;
-	indices = &indices[i*3];
-#else
-	for (NxU32 i=0; i<tcount_mesh; i++)
-#endif
-	{
-		NxU32 i1 = indices[0];
-		NxU32 i2 = indices[1];
-		NxU32 i3 = indices[2];
-
-		const NxF32 *p1 = &vertices_mesh[i1*3];
-		const NxF32 *p2 = &vertices_mesh[i2*3];
-		const NxF32 *p3 = &vertices_mesh[i3*3];
-
-		NxF32 normal[3];
-		NxF32 d = fm_computePlane(p3,p2,p1,normal);
-
-		NxF32  vertices[6*3];
-
-		vertices[0] = p1[0];
-		vertices[1] = p1[1];
-		vertices[2] = p1[2];
-
-		vertices[3] = p2[0];
-		vertices[4] = p2[1];
-		vertices[5] = p2[2];
-
-		vertices[6] = p3[0];
-		vertices[7] = p3[1];
-		vertices[8] = p3[2];
-
-		NxF32 midPoint[3];
-		midPoint[0] = (p1[0]+p2[0]+p3[0])/3;
-		midPoint[1] = (p1[1]+p2[1]+p3[1])/3;
-		midPoint[2] = (p1[2]+p2[2]+p3[2])/3;
-
-		fm_lerp(midPoint,p1,&vertices[0],0.9999f);
-		fm_lerp(midPoint,p2,&vertices[3],0.9999f);
-		fm_lerp(midPoint,p3,&vertices[6],0.9999f);
-
-		NxF32 *_p1 = &vertices[3*3];
-		NxF32 *_p2 = &vertices[4*3];
-		NxF32 *_p3 = &vertices[5*3];
-
-		NxU32 hitCount = 0;
-
-		if ( raycast(&vertices[0],normal, _p1,cast_hull,cast_mesh) ) hitCount++;
-		if ( raycast(&vertices[3],normal, _p2,cast_hull,cast_mesh) ) hitCount++;
-		if ( raycast(&vertices[6],normal, _p3,cast_hull,cast_mesh) ) hitCount++;
-
-		// form triangle mesh!
-		if ( hitCount == 3 )
-		{
-			NxU32 tcount = 0;
-			NxU32 tindices[8*3];
-
-			addTri(tindices,2,1,0,tcount);
-			addTri(tindices,3,4,5,tcount);
-
-			addTri(tindices,0,3,2,tcount);
-			addTri(tindices,2,3,5,tcount);
-
-			addTri(tindices,1,3,0,tcount);
-			addTri(tindices,4,3,1,tcount);
-
-			addTri(tindices,5,4,1,tcount);
-			addTri(tindices,2,5,1,tcount);
-
-			NxF32 volume = fm_computeMeshVolume(vertices,tcount,tindices);
-			total_volume+=volume;
-#if SHOW_DEBUG
-			NVSHARE::gRenderDebug->setCurrentColor(0x0000FF,0xFFFFFF);
-			NVSHARE::gRenderDebug->addToCurrentState(NVSHARE::DebugRenderState::SolidWireShaded);
-
-			for (NxU32 i=0; i<tcount; i++)
-			{
-				NxU32 i1 = tindices[i*3+0];
-				NxU32 i2 = tindices[i*3+1];
-				NxU32 i3 = tindices[i*3+2];
-
-				const NxF32 *p1 = &vertices[i1*3];
-				const NxF32 *p2 = &vertices[i2*3];
-				const NxF32 *p3 = &vertices[i3*3];
-
-				NVSHARE::gRenderDebug->DebugTri(p1,p2,p3);
-			}
-#endif
-		}
-		indices+=3;
-	}
-#if SHOW_DEBUG
-	NVSHARE::gRenderDebug->popRenderState();
-#endif
-
-	releaseRayCast(cast_hull);
-	releaseRayCast(cast_mesh);
-
-	return total_volume;
-}
-
-}; // end of namespace

+ 0 - 78
Engine/lib/convexDecomp/NvConcavityVolume.h

@@ -1,78 +0,0 @@
-#ifndef NV_CONCAVITY_H
-
-#define NV_CONCAVITY_H
-
-/*
-
-NvConcavityVolume.h : This is a code snippet that computes the volume of concavity of a traingle mesh.
-
-*/
-
-
-/*!
-**
-** Copyright (c) 2009 by John W. Ratcliff mailto:[email protected]
-**
-** Portions of this source has been released with the PhysXViewer application, as well as
-** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
-**
-** If you find this code useful or you are feeling particularily generous I would
-** ask that you please go to http://www.amillionpixels.us and make a donation
-** to Troy DeMolay.
-**
-** DeMolay is a youth group for young men between the ages of 12 and 21.
-** It teaches strong moral principles, as well as leadership skills and
-** public speaking.  The donations page uses the 'pay for pixels' paradigm
-** where, in this case, a pixel is only a single penny.  Donations can be
-** made for as small as $4 or as high as a $100 block.  Each person who donates
-** will get a link to their own site as well as acknowledgement on the
-** donations blog located here http://www.amillionpixels.blogspot.com/
-**
-** If you wish to contact me you can use the following methods:
-**
-** Skype ID: jratcliff63367
-** Yahoo: jratcliff63367
-** AOL: jratcliff1961
-** email: [email protected]
-**
-**
-** The MIT license:
-**
-** Permission is hereby granted, free of charge, to any person obtaining a copy
-** of this software and associated documentation files (the "Software"), to deal
-** in the Software without restriction, including without limitation the rights
-** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-** copies of the Software, and to permit persons to whom the Software is furnished
-** to do so, subject to the following conditions:
-**
-** The above copyright notice and this permission notice shall be included in all
-** copies or substantial portions of the Software.
-
-** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#include "NvUserMemAlloc.h"
-
-namespace CONVEX_DECOMPOSITION
-{
-
-// computes the 'volume of concavity' of a triangle mesh projected against its surrounding convex hull.
-
-NxF32 computeConcavityVolume(NxU32 vcount_hull,
-						     const NxF32 *vertices_hull,
-						     NxU32 tcount_hull,
-						     const NxU32 *indices_hull,
-						     NxU32 vcount_mesh,
-						     const NxF32 *vertices_mesh,
-						     NxU32 tcount_mesh,
-						     const NxU32 *indices_mesh);
-
-}; // end of namespace
-
-#endif

+ 0 - 788
Engine/lib/convexDecomp/NvConvexDecomposition.cpp

@@ -1,788 +0,0 @@
-
-/*
-
-NvConvexDecomposition.cpp : The main interface to the convex decomposition library.
-
-*/
-
-/*!
-**
-** Copyright (c) 2009 by John W. Ratcliff mailto:[email protected]
-**
-** Portions of this source has been released with the PhysXViewer application, as well as
-** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
-**
-** If you find this code useful or you are feeling particularily generous I would
-** ask that you please go to http://www.amillionpixels.us and make a donation
-** to Troy DeMolay.
-**
-** DeMolay is a youth group for young men between the ages of 12 and 21.
-** It teaches strong moral principles, as well as leadership skills and
-** public speaking.  The donations page uses the 'pay for pixels' paradigm
-** where, in this case, a pixel is only a single penny.  Donations can be
-** made for as small as $4 or as high as a $100 block.  Each person who donates
-** will get a link to their own site as well as acknowledgement on the
-** donations blog located here http://www.amillionpixels.blogspot.com/
-**
-** If you wish to contact me you can use the following methods:
-**
-** Skype ID: jratcliff63367
-** Yahoo: jratcliff63367
-** AOL: jratcliff1961
-** email: [email protected]
-**
-**
-** The MIT license:
-**
-** Permission is hereby granted, free of charge, to any person obtaining a copy
-** of this software and associated documentation files (the "Software"), to deal
-** in the Software without restriction, including without limitation the rights
-** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-** copies of the Software, and to permit persons to whom the Software is furnished
-** to do so, subject to the following conditions:
-**
-** The above copyright notice and this permission notice shall be included in all
-** copies or substantial portions of the Software.
-
-** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-#include <math.h>
-#include <float.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include "NvConvexDecomposition.h"
-#include "NvHashMap.h"
-#include "NvFloatMath.h"
-#include "NvRemoveTjunctions.h"
-#include "NvMeshIslandGeneration.h"
-#include "NvStanHull.h"
-#include "NvConcavityVolume.h"
-#include "NvSplitMesh.h"
-#include "NvThreadConfig.h"
-
-
-#pragma warning(disable:4996 4100 4189)
-
-namespace CONVEX_DECOMPOSITION
-{
-
-
-#define GRANULARITY 0.0000000001f
-
-typedef CONVEX_DECOMPOSITION::Array< NxU32 > NxU32Array;
-
-class ConvexHull : public Memalloc
-{
-public:
-	ConvexHull(NxU32 vcount,const NxF32 *vertices,NxU32 tcount,const NxU32 *indices)
-	{
-		mTested = false;
-		mVcount = vcount;
-		mTcount = tcount;
-		mVertices = 0;
-		mIndices = 0;
-		mHullVolume = 0;
-		if ( vcount )
-		{
-			mVertices = (NxF32 *)MEMALLOC_MALLOC(sizeof(NxF32)*3*vcount);
-			memcpy(mVertices,vertices,sizeof(NxF32)*3*vcount);
-		}
-		if ( tcount )
-		{
-			mIndices = (NxU32 *)MEMALLOC_MALLOC(sizeof(NxU32)*3*tcount);
-			memcpy(mIndices,indices,sizeof(NxU32)*3*tcount);
-		}
-		if ( mVcount && mTcount )
-		{
-			mHullVolume = fm_computeMeshVolume( mVertices, mTcount, mIndices);
-		}
-	}
-
-	~ConvexHull(void)
-	{
-		reset();
-	}
-
-    void reset(void)
-    {
-		MEMALLOC_FREE(mVertices);
-		MEMALLOC_FREE(mIndices);
-		mVertices = 0;
-		mIndices = 0;
-		mVcount = 0;
-		mTcount = 0;
-		mHullVolume = 0;
-	}
-
-	// return true if merging this hull with the 'mergeHull' produces a new convex hull which is no greater in volume than the
-	// mergeThresholdPercentage
-	bool canMerge(ConvexHull *mergeHull,NxF32 mergeThresholdPercent,NxU32 maxVertices,NxF32 skinWidth,NxF32 &percent)
-	{
-		bool ret = false;
-
-		if ( mHullVolume > 0 && mergeHull->mHullVolume > 0 )
-		{
-			NxU32 combineVcount = mVcount + mergeHull->mVcount;
-			NxF32 *vertices = (NxF32 *)MEMALLOC_MALLOC(sizeof(NxF32)*combineVcount*3);
-			NxF32 *dest = vertices;
-			const NxF32 *source = mVertices;
-
-			for (NxU32 i=0; i<mVcount; i++)
-			{
-				dest[0] = source[0];
-				dest[1] = source[1];
-				dest[2] = source[2];
-				dest+=3;
-				source+=3;
-			}
-			source = mergeHull->mVertices;
-			for (NxU32 i=0; i<mergeHull->mVcount; i++)
-			{
-				dest[0] = source[0];
-				dest[1] = source[1];
-				dest[2] = source[2];
-				dest+=3;
-				source+=3;
-			}
-
-			// create the combined convex hull.
-    		HullDesc hd;
-    		hd.mVcount 			= combineVcount;
-    		hd.mVertices 		= vertices;
-    		hd.mVertexStride 	= sizeof(NxF32)*3;
-    		hd.mMaxVertices 	= maxVertices;
-    		hd.mSkinWidth		= skinWidth;
-    		HullLibrary hl;
-    		HullResult result;
-    		hl.CreateConvexHull(hd,result);
-
-			NxF32 combinedVolume = fm_computeMeshVolume(result.mOutputVertices, result.mNumFaces, result.mIndices );
-			NxF32 seperateVolume = mHullVolume+mergeHull->mHullVolume;
-
-			NxF32 percentMerge = 100 - (seperateVolume*100 / combinedVolume );
-
-			if ( percentMerge <= mergeThresholdPercent )
-			{
-				percent = percentMerge;
-				ret = true;
-			}
-			MEMALLOC_FREE(vertices);
-			hl.ReleaseResult(result);
-		}
-		return ret;
-	}
-
-	void merge(ConvexHull *mergeHull,NxU32 maxVertices,NxF32 skinWidth)
-	{
-		NxU32 combineVcount = mVcount + mergeHull->mVcount;
-		NxF32 *vertices = (NxF32 *)MEMALLOC_MALLOC(sizeof(NxF32)*combineVcount*3);
-		NxF32 *dest = vertices;
-		const NxF32 *source = mVertices;
-
-		for (NxU32 i=0; i<mVcount; i++)
-		{
-			dest[0] = source[0];
-			dest[1] = source[1];
-			dest[2] = source[2];
-			dest+=3;
-			source+=3;
-		}
-		source = mergeHull->mVertices;
-		for (NxU32 i=0; i<mergeHull->mVcount; i++)
-		{
-			dest[0] = source[0];
-			dest[1] = source[1];
-			dest[2] = source[2];
-			dest+=3;
-			source+=3;
-		}
-
-		// create the combined convex hull.
-   		HullDesc hd;
-   		hd.mVcount 			= combineVcount;
-   		hd.mVertices 		= vertices;
-   		hd.mVertexStride 	= sizeof(NxF32)*3;
-   		hd.mMaxVertices 	= maxVertices;
-   		hd.mSkinWidth		= skinWidth;
-   		HullLibrary hl;
-   		HullResult result;
-   		hl.CreateConvexHull(hd,result);
-
-		reset();
-		mergeHull->reset();
-		mergeHull->mTested = true; // it's been tested.
-		mVcount = result.mNumOutputVertices;
-		mVertices = (NxF32 *)MEMALLOC_MALLOC(sizeof(NxF32)*3*mVcount);
-		memcpy(mVertices,result.mOutputVertices,sizeof(NxF32)*3*mVcount);
-		mTcount = result.mNumFaces;
-		mIndices = (NxU32 *)MEMALLOC_MALLOC(sizeof(NxU32)*mTcount*3);
-		memcpy(mIndices, result.mIndices, sizeof(NxU32)*mTcount*3);
-
-		MEMALLOC_FREE(vertices);
-		hl.ReleaseResult(result);
-	}
-
-	void setTested(bool state)
-	{
-		mTested = state;
-	}
-
-    bool beenTested(void) const { return mTested; };
-
-	bool    mTested;
-	NxF32	mHullVolume;
-	NxU32	mVcount;
-	NxF32	*mVertices;
-	NxU32	mTcount;
-	NxU32	*mIndices;
-};
-
-typedef Array< ConvexHull *> ConvexHullVector;
-
-class ConvexDecomposition : public iConvexDecomposition, public CONVEX_DECOMPOSITION::Memalloc, public ThreadInterface
-{
-public:
-	ConvexDecomposition(void)
-	{
-		mVertexIndex = 0;
-		mComplete = false;
-		mCancel = false;
-		mThread = 0;
-	}
-
-	~ConvexDecomposition(void)
-	{
-		wait();
-		reset();
-		if ( mThread )
-		{
-			tc_releaseThread(mThread);
-		}
-	}
-
-	void wait(void) const
-	{
-		while ( mThread && !mComplete );
-	}
-
-	virtual void reset(void)  // reset the input mesh data.
-	{
-		wait();
-		if ( mVertexIndex )
-		{
-			fm_releaseVertexIndex(mVertexIndex);
-			mVertexIndex = 0;
-		}
-		mIndices.clear();
-		ConvexHullVector::Iterator i;
-		for (i=mHulls.begin(); i!=mHulls.end(); ++i)
-		{
-			ConvexHull *ch = (*i);
-			delete ch;
-		}
-		mHulls.clear();
-	}
-
-	virtual bool addTriangle(const NxF32 *p1,const NxF32 *p2,const NxF32 *p3)
-	{
-		bool ret = true;
-		wait();
-		if ( mVertexIndex == 0 )
-		{
-			mVertexIndex = fm_createVertexIndex(GRANULARITY,false);
-		}
-
-		bool newPos;
-		NxU32 i1 = mVertexIndex->getIndex(p1,newPos);
-		NxU32 i2 = mVertexIndex->getIndex(p2,newPos);
-		NxU32 i3 = mVertexIndex->getIndex(p3,newPos);
-
-		if ( i1 == i2 || i1 == i3 || i2 == i3 )
-		{
-			ret = false; // triangle is degenerate
-		}
-		else
-		{
-			mIndices.pushBack(i1);
-			mIndices.pushBack(i2);
-			mIndices.pushBack(i3);
-		}
-		return ret;
-	}
-
-	ConvexHull * getNonTested(void) const
-	{
-		ConvexHull *ret = 0;
-		for (NxU32 i=0; i<mHulls.size(); i++)
-		{
-			ConvexHull *ch = mHulls[i];
-			if ( !ch->beenTested() )
-			{
-				ret = ch;
-				break;
-			}
-		}
-		return ret;
-	}
-
-	virtual NxU32 computeConvexDecomposition(NxF32 skinWidth,
-											 NxU32 decompositionDepth,
-											 NxU32 maxHullVertices,
-											 NxF32 concavityThresholdPercent,
-											 NxF32 mergeThresholdPercent,
-											 NxF32 volumeSplitThresholdPercent,
-											 bool  useInitialIslandGeneration,
-											 bool  useIslandGeneration,
-											 bool  useThreads)
-	{
-		NxU32 ret = 0;
-
-		if ( mThread )
-			return 0;
-
-		if ( mVertexIndex )
-		{
-
-			mSkinWidth = skinWidth;
-			mDecompositionDepth = decompositionDepth;
-			mMaxHullVertices = maxHullVertices;
-			mConcavityThresholdPercent = concavityThresholdPercent;
-			mMergeThresholdPercent = mergeThresholdPercent;
-			mVolumeSplitThresholdPercent = volumeSplitThresholdPercent;
-			mUseInitialIslandGeneration = useInitialIslandGeneration;
-			mUseIslandGeneration = false; // Not currently supported. useIslandGeneration;
-			mComplete = false;
-			mCancel   = false;
-
-			if ( useThreads )
-			{
-				mThread = tc_createThread(this);
-			}
-			else
-			{
-				threadMain();
-				ret = getHullCount();
-       		}
-    	}
-		return ret;
-	}
-
-	void performConvexDecomposition(NxU32 vcount,
-									 const NxF32 *vertices,
-									 NxU32 tcount,
-									 const NxU32 *indices,
-									 NxF32 skinWidth,
-									 NxU32 decompositionDepth,
-									 NxU32 maxHullVertices,
-									 NxF32 concavityThresholdPercent,
-									 NxF32 mergeThresholdPercent,
-									 NxF32 volumeSplitThresholdPercent,
-									 bool  useInitialIslandGeneration,
-									 bool  useIslandGeneration,
-									 NxU32 depth)
-	{
-		if ( mCancel ) return;
-		if ( depth >= decompositionDepth ) return;
-
-		RemoveTjunctionsDesc desc;
-		desc.mVcount 	= vcount;
-		desc.mVertices 	= vertices;
-		desc.mTcount	= tcount;
-		desc.mIndices	= indices;
-
-#if 0
-		RemoveTjunctions *rt = createRemoveTjunctions();
-		rt->removeTjunctions(desc);
-#else
-
-		desc.mTcountOut = desc.mTcount;
-		desc.mIndicesOut = desc.mIndices;
-
-#endif
-   	    // ok..we now have a clean mesh without any tjunctions.
-		bool island = (depth == 0 ) ? useInitialIslandGeneration : useIslandGeneration;
-   	    if ( island )
-   	    {
-   	    	MeshIslandGeneration *mi = createMeshIslandGeneration();
-   	    	NxU32 icount = mi->islandGenerate(desc.mTcountOut,desc.mIndicesOut,desc.mVertices);
-   	    	for (NxU32 i=0; i<icount && !mCancel; i++)
-   	    	{
-				NxU32 tcount;
-   	    		NxU32 *indices = mi->getIsland(i,tcount);
-
-   	    		baseConvexDecomposition(desc.mVcount,desc.mVertices,
-											tcount,indices,
-   	    									skinWidth,
-   	    									decompositionDepth,
-   	    									maxHullVertices,
-   											concavityThresholdPercent,
-   											mergeThresholdPercent,
-   											volumeSplitThresholdPercent,
-											useInitialIslandGeneration,
-   											useIslandGeneration,depth);
-   			}
-   			releaseMeshIslandGeneration(mi);
-   	    }
-   	    else
-   	    {
-       		baseConvexDecomposition(desc.mVcount,desc.mVertices,desc.mTcountOut,
-									desc.mIndicesOut,
-   									skinWidth,
-   									decompositionDepth,
-   									maxHullVertices,
-									concavityThresholdPercent,
-									mergeThresholdPercent,
-									volumeSplitThresholdPercent,
-									useInitialIslandGeneration,
-   									useIslandGeneration,depth);
-   	    }
-#if 0
-   	    releaseRemoveTjunctions(rt);
-#endif
-	}
-
-	virtual void baseConvexDecomposition(NxU32 vcount,
-										 const NxF32 *vertices,
-										 NxU32 tcount,
-										 const NxU32 *indices,
-										 NxF32 skinWidth,
-										 NxU32 decompositionDepth,
-										 NxU32 maxHullVertices,
-										 NxF32 concavityThresholdPercent,
-										 NxF32 mergeThresholdPercent,
-										 NxF32 volumeSplitThresholdPercent,
-										 bool  useInitialIslandGeneration,
-										 bool  useIslandGeneration,
-										 NxU32 depth)
-	{
-
-		if ( mCancel ) return;
-
-		bool split = false; // by default we do not split
-
-
-		NxU32 *out_indices 	= (NxU32 *)MEMALLOC_MALLOC( sizeof(NxU32)*tcount*3 );
-		NxF32 *out_vertices = (NxF32 *)MEMALLOC_MALLOC( sizeof(NxF32)*3*vcount );
-
-		NxU32 out_vcount = fm_copyUniqueVertices( vcount, vertices, out_vertices, tcount, indices, out_indices );
-		// get a copy of only the unique vertices which are actually being used.
-
-		HullDesc hd;
-		hd.mVcount 			= out_vcount;
-		hd.mVertices 		= out_vertices;
-		hd.mVertexStride 	= sizeof(NxF32)*3;
-		hd.mMaxVertices 	= maxHullVertices;
-		hd.mSkinWidth		= skinWidth;
-		HullLibrary hl;
-		HullResult result;
-		hl.CreateConvexHull(hd,result);
-
-		NxF32 meshVolume = fm_computeMeshVolume(result.mOutputVertices, result.mNumFaces, result.mIndices );
-
-		if ( (depth+1) < decompositionDepth )
-		{
-			// compute the volume of this mesh...
-			NxF32 percentVolume = (meshVolume*100)/mOverallMeshVolume; // what percentage of the overall mesh volume are we?
-			if ( percentVolume > volumeSplitThresholdPercent ) // this piece must be greater thant he volume split threshold percent
-			{
-				// ok..now we will compute the concavity...
-				NxF32 concave_volume = computeConcavityVolume(result.mNumOutputVertices, result.mOutputVertices, result.mNumFaces, result.mIndices, out_vcount, out_vertices,	tcount, out_indices );
-				NxF32 concave_percent = (concave_volume*100) / meshVolume;
-				if ( concave_percent >=	concavityThresholdPercent )
-				{
-					// ready to do split here..
-					split = true;
-				}
-			}
-		}
-
-		if ( !split )
-		{
-			saveConvexHull(result.mNumOutputVertices,result.mOutputVertices,result.mNumFaces,result.mIndices);
-		}
-
-		// Compute the best fit plane relative to the computed convex hull.
-		NxF32 plane[4];
-		bool ok = fm_computeSplitPlane(result.mNumOutputVertices,result.mOutputVertices,result.mNumFaces,result.mIndices,plane);
-		assert(ok);
-
-		hl.ReleaseResult(result);
-		MEMALLOC_FREE(out_indices);
-		MEMALLOC_FREE(out_vertices);
-
-		if ( split )
-		{
-			iSplitMesh *sm = createSplitMesh();
-
-			NvSplitMesh n;
-			n.mVcount 	=	vcount;
-			n.mVertices =  vertices;
-			n.mTcount	= tcount;
-			n.mIndices	= indices;
-			if ( ok )
-			{
-				NvSplitMesh leftMesh;
-				NvSplitMesh rightMesh;
-
-				sm->splitMesh(n,leftMesh,rightMesh,plane,GRANULARITY);
-
-				if ( leftMesh.mTcount )
-				{
-					performConvexDecomposition(leftMesh.mVcount,
-											   leftMesh.mVertices,
-											   leftMesh.mTcount,
-											   leftMesh.mIndices,
-											   skinWidth,
-											   decompositionDepth,
-											   maxHullVertices,
-											   concavityThresholdPercent,
-											   mergeThresholdPercent,
-											   volumeSplitThresholdPercent,
-											   useInitialIslandGeneration,
-											   useIslandGeneration,
-											   depth+1);
-
-				}
-				if ( rightMesh.mTcount )
-				{
-					performConvexDecomposition(rightMesh.mVcount,
-											   rightMesh.mVertices,
-											   rightMesh.mTcount,
-											   rightMesh.mIndices,
-											   skinWidth,
-											   decompositionDepth,
-											   maxHullVertices,
-											   concavityThresholdPercent,
-											   mergeThresholdPercent,
-											   volumeSplitThresholdPercent,
-											   useInitialIslandGeneration,
-											   useIslandGeneration,
-											   depth+1);
-				}
-			}
-			releaseSplitMesh(sm);
-		}
-	}
-
-	// Copies only the vertices which are actually used.
-	// Then computes the convex hull around these used vertices.
-	// Next computes the volume of this convex hull.
-	// Frees up scratch memory and returns the volume of the convex hull around the source triangle mesh.
-	NxF32 computeHullMeshVolume(NxU32 vcount,const NxF32 *vertices,NxU32 tcount,const NxU32 *indices,NxU32 maxVertices,NxF32 skinWidth)
-	{
-		if ( mCancel ) return 0;
-		// first thing we should do is compute the overall mesh volume.
-		NxU32 *out_indices 	= (NxU32 *)MEMALLOC_MALLOC( sizeof(NxU32)*tcount*3 );
-		NxF32 *out_vertices = (NxF32 *)MEMALLOC_MALLOC( sizeof(NxF32)*3*vcount );
-
-		NxU32 out_vcount = fm_copyUniqueVertices( vcount, vertices, out_vertices, tcount, indices, out_indices );
-		// get a copy of only the unique vertices which are actually being used.
-
-		HullDesc hd;
-		hd.mVcount 			= out_vcount;
-		hd.mVertices 		= out_vertices;
-		hd.mVertexStride 	= sizeof(NxF32)*3;
-		hd.mMaxVertices 	= maxVertices;
-		hd.mSkinWidth		= skinWidth;
-		HullLibrary hl;
-		HullResult result;
-		hl.CreateConvexHull(hd,result);
-
-		NxF32 volume = fm_computeMeshVolume(result.mOutputVertices, result.mNumFaces, result.mIndices );
-
-		hl.ReleaseResult(result);
-		MEMALLOC_FREE(out_indices);
-		MEMALLOC_FREE(out_vertices);
-
-		return volume;
-	}
-
-
-	virtual bool isComputeComplete(void)  // if building the convex hulls in a background thread, this returns true if it is complete.
-	{
-		bool ret = true;
-
-		if ( mThread )
-		{
-			ret = mComplete;
-			if ( ret )
-			{
-				tc_releaseThread(mThread);
-				mThread = 0;
-			}
-		}
-
-		return ret;
-	}
-
-
-	virtual NxU32 getHullCount(void) 
-	{
-		NxU32 hullCount = 0;
-		wait();
-		if ( mCancel )
-		{
-			reset();
-		}
-		for (NxU32 i=0; i<mHulls.size(); i++)
-		{
-			ConvexHull *ch = mHulls[i];
-			if ( ch->mTcount )
-			{
-				hullCount++;
-			}
-		}
-		return hullCount;
-	}
-
-	virtual bool  getConvexHullResult(NxU32 hullIndex,ConvexHullResult &result)
-	{
-		bool ret = false;
-
-		wait();
-		NxU32 index = 0;
-		for (NxU32 i=0; i<mHulls.size(); i++)
-		{
-			ConvexHull *ch = mHulls[i];
-			if ( ch->mTcount )
-			{
-				if ( hullIndex == index )
-				{
-					ret = true;
-					result.mVcount = ch->mVcount;
-					result.mTcount = ch->mTcount;
-					result.mVertices = ch->mVertices;
-					result.mIndices = ch->mIndices;
-					break;
-				}
-				index++;
-			}
-		}
-
-		return ret;
-	}
-
-	void saveConvexHull(NxU32 vcount,const NxF32 *vertices,NxU32 tcount,const NxU32 *indices)
-	{
-		ConvexHull *ch = MEMALLOC_NEW(ConvexHull)(vcount,vertices,tcount,indices);
-		mHulls.pushBack(ch);
-	}
-
-  	virtual void threadMain(void)
-  	{
-    	mOverallMeshVolume = computeHullMeshVolume( mVertexIndex->getVcount(),
-    												mVertexIndex->getVerticesFloat(),
-    												mIndices.size()/3,
-    												&mIndices[0],
-    												mMaxHullVertices, mSkinWidth );
-
-   		performConvexDecomposition(mVertexIndex->getVcount(),mVertexIndex->getVerticesFloat(),
-         							       	mIndices.size()/3,&mIndices[0],
-         									mSkinWidth,
-         									mDecompositionDepth,
-         									mMaxHullVertices,
-     										mConcavityThresholdPercent,
-     										mMergeThresholdPercent,
-     										mVolumeSplitThresholdPercent,
-    										mUseInitialIslandGeneration,
-     										mUseIslandGeneration,0);
-
-		if ( mHulls.size() && !mCancel )
-		{
-			// While convex hulls can be merged...
-			ConvexHull *ch = getNonTested();
-			while ( ch && !mCancel )
-			{
-				// Sort all convex hulls by volume, largest to smallest.
-				NxU32 hullCount = mHulls.size();
-				ConvexHull *bestHull = 0;
-				NxF32 bestPercent = 100;
-
-				for (NxU32 i=0; i<hullCount; i++)
-				{
-					ConvexHull *mergeHull = mHulls[i];
-					if ( !mergeHull->beenTested() && mergeHull != ch )
-					{
-						NxF32 percent;
-            			if ( ch->canMerge(mergeHull,mMergeThresholdPercent,mMaxHullVertices,mSkinWidth,percent) )
-            			{
-            				if ( percent < bestPercent )
-            				{
-            					bestHull = mergeHull;
-            					bestPercent = percent;
-            				}
-            			}
-            		}
-				}
-
-				if ( bestHull )
-				{
-            		ch->merge(bestHull,mMaxHullVertices,mSkinWidth);
-				}
-				else
-				{
-					ch->setTested(true);
-				}
-
-				ch = getNonTested();
-			}
-		}
-    	mComplete = true;
-  	}
-
-	virtual bool cancelCompute(void)  // cause background thread computation to abort early.  Will return no results. Use 'isComputeComplete' to confirm the thread is done.
-	{
-		bool ret = false;
-
-		if ( mThread && !mComplete )
-		{
-			mCancel = true;
-			ret = true;
-		}
-
-		return ret;
-	}
-
-private:
-	bool				mComplete;
-	bool				mCancel;
-	fm_VertexIndex 		*mVertexIndex;
-	NxU32Array			mIndices;
-	NxF32				mOverallMeshVolume;
-	ConvexHullVector	mHulls;
-	Thread				*mThread;
-
-	NxF32 				mSkinWidth;
-	NxU32 				mDecompositionDepth;
-	NxU32 				mMaxHullVertices;
-	NxF32 				mConcavityThresholdPercent;
-	NxF32 				mMergeThresholdPercent;
-	NxF32 				mVolumeSplitThresholdPercent;
-	bool  				mUseInitialIslandGeneration;
-	bool  				mUseIslandGeneration;
-
-};
-
-
-iConvexDecomposition * createConvexDecomposition(void)
-{
-	ConvexDecomposition *cd = MEMALLOC_NEW(ConvexDecomposition);
-	return static_cast< iConvexDecomposition *>(cd);
-
-}
-
-void				   releaseConvexDecomposition(iConvexDecomposition *ic)
-{
-	ConvexDecomposition *cd = static_cast< ConvexDecomposition *>(ic);
-	delete cd;
-}
-
-}; // end of namespace

+ 0 - 111
Engine/lib/convexDecomp/NvConvexDecomposition.h

@@ -1,111 +0,0 @@
-#ifndef CONVEX_DECOMPOSITION_H
-
-#define CONVEX_DECOMPOSITION_H
-
-/*
-
-NvConvexDecomposition.h : The main interface to the convex decomposition library.
-
-*/
-
-
-/*!
-**
-** Copyright (c) 2009 by John W. Ratcliff mailto:[email protected]
-**
-** Portions of this source has been released with the PhysXViewer application, as well as
-** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
-**
-** If you find this code useful or you are feeling particularily generous I would
-** ask that you please go to http://www.amillionpixels.us and make a donation
-** to Troy DeMolay.
-**
-** DeMolay is a youth group for young men between the ages of 12 and 21.
-** It teaches strong moral principles, as well as leadership skills and
-** public speaking.  The donations page uses the 'pay for pixels' paradigm
-** where, in this case, a pixel is only a single penny.  Donations can be
-** made for as small as $4 or as high as a $100 block.  Each person who donates
-** will get a link to their own site as well as acknowledgement on the
-** donations blog located here http://www.amillionpixels.blogspot.com/
-**
-** If you wish to contact me you can use the following methods:
-**
-** Skype ID: jratcliff63367
-** Yahoo: jratcliff63367
-** AOL: jratcliff1961
-** email: [email protected]
-**
-**
-** The MIT license:
-**
-** Permission is hereby granted, free of charge, to any person obtaining a copy
-** of this software and associated documentation files (the "Software"), to deal
-** in the Software without restriction, including without limitation the rights
-** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-** copies of the Software, and to permit persons to whom the Software is furnished
-** to do so, subject to the following conditions:
-**
-** The above copyright notice and this permission notice shall be included in all
-** copies or substantial portions of the Software.
-
-** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#include "NvSimpleTypes.h"
-
-namespace CONVEX_DECOMPOSITION
-{
-
-struct ConvexHullResult
-{
-	NxU32	mVcount;				// number of vertices.
-	NxF32	*mVertices;				// vertex positions.
-	NxU32	mTcount;				// number of triangles.
-	NxU32	*mIndices;				// indexed triangle list.
-};
-
-class iConvexDecomposition
-{
-public:
-	virtual void reset(void) = 0; // reset the input mesh data.
-
-	virtual bool addTriangle(const NxF32 *p1,const NxF32 *p2,const NxF32 *p3) = 0; // add the input mesh one triangle at a time.
-
-	virtual NxU32 computeConvexDecomposition(NxF32 skinWidth=0,			// Skin width on the convex hulls generated
-											 NxU32 decompositionDepth=8, // recursion depth for convex decomposition.
-											 NxU32 maxHullVertices=64,	// maximum number of vertices in output convex hulls.
-											 NxF32 concavityThresholdPercent=0.1f, // The percentage of concavity allowed without causing a split to occur.
-											 NxF32 mergeThresholdPercent=30.0f,    // The percentage of volume difference allowed to merge two convex hulls.
-											 NxF32 volumeSplitThresholdPercent=0.1f, // The percentage of the total volume of the object above which splits will still occur.
-											 bool  useInitialIslandGeneration=true,	// whether or not to perform initial island generation on the input mesh.
-											 bool  useIslandGeneration=false,		// Whether or not to perform island generation at each split.  Currently disabled due to bug in RemoveTjunctions
-											 bool  useBackgroundThread=true) = 0;	// Whether or not to compute the convex decomposition in a background thread, the default is true.
-
-	virtual bool isComputeComplete(void) = 0; // if building the convex hulls in a background thread, this returns true if it is complete.
-
-	virtual bool cancelCompute(void) = 0; // cause background thread computation to abort early.  Will return no results. Use 'isComputeComplete' to confirm the thread is done.
-
-
-	virtual NxU32 getHullCount(void)  = 0; // returns the number of convex hulls produced.
-	virtual bool  getConvexHullResult(NxU32 hullIndex,ConvexHullResult &result) = 0; // returns each convex hull result.
-
-protected:
-   	virtual ~iConvexDecomposition(void)
-   	{
-	}
-
-};
-
-
-iConvexDecomposition * createConvexDecomposition(void);
-void				   releaseConvexDecomposition(iConvexDecomposition *ic);
-
-}; // end of namespace
-
-#endif

+ 0 - 74
Engine/lib/convexDecomp/NvFloatMath.cpp

@@ -1,74 +0,0 @@
-// a set of routines that let you do common 3d math
-// operations without any vector, matrix, or quaternion
-// classes or templates.
-//
-// a vector (or point) is a 'NxF32 *' to 3 floating point numbers.
-// a matrix is a 'NxF32 *' to an array of 16 floating point numbers representing a 4x4 transformation matrix compatible with D3D or OGL
-// a quaternion is a 'NxF32 *' to 4 floats representing a quaternion x,y,z,w
-//
-//
-
-/*!
-**
-** Copyright (c) 2009 by John W. Ratcliff mailto:[email protected]
-**
-** Portions of this source has been released with the PhysXViewer application, as well as
-** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
-**
-** If you find this code useful or you are feeling particularily generous I would
-** ask that you please go to http://www.amillionpixels.us and make a donation
-** to Troy DeMolay.
-**
-** DeMolay is a youth group for young men between the ages of 12 and 21.
-** It teaches strong moral principles, as well as leadership skills and
-** public speaking.  The donations page uses the 'pay for pixels' paradigm
-** where, in this case, a pixel is only a single penny.  Donations can be
-** made for as small as $4 or as high as a $100 block.  Each person who donates
-** will get a link to their own site as well as acknowledgement on the
-** donations blog located here http://www.amillionpixels.blogspot.com/
-**
-** If you wish to contact me you can use the following methods:
-**
-** Skype ID: jratcliff63367
-** Yahoo: jratcliff63367
-** AOL: jratcliff1961
-** email: [email protected]
-**
-**
-** The MIT license:
-**
-** Permission is hereby granted, free of charge, to any person obtaining a copy
-** of this software and associated documentation files (the "Software"), to deal
-** in the Software without restriction, including without limitation the rights
-** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-** copies of the Software, and to permit persons to whom the Software is furnished
-** to do so, subject to the following conditions:
-**
-** The above copyright notice and this permission notice shall be included in all
-** copies or substantial portions of the Software.
-
-** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <math.h>
-#include <float.h>
-
-#include "NvFloatMath.h"
-
-#define REAL NxF32
-
-#include "NvFloatMath.inl"
-
-#undef REAL
-#define REAL NxF64
-
-#include "NvFloatMath.inl"

+ 0 - 586
Engine/lib/convexDecomp/NvFloatMath.h

@@ -1,586 +0,0 @@
-#ifndef NV_FLOAT_MATH_H
-
-#define NV_FLOAT_MATH_H
-
-#include "NvUserMemAlloc.h"
-
-// a set of routines that let you do common 3d math
-// operations without any vector, matrix, or quaternion
-// classes or templates.
-//
-// a vector (or point) is a 'NxF32 *' to 3 floating point numbers.
-// a matrix is a 'NxF32 *' to an array of 16 floating point numbers representing a 4x4 transformation matrix compatible with D3D or OGL
-// a quaternion is a 'NxF32 *' to 4 floats representing a quaternion x,y,z,w
-//
-//
-
-/*!
-**
-** Copyright (c) 2009 by John W. Ratcliff mailto:[email protected]
-**
-** Portions of this source has been released with the PhysXViewer application, as well as
-** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
-**
-** If you find this code useful or you are feeling particularily generous I would
-** ask that you please go to http://www.amillionpixels.us and make a donation
-** to Troy DeMolay.
-**
-** DeMolay is a youth group for young men between the ages of 12 and 21.
-** It teaches strong moral principles, as well as leadership skills and
-** public speaking.  The donations page uses the 'pay for pixels' paradigm
-** where, in this case, a pixel is only a single penny.  Donations can be
-** made for as small as $4 or as high as a $100 block.  Each person who donates
-** will get a link to their own site as well as acknowledgement on the
-** donations blog located here http://www.amillionpixels.blogspot.com/
-**
-** If you wish to contact me you can use the following methods:
-**
-** Skype ID: jratcliff63367
-** Yahoo: jratcliff63367
-** AOL: jratcliff1961
-** email: [email protected]
-**
-**
-** The MIT license:
-**
-** Permission is hereby granted, free of charge, to any person obtaining a copy
-** of this software and associated documentation files (the "Software"), to deal
-** in the Software without restriction, including without limitation the rights
-** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-** copies of the Software, and to permit persons to whom the Software is furnished
-** to do so, subject to the following conditions:
-**
-** The above copyright notice and this permission notice shall be included in all
-** copies or substantial portions of the Software.
-
-** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#include <float.h>
-
-namespace CONVEX_DECOMPOSITION
-{
-
-enum FM_ClipState
-{
-  FMCS_XMIN       = (1<<0),
-  FMCS_XMAX       = (1<<1),
-  FMCS_YMIN       = (1<<2),
-  FMCS_YMAX       = (1<<3),
-  FMCS_ZMIN       = (1<<4),
-  FMCS_ZMAX       = (1<<5),
-};
-
-enum FM_Axis
-{
-  FM_XAXIS   = (1<<0),
-  FM_YAXIS   = (1<<1),
-  FM_ZAXIS   = (1<<2)
-};
-
-enum LineSegmentType
-{
-  LS_START,
-  LS_MIDDLE,
-  LS_END
-};
-
-
-const NxF32 FM_PI = 3.1415926535897932384626433832795028841971693993751f;
-const NxF32 FM_DEG_TO_RAD = ((2.0f * FM_PI) / 360.0f);
-const NxF32 FM_RAD_TO_DEG = (360.0f / (2.0f * FM_PI));
-
-//***************** Float versions
-//***
-//*** vectors are assumed to be 3 floats or 3 doubles representing X, Y, Z
-//*** quaternions are assumed to be 4 floats or 4 doubles representing X,Y,Z,W
-//*** matrices are assumed to be 16 floats or 16 doubles representing a standard D3D or OpenGL style 4x4 matrix
-//*** bounding volumes are expressed as two sets of 3 floats/NxF64 representing bmin(x,y,z) and bmax(x,y,z)
-//*** Plane equations are assumed to be 4 floats or 4 doubles representing Ax,By,Cz,D
-
-FM_Axis fm_getDominantAxis(const NxF32 normal[3]);
-FM_Axis fm_getDominantAxis(const NxF64 normal[3]);
-
-void fm_decomposeTransform(const NxF32 local_transform[16],NxF32 trans[3],NxF32 rot[4],NxF32 scale[3]);
-void fm_decomposeTransform(const NxF64 local_transform[16],NxF64 trans[3],NxF64 rot[4],NxF64 scale[3]);
-
-void  fm_multiplyTransform(const NxF32 *pA,const NxF32 *pB,NxF32 *pM);
-void  fm_multiplyTransform(const NxF64 *pA,const NxF64 *pB,NxF64 *pM);
-
-void  fm_inverseTransform(const NxF32 matrix[16],NxF32 inverse_matrix[16]);
-void  fm_inverseTransform(const NxF64 matrix[16],NxF64 inverse_matrix[16]);
-
-void  fm_identity(NxF32 matrix[16]); // set 4x4 matrix to identity.
-void  fm_identity(NxF64 matrix[16]); // set 4x4 matrix to identity.
-
-void  fm_inverseRT(const NxF32 matrix[16], const NxF32 pos[3], NxF32 t[3]); // inverse rotate translate the point.
-void  fm_inverseRT(const NxF64 matrix[16],const NxF64 pos[3],NxF64 t[3]); // inverse rotate translate the point.
-
-void  fm_transform(const NxF32 matrix[16], const NxF32 pos[3], NxF32 t[3]); // rotate and translate this point.
-void  fm_transform(const NxF64 matrix[16],const NxF64 pos[3],NxF64 t[3]); // rotate and translate this point.
-
-NxF32  fm_getDeterminant(const NxF32 matrix[16]);
-NxF64 fm_getDeterminant(const NxF64 matrix[16]);
-
-void fm_getSubMatrix(NxI32 ki,NxI32 kj,NxF32 pDst[16],const NxF32 matrix[16]);
-void fm_getSubMatrix(NxI32 ki,NxI32 kj,NxF64 pDst[16],const NxF32 matrix[16]);
-
-void  fm_rotate(const NxF32 matrix[16],const NxF32 pos[3],NxF32 t[3]); // only rotate the point by a 4x4 matrix, don't translate.
-void  fm_rotate(const NxF64 matri[16],const NxF64 pos[3],NxF64 t[3]); // only rotate the point by a 4x4 matrix, don't translate.
-
-void  fm_eulerToMatrix(NxF32 ax,NxF32 ay,NxF32 az,NxF32 matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero)
-void  fm_eulerToMatrix(NxF64 ax,NxF64 ay,NxF64 az,NxF64 matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero)
-
-void  fm_getAABB(NxU32 vcount,const NxF32 *points,NxU32 pstride,NxF32 bmin[3],NxF32 bmax[3]);
-void  fm_getAABB(NxU32 vcount,const NxF64 *points,NxU32 pstride,NxF64 bmin[3],NxF64 bmax[3]);
-
-void  fm_getAABBCenter(const NxF32 bmin[3],const NxF32 bmax[3],NxF32 center[3]);
-void  fm_getAABBCenter(const NxF64 bmin[3],const NxF64 bmax[3],NxF64 center[3]);
-
-void  fm_eulerToQuat(NxF32 x,NxF32 y,NxF32 z,NxF32 quat[4]); // convert euler angles to quaternion.
-void  fm_eulerToQuat(NxF64 x,NxF64 y,NxF64 z,NxF64 quat[4]); // convert euler angles to quaternion.
-
-void  fm_quatToEuler(const NxF32 quat[4],NxF32 &ax,NxF32 &ay,NxF32 &az);
-void  fm_quatToEuler(const NxF64 quat[4],NxF64 &ax,NxF64 &ay,NxF64 &az);
-
-void  fm_eulerToQuat(const NxF32 euler[3],NxF32 quat[4]); // convert euler angles to quaternion. Angles must be radians not degrees!
-void  fm_eulerToQuat(const NxF64 euler[3],NxF64 quat[4]); // convert euler angles to quaternion.
-
-void  fm_scale(NxF32 x,NxF32 y,NxF32 z,NxF32 matrix[16]); // apply scale to the matrix.
-void  fm_scale(NxF64 x,NxF64 y,NxF64 z,NxF64 matrix[16]); // apply scale to the matrix.
-
-void  fm_eulerToQuatDX(NxF32 x,NxF32 y,NxF32 z,NxF32 quat[4]); // convert euler angles to quaternion using the fucked up DirectX method
-void  fm_eulerToQuatDX(NxF64 x,NxF64 y,NxF64 z,NxF64 quat[4]); // convert euler angles to quaternion using the fucked up DirectX method
-
-void  fm_eulerToMatrixDX(NxF32 x,NxF32 y,NxF32 z,NxF32 matrix[16]); // convert euler angles to quaternion using the fucked up DirectX method.
-void  fm_eulerToMatrixDX(NxF64 x,NxF64 y,NxF64 z,NxF64 matrix[16]); // convert euler angles to quaternion using the fucked up DirectX method.
-
-void  fm_quatToMatrix(const NxF32 quat[4],NxF32 matrix[16]); // convert quaterinion rotation to matrix, translation set to zero.
-void  fm_quatToMatrix(const NxF64 quat[4],NxF64 matrix[16]); // convert quaterinion rotation to matrix, translation set to zero.
-
-void  fm_quatRotate(const NxF32 quat[4],const NxF32 v[3],NxF32 r[3]); // rotate a vector directly by a quaternion.
-void  fm_quatRotate(const NxF64 quat[4],const NxF64 v[3],NxF64 r[3]); // rotate a vector directly by a quaternion.
-
-void  fm_getTranslation(const NxF32 matrix[16],NxF32 t[3]);
-void  fm_getTranslation(const NxF64 matrix[16],NxF64 t[3]);
-
-void  fm_setTranslation(const NxF32 *translation,NxF32 matrix[16]);
-void  fm_setTranslation(const NxF64 *translation,NxF64 matrix[16]);
-
-void  fm_multiplyQuat(const NxF32 *qa,const NxF32 *qb,NxF32 *quat);
-void  fm_multiplyQuat(const NxF64 *qa,const NxF64 *qb,NxF64 *quat);
-
-void  fm_matrixToQuat(const NxF32 matrix[16],NxF32 quat[4]); // convert the 3x3 portion of a 4x4 matrix into a quaterion as x,y,z,w
-void  fm_matrixToQuat(const NxF64 matrix[16],NxF64 quat[4]); // convert the 3x3 portion of a 4x4 matrix into a quaterion as x,y,z,w
-
-NxF32 fm_sphereVolume(NxF32 radius); // return's the volume of a sphere of this radius (4/3 PI * R cubed )
-NxF64 fm_sphereVolume(NxF64 radius); // return's the volume of a sphere of this radius (4/3 PI * R cubed )
-
-NxF32 fm_cylinderVolume(NxF32 radius,NxF32 h);
-NxF64 fm_cylinderVolume(NxF64 radius,NxF64 h);
-
-NxF32 fm_capsuleVolume(NxF32 radius,NxF32 h);
-NxF64 fm_capsuleVolume(NxF64 radius,NxF64 h);
-
-NxF32 fm_distance(const NxF32 p1[3],const NxF32 p2[3]);
-NxF64 fm_distance(const NxF64 p1[3],const NxF64 p2[3]);
-
-NxF32 fm_distanceSquared(const NxF32 p1[3],const NxF32 p2[3]);
-NxF64 fm_distanceSquared(const NxF64 p1[3],const NxF64 p2[3]);
-
-NxF32 fm_distanceSquaredXZ(const NxF32 p1[3],const NxF32 p2[3]);
-NxF64 fm_distanceSquaredXZ(const NxF64 p1[3],const NxF64 p2[3]);
-
-NxF32 fm_computePlane(const NxF32 p1[3],const NxF32 p2[3],const NxF32 p3[3],NxF32 *n); // return D
-NxF64 fm_computePlane(const NxF64 p1[3],const NxF64 p2[3],const NxF64 p3[3],NxF64 *n); // return D
-
-NxF32 fm_distToPlane(const NxF32 plane[4],const NxF32 pos[3]); // computes the distance of this point from the plane.
-NxF64 fm_distToPlane(const NxF64 plane[4],const NxF64 pos[3]); // computes the distance of this point from the plane.
-
-NxF32 fm_dot(const NxF32 p1[3],const NxF32 p2[3]);
-NxF64 fm_dot(const NxF64 p1[3],const NxF64 p2[3]);
-
-void  fm_cross(NxF32 cross[3],const NxF32 a[3],const NxF32 b[3]);
-void  fm_cross(NxF64 cross[3],const NxF64 a[3],const NxF64 b[3]);
-
-void  fm_computeNormalVector(NxF32 n[3],const NxF32 p1[3],const NxF32 p2[3]); // as P2-P1 normalized.
-void  fm_computeNormalVector(NxF64 n[3],const NxF64 p1[3],const NxF64 p2[3]); // as P2-P1 normalized.
-
-bool  fm_computeWindingOrder(const NxF32 p1[3],const NxF32 p2[3],const NxF32 p3[3]); // returns true if the triangle is clockwise.
-bool  fm_computeWindingOrder(const NxF64 p1[3],const NxF64 p2[3],const NxF64 p3[3]); // returns true if the triangle is clockwise.
-
-NxF32  fm_normalize(NxF32 n[3]); // normalize this vector and return the distance
-NxF64  fm_normalize(NxF64 n[3]); // normalize this vector and return the distance
-
-void  fm_matrixMultiply(const NxF32 A[16],const NxF32 B[16],NxF32 dest[16]);
-void  fm_matrixMultiply(const NxF64 A[16],const NxF64 B[16],NxF64 dest[16]);
-
-void  fm_composeTransform(const NxF32 position[3],const NxF32 quat[4],const NxF32 scale[3],NxF32 matrix[16]);
-void  fm_composeTransform(const NxF64 position[3],const NxF64 quat[4],const NxF64 scale[3],NxF64 matrix[16]);
-
-NxF32 fm_computeArea(const NxF32 p1[3],const NxF32 p2[3],const NxF32 p3[3]);
-NxF64 fm_computeArea(const NxF64 p1[3],const NxF64 p2[3],const NxF64 p3[3]);
-
-void  fm_lerp(const NxF32 p1[3],const NxF32 p2[3],NxF32 dest[3],NxF32 lerpValue);
-void  fm_lerp(const NxF64 p1[3],const NxF64 p2[3],NxF64 dest[3],NxF64 lerpValue);
-
-bool  fm_insideTriangleXZ(const NxF32 test[3],const NxF32 p1[3],const NxF32 p2[3],const NxF32 p3[3]);
-bool  fm_insideTriangleXZ(const NxF64 test[3],const NxF64 p1[3],const NxF64 p2[3],const NxF64 p3[3]);
-
-bool  fm_insideAABB(const NxF32 pos[3],const NxF32 bmin[3],const NxF32 bmax[3]);
-bool  fm_insideAABB(const NxF64 pos[3],const NxF64 bmin[3],const NxF64 bmax[3]);
-
-bool  fm_insideAABB(const NxF32 obmin[3],const NxF32 obmax[3],const NxF32 tbmin[3],const NxF32 tbmax[3]); // test if bounding box tbmin/tmbax is fully inside obmin/obmax
-bool  fm_insideAABB(const NxF64 obmin[3],const NxF64 obmax[3],const NxF64 tbmin[3],const NxF64 tbmax[3]); // test if bounding box tbmin/tmbax is fully inside obmin/obmax
-
-NxU32 fm_clipTestPoint(const NxF32 bmin[3],const NxF32 bmax[3],const NxF32 pos[3]);
-NxU32 fm_clipTestPoint(const NxF64 bmin[3],const NxF64 bmax[3],const NxF64 pos[3]);
-
-NxU32 fm_clipTestPointXZ(const NxF32 bmin[3],const NxF32 bmax[3],const NxF32 pos[3]); // only tests X and Z, not Y
-NxU32 fm_clipTestPointXZ(const NxF64 bmin[3],const NxF64 bmax[3],const NxF64 pos[3]); // only tests X and Z, not Y
-
-
-NxU32 fm_clipTestAABB(const NxF32 bmin[3],const NxF32 bmax[3],const NxF32 p1[3],const NxF32 p2[3],const NxF32 p3[3],NxU32 &andCode);
-NxU32 fm_clipTestAABB(const NxF64 bmin[3],const NxF64 bmax[3],const NxF64 p1[3],const NxF64 p2[3],const NxF64 p3[3],NxU32 &andCode);
-
-
-bool     fm_lineTestAABBXZ(const NxF32 p1[3],const NxF32 p2[3],const NxF32 bmin[3],const NxF32 bmax[3],NxF32 &time);
-bool     fm_lineTestAABBXZ(const NxF64 p1[3],const NxF64 p2[3],const NxF64 bmin[3],const NxF64 bmax[3],NxF64 &time);
-
-bool     fm_lineTestAABB(const NxF32 p1[3],const NxF32 p2[3],const NxF32 bmin[3],const NxF32 bmax[3],NxF32 &time);
-bool     fm_lineTestAABB(const NxF64 p1[3],const NxF64 p2[3],const NxF64 bmin[3],const NxF64 bmax[3],NxF64 &time);
-
-
-void  fm_initMinMax(const NxF32 p[3],NxF32 bmin[3],NxF32 bmax[3]);
-void  fm_initMinMax(const NxF64 p[3],NxF64 bmin[3],NxF64 bmax[3]);
-
-void  fm_initMinMax(NxF32 bmin[3],NxF32 bmax[3]);
-void  fm_initMinMax(NxF64 bmin[3],NxF64 bmax[3]);
-
-void  fm_minmax(const NxF32 p[3],NxF32 bmin[3],NxF32 bmax[3]); // accmulate to a min-max value
-void  fm_minmax(const NxF64 p[3],NxF64 bmin[3],NxF64 bmax[3]); // accmulate to a min-max value
-
-
-NxF32 fm_solveX(const NxF32 plane[4],NxF32 y,NxF32 z); // solve for X given this plane equation and the other two components.
-NxF64 fm_solveX(const NxF64 plane[4],NxF64 y,NxF64 z); // solve for X given this plane equation and the other two components.
-
-NxF32 fm_solveY(const NxF32 plane[4],NxF32 x,NxF32 z); // solve for Y given this plane equation and the other two components.
-NxF64 fm_solveY(const NxF64 plane[4],NxF64 x,NxF64 z); // solve for Y given this plane equation and the other two components.
-
-NxF32 fm_solveZ(const NxF32 plane[4],NxF32 x,NxF32 y); // solve for Z given this plane equation and the other two components.
-NxF64 fm_solveZ(const NxF64 plane[4],NxF64 x,NxF64 y); // solve for Z given this plane equation and the other two components.
-
-bool  fm_computeBestFitPlane(NxU32 vcount,     // number of input data points
-                     const NxF32 *points,     // starting address of points array.
-                     NxU32 vstride,    // stride between input points.
-                     const NxF32 *weights,    // *optional point weighting values.
-                     NxU32 wstride,    // weight stride for each vertex.
-                     NxF32 plane[4]);
-
-bool  fm_computeBestFitPlane(NxU32 vcount,     // number of input data points
-                     const NxF64 *points,     // starting address of points array.
-                     NxU32 vstride,    // stride between input points.
-                     const NxF64 *weights,    // *optional point weighting values.
-                     NxU32 wstride,    // weight stride for each vertex.
-                     NxF64 plane[4]);
-
-
-NxF32  fm_computeBestFitAABB(NxU32 vcount,const NxF32 *points,NxU32 pstride,NxF32 bmin[3],NxF32 bmax[3]); // returns the diagonal distance
-NxF64 fm_computeBestFitAABB(NxU32 vcount,const NxF64 *points,NxU32 pstride,NxF64 bmin[3],NxF64 bmax[3]); // returns the diagonal distance
-
-NxF32  fm_computeBestFitSphere(NxU32 vcount,const NxF32 *points,NxU32 pstride,NxF32 center[3]);
-NxF64  fm_computeBestFitSphere(NxU32 vcount,const NxF64 *points,NxU32 pstride,NxF64 center[3]);
-
-bool fm_lineSphereIntersect(const NxF32 center[3],NxF32 radius,const NxF32 p1[3],const NxF32 p2[3],NxF32 intersect[3]);
-bool fm_lineSphereIntersect(const NxF64 center[3],NxF64 radius,const NxF64 p1[3],const NxF64 p2[3],NxF64 intersect[3]);
-
-bool fm_intersectRayAABB(const NxF32 bmin[3],const NxF32 bmax[3],const NxF32 pos[3],const NxF32 dir[3],NxF32 intersect[3]);
-bool fm_intersectLineSegmentAABB(const NxF32 bmin[3],const NxF32 bmax[3],const NxF32 p1[3],const NxF32 p2[3],NxF32 intersect[3]);
-
-bool fm_lineIntersectsTriangle(const NxF32 rayStart[3],const NxF32 rayEnd[3],const NxF32 p1[3],const NxF32 p2[3],const NxF32 p3[3],NxF32 sect[3]);
-bool fm_lineIntersectsTriangle(const NxF64 rayStart[3],const NxF64 rayEnd[3],const NxF64 p1[3],const NxF64 p2[3],const NxF64 p3[3],NxF64 sect[3]);
-
-bool fm_rayIntersectsTriangle(const NxF32 origin[3],const NxF32 dir[3],const NxF32 v0[3],const NxF32 v1[3],const NxF32 v2[3],NxF32 &t);
-bool fm_rayIntersectsTriangle(const NxF64 origin[3],const NxF64 dir[3],const NxF64 v0[3],const NxF64 v1[3],const NxF64 v2[3],NxF64 &t);
-
-bool fm_raySphereIntersect(const NxF32 center[3],NxF32 radius,const NxF32 pos[3],const NxF32 dir[3],NxF32 distance,NxF32 intersect[3]);
-bool fm_raySphereIntersect(const NxF64 center[3],NxF64 radius,const NxF64 pos[3],const NxF64 dir[3],NxF64 distance,NxF64 intersect[3]);
-
-void fm_catmullRom(NxF32 out_vector[3],const NxF32 p1[3],const NxF32 p2[3],const NxF32 p3[3],const NxF32 *p4, const NxF32 s);
-void fm_catmullRom(NxF64 out_vector[3],const NxF64 p1[3],const NxF64 p2[3],const NxF64 p3[3],const NxF64 *p4, const NxF64 s);
-
-bool fm_intersectAABB(const NxF32 bmin1[3],const NxF32 bmax1[3],const NxF32 bmin2[3],const NxF32 bmax2[3]);
-bool fm_intersectAABB(const NxF64 bmin1[3],const NxF64 bmax1[3],const NxF64 bmin2[3],const NxF64 bmax2[3]);
-
-
-// computes the rotation quaternion to go from unit-vector v0 to unit-vector v1
-void fm_rotationArc(const NxF32 v0[3],const NxF32 v1[3],NxF32 quat[4]);
-void fm_rotationArc(const NxF64 v0[3],const NxF64 v1[3],NxF64 quat[4]);
-
-NxF32  fm_distancePointLineSegment(const NxF32 Point[3],const NxF32 LineStart[3],const NxF32 LineEnd[3],NxF32 intersection[3],LineSegmentType &type,NxF32 epsilon);
-NxF64 fm_distancePointLineSegment(const NxF64 Point[3],const NxF64 LineStart[3],const NxF64 LineEnd[3],NxF64 intersection[3],LineSegmentType &type,NxF64 epsilon);
-
-
-bool fm_colinear(const NxF64 p1[3],const NxF64 p2[3],const NxF64 p3[3],NxF64 epsilon=0.999);               // true if these three points in a row are co-linear
-bool fm_colinear(const NxF32  p1[3],const NxF32  p2[3],const NxF32 p3[3],NxF32 epsilon=0.999f);
-
-bool fm_colinear(const NxF32 a1[3],const NxF32 a2[3],const NxF32 b1[3],const NxF32 b2[3],NxF32 epsilon=0.999f);  // true if these two line segments are co-linear.
-bool fm_colinear(const NxF64 a1[3],const NxF64 a2[3],const NxF64 b1[3],const NxF64 b2[3],NxF64 epsilon=0.999);  // true if these two line segments are co-linear.
-
-enum IntersectResult
-{
-  IR_DONT_INTERSECT,
-  IR_DO_INTERSECT,
-  IR_COINCIDENT,
-  IR_PARALLEL,
-};
-
-IntersectResult fm_intersectLineSegments2d(const NxF32 a1[3], const NxF32 a2[3], const NxF32 b1[3], const NxF32 b2[3], NxF32 intersectionPoint[3]);
-IntersectResult fm_intersectLineSegments2d(const NxF64 a1[3],const NxF64 a2[3],const NxF64 b1[3],const NxF64 b2[3],NxF64 intersectionPoint[3]);
-
-IntersectResult fm_intersectLineSegments2dTime(const NxF32 a1[3], const NxF32 a2[3], const NxF32 b1[3], const NxF32 b2[3],NxF32 &t1,NxF32 &t2);
-IntersectResult fm_intersectLineSegments2dTime(const NxF64 a1[3],const NxF64 a2[3],const NxF64 b1[3],const NxF64 b2[3],NxF64 &t1,NxF64 &t2);
-
-// Plane-Triangle splitting
-
-enum PlaneTriResult
-{
-  PTR_ON_PLANE,
-  PTR_FRONT,
-  PTR_BACK,
-  PTR_SPLIT,
-};
-
-PlaneTriResult fm_planeTriIntersection(const NxF32 plane[4],    // the plane equation in Ax+By+Cz+D format
-                                    const NxF32 *triangle, // the source triangle.
-                                    NxU32 tstride,  // stride in bytes of the input and output *vertices*
-                                    NxF32        epsilon,  // the co-planer epsilon value.
-                                    NxF32       *front,    // the triangle in front of the
-                                    NxU32 &fcount,  // number of vertices in the 'front' triangle
-                                    NxF32       *back,     // the triangle in back of the plane
-                                    NxU32 &bcount); // the number of vertices in the 'back' triangle.
-
-
-PlaneTriResult fm_planeTriIntersection(const NxF64 plane[4],    // the plane equation in Ax+By+Cz+D format
-                                    const NxF64 *triangle, // the source triangle.
-                                    NxU32 tstride,  // stride in bytes of the input and output *vertices*
-                                    NxF64        epsilon,  // the co-planer epsilon value.
-                                    NxF64       *front,    // the triangle in front of the
-                                    NxU32 &fcount,  // number of vertices in the 'front' triangle
-                                    NxF64       *back,     // the triangle in back of the plane
-                                    NxU32 &bcount); // the number of vertices in the 'back' triangle.
-
-
-void fm_intersectPointPlane(const NxF32 p1[3],const NxF32 p2[3],NxF32 *split,const NxF32 plane[4]);
-void fm_intersectPointPlane(const NxF64 p1[3],const NxF64 p2[3],NxF64 *split,const NxF64 plane[4]);
-
-PlaneTriResult fm_getSidePlane(const NxF32 p[3],const NxF32 plane[4],NxF32 epsilon);
-PlaneTriResult fm_getSidePlane(const NxF64 p[3],const NxF64 plane[4],NxF64 epsilon);
-
-
-void fm_computeBestFitOBB(NxU32 vcount,const NxF32 *points,NxU32 pstride,NxF32 *sides,NxF32 matrix[16],bool bruteForce=true);
-void fm_computeBestFitOBB(NxU32 vcount,const NxF64 *points,NxU32 pstride,NxF64 *sides,NxF64 matrix[16],bool bruteForce=true);
-
-void fm_computeBestFitOBB(NxU32 vcount,const NxF32 *points,NxU32 pstride,NxF32 *sides,NxF32 pos[3],NxF32 quat[4],bool bruteForce=true);
-void fm_computeBestFitOBB(NxU32 vcount,const NxF64 *points,NxU32 pstride,NxF64 *sides,NxF64 pos[3],NxF64 quat[4],bool bruteForce=true);
-
-void fm_computeBestFitABB(NxU32 vcount,const NxF32 *points,NxU32 pstride,NxF32 *sides,NxF32 pos[3]);
-void fm_computeBestFitABB(NxU32 vcount,const NxF64 *points,NxU32 pstride,NxF64 *sides,NxF64 pos[3]);
-
-
-//** Note, if the returned capsule height is less than zero, then you must represent it is a sphere of size radius.
-void fm_computeBestFitCapsule(NxU32 vcount,const NxF32 *points,NxU32 pstride,NxF32 &radius,NxF32 &height,NxF32 matrix[16],bool bruteForce=true);
-void fm_computeBestFitCapsule(NxU32 vcount,const NxF64 *points,NxU32 pstride,NxF32 &radius,NxF32 &height,NxF64 matrix[16],bool bruteForce=true);
-
-
-void fm_planeToMatrix(const NxF32 plane[4],NxF32 matrix[16]); // convert a plane equation to a 4x4 rotation matrix.  Reference vector is 0,1,0
-void fm_planeToQuat(const NxF32 plane[4],NxF32 quat[4],NxF32 pos[3]); // convert a plane equation to a quaternion and translation
-
-void fm_planeToMatrix(const NxF64 plane[4],NxF64 matrix[16]); // convert a plane equation to a 4x4 rotation matrix
-void fm_planeToQuat(const NxF64 plane[4],NxF64 quat[4],NxF64 pos[3]); // convert a plane equation to a quaternion and translation
-
-inline void fm_doubleToFloat3(const NxF64 p[3],NxF32 t[3]) { t[0] = (NxF32) p[0]; t[1] = (NxF32)p[1]; t[2] = (NxF32)p[2]; };
-inline void fm_floatToDouble3(const NxF32 p[3],NxF64 t[3]) { t[0] = (NxF64)p[0]; t[1] = (NxF64)p[1]; t[2] = (NxF64)p[2]; };
-
-
-void  fm_eulerMatrix(NxF32 ax,NxF32 ay,NxF32 az,NxF32 matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero)
-void  fm_eulerMatrix(NxF64 ax,NxF64 ay,NxF64 az,NxF64 matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero)
-
-
-NxF32  fm_computeMeshVolume(const NxF32 *vertices,NxU32 tcount,const NxU32 *indices);
-NxF64 fm_computeMeshVolume(const NxF64 *vertices,NxU32 tcount,const NxU32 *indices);
-
-
-#define FM_DEFAULT_GRANULARITY 0.001f  // 1 millimeter is the default granularity
-
-class fm_VertexIndex
-{
-public:
-  virtual NxU32          getIndex(const NxF32 pos[3],bool &newPos) = 0;  // get welded index for this NxF32 vector[3]
-  virtual NxU32          getIndex(const NxF64 pos[3],bool &newPos) = 0;  // get welded index for this NxF64 vector[3]
-  virtual const NxF32 *   getVerticesFloat(void) const = 0;
-  virtual const NxF64 *  getVerticesDouble(void) const = 0;
-  virtual const NxF32 *   getVertexFloat(NxU32 index) const = 0;
-  virtual const NxF64 *  getVertexDouble(NxU32 index) const = 0;
-  virtual NxU32          getVcount(void) const = 0;
-  virtual bool            isDouble(void) const = 0;
-  virtual bool            saveAsObj(const char *fname,NxU32 tcount,NxU32 *indices) = 0;
-};
-
-fm_VertexIndex * fm_createVertexIndex(NxF64 granularity,bool snapToGrid); // create an indexed vertex system for doubles
-fm_VertexIndex * fm_createVertexIndex(NxF32 granularity,bool snapToGrid);  // create an indexed vertext system for floats
-void             fm_releaseVertexIndex(fm_VertexIndex *vindex);
-
-
-
-#if 0 // currently disabled
-
-class fm_LineSegment
-{
-public:
-  fm_LineSegment(void)
-  {
-    mE1 = mE2 = 0;
-  }
-
-  fm_LineSegment(NxU32 e1,NxU32 e2)
-  {
-    mE1 = e1;
-    mE2 = e2;
-  }
-
-  NxU32 mE1;
-  NxU32 mE2;
-};
-
-
-// LineSweep *only* supports doublees.  As a geometric operation it needs as much precision as possible.
-class fm_LineSweep
-{
-public:
-
- virtual fm_LineSegment * performLineSweep(const fm_LineSegment *segments,
-                                   NxU32 icount,
-                                   const NxF64 *planeEquation,
-                                   fm_VertexIndex *pool,
-                                   NxU32 &scount) = 0;
-
-
-};
-
-fm_LineSweep * fm_createLineSweep(void);
-void           fm_releaseLineSweep(fm_LineSweep *sweep);
-
-#endif
-
-class fm_Triangulate
-{
-public:
-  virtual const NxF64 *       triangulate3d(NxU32 pcount,
-                                             const NxF64 *points,
-                                             NxU32 vstride,
-                                             NxU32 &tcount,
-                                             bool consolidate,
-                                             NxF64 epsilon) = 0;
-
-  virtual const NxF32  *       triangulate3d(NxU32 pcount,
-                                             const NxF32  *points,
-                                             NxU32 vstride,
-                                             NxU32 &tcount,
-                                             bool consolidate,
-                                             NxF32 epsilon) = 0;
-};
-
-fm_Triangulate * fm_createTriangulate(void);
-void             fm_releaseTriangulate(fm_Triangulate *t);
-
-
-const NxF32 * fm_getPoint(const NxF32 *points,NxU32 pstride,NxU32 index);
-const NxF64 * fm_getPoint(const NxF64 *points,NxU32 pstride,NxU32 index);
-
-bool   fm_insideTriangle(NxF32 Ax, NxF32 Ay,NxF32 Bx, NxF32 By,NxF32 Cx, NxF32 Cy,NxF32 Px, NxF32 Py);
-bool   fm_insideTriangle(NxF64 Ax, NxF64 Ay,NxF64 Bx, NxF64 By,NxF64 Cx, NxF64 Cy,NxF64 Px, NxF64 Py);
-NxF32  fm_areaPolygon2d(NxU32 pcount,const NxF32 *points,NxU32 pstride);
-NxF64 fm_areaPolygon2d(NxU32 pcount,const NxF64 *points,NxU32 pstride);
-
-bool  fm_pointInsidePolygon2d(NxU32 pcount,const NxF32 *points,NxU32 pstride,const NxF32 *point,NxU32 xindex=0,NxU32 yindex=1);
-bool  fm_pointInsidePolygon2d(NxU32 pcount,const NxF64 *points,NxU32 pstride,const NxF64 *point,NxU32 xindex=0,NxU32 yindex=1);
-
-NxU32 fm_consolidatePolygon(NxU32 pcount,const NxF32 *points,NxU32 pstride,NxF32 *dest,NxF32 epsilon=0.999999f); // collapses co-linear edges.
-NxU32 fm_consolidatePolygon(NxU32 pcount,const NxF64 *points,NxU32 pstride,NxF64 *dest,NxF64 epsilon=0.999999); // collapses co-linear edges.
-
-
-bool fm_computeSplitPlane(NxU32 vcount,const NxF64 *vertices,NxU32 tcount,const NxU32 *indices,NxF64 *plane);
-bool fm_computeSplitPlane(NxU32 vcount,const NxF32 *vertices,NxU32 tcount,const NxU32 *indices,NxF32 *plane);
-
-void fm_nearestPointInTriangle(const NxF32 *pos,const NxF32 *p1,const NxF32 *p2,const NxF32 *p3,NxF32 *nearest);
-void fm_nearestPointInTriangle(const NxF64 *pos,const NxF64 *p1,const NxF64 *p2,const NxF64 *p3,NxF64 *nearest);
-
-NxF32  fm_areaTriangle(const NxF32 *p1,const NxF32 *p2,const NxF32 *p3);
-NxF64 fm_areaTriangle(const NxF64 *p1,const NxF64 *p2,const NxF64 *p3);
-
-void fm_subtract(const NxF32 *A,const NxF32 *B,NxF32 *diff); // compute A-B and store the result in 'diff'
-void fm_subtract(const NxF64 *A,const NxF64 *B,NxF64 *diff); // compute A-B and store the result in 'diff'
-
-void fm_multiply(NxF32 *A,NxF32 scaler);
-void fm_multiply(NxF64 *A,NxF64 scaler);
-
-void fm_add(const NxF32 *A,const NxF32 *B,NxF32 *sum);
-void fm_add(const NxF64 *A,const NxF64 *B,NxF64 *sum);
-
-void fm_copy3(const NxF32 *source,NxF32 *dest);
-void fm_copy3(const NxF64 *source,NxF64 *dest);
-
-// re-indexes an indexed triangle mesh but drops unused vertices.  The output_indices can be the same pointer as the input indices.
-// the output_vertices can point to the input vertices if you desire.  The output_vertices buffer should be at least the same size
-// is the input buffer.  The routine returns the new vertex count after re-indexing.
-NxU32  fm_copyUniqueVertices(NxU32 vcount,const NxF32 *input_vertices,NxF32 *output_vertices,NxU32 tcount,const NxU32 *input_indices,NxU32 *output_indices);
-NxU32  fm_copyUniqueVertices(NxU32 vcount,const NxF64 *input_vertices,NxF64 *output_vertices,NxU32 tcount,const NxU32 *input_indices,NxU32 *output_indices);
-
-bool    fm_isMeshCoplanar(NxU32 tcount,const NxU32 *indices,const NxF32 *vertices,bool doubleSided); // returns true if this collection of indexed triangles are co-planar!
-bool    fm_isMeshCoplanar(NxU32 tcount,const NxU32 *indices,const NxF64 *vertices,bool doubleSided); // returns true if this collection of indexed triangles are co-planar!
-
-bool    fm_samePlane(const NxF32 p1[4],const NxF32 p2[4],NxF32 normalEpsilon=0.01f,NxF32 dEpsilon=0.001f,bool doubleSided=false); // returns true if these two plane equations are identical within an epsilon
-bool    fm_samePlane(const NxF64 p1[4],const NxF64 p2[4],NxF64 normalEpsilon=0.01,NxF64 dEpsilon=0.001,bool doubleSided=false);
-
-void    fm_OBBtoAABB(const NxF32 obmin[3],const NxF32 obmax[3],const NxF32 matrix[16],NxF32 abmin[3],NxF32 abmax[3]);
-
-// a utility class that will tesseleate a mesh.
-class fm_Tesselate
-{
-public:
-  virtual const NxU32 * tesselate(fm_VertexIndex *vindex,NxU32 tcount,const NxU32 *indices,NxF32 longEdge,NxU32 maxDepth,NxU32 &outcount) = 0;
-};
-
-fm_Tesselate * fm_createTesselate(void);
-void           fm_releaseTesselate(fm_Tesselate *t);
-
-void fm_computeMeanNormals(NxU32 vcount,       // the number of vertices
-                           const NxF32 *vertices,     // the base address of the vertex position data.
-                           NxU32 vstride,      // the stride between position data.
-                           NxF32 *normals,            // the base address  of the destination for mean vector normals
-                           NxU32 nstride,      // the stride between normals
-                           NxU32 tcount,       // the number of triangles
-                           const NxU32 *indices);     // the triangle indices
-
-void fm_computeMeanNormals(NxU32 vcount,       // the number of vertices
-                           const NxF64 *vertices,     // the base address of the vertex position data.
-                           NxU32 vstride,      // the stride between position data.
-                           NxF64 *normals,            // the base address  of the destination for mean vector normals
-                           NxU32 nstride,      // the stride between normals
-                           NxU32 tcount,       // the number of triangles
-                           const NxU32 *indices);     // the triangle indices
-
-
-bool fm_isValidTriangle(const NxF32 *p1,const NxF32 *p2,const NxF32 *p3,NxF32 epsilon=0.00001f);
-bool fm_isValidTriangle(const NxF64 *p1,const NxF64 *p2,const NxF64 *p3,NxF64 epsilon=0.00001f);
-
-}; // end of namespace
-
-#endif

+ 0 - 5607
Engine/lib/convexDecomp/NvFloatMath.inl

@@ -1,5607 +0,0 @@
-// a set of routines that let you do common 3d math
-// operations without any vector, matrix, or quaternion
-// classes or templates.
-//
-// a vector (or point) is a 'NxF32 *' to 3 floating point numbers.
-// a matrix is a 'NxF32 *' to an array of 16 floating point numbers representing a 4x4 transformation matrix compatible with D3D or OGL
-// a quaternion is a 'NxF32 *' to 4 floats representing a quaternion x,y,z,w
-//
-//
-/*!
-**
-** Copyright (c) 2009 by John W. Ratcliff mailto:[email protected]
-**
-** Portions of this source has been released with the PhysXViewer application, as well as
-** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
-**
-** If you find this code useful or you are feeling particularily generous I would
-** ask that you please go to http://www.amillionpixels.us and make a donation
-** to Troy DeMolay.
-**
-** DeMolay is a youth group for young men between the ages of 12 and 21.
-** It teaches strong moral principles, as well as leadership skills and
-** public speaking.  The donations page uses the 'pay for pixels' paradigm
-** where, in this case, a pixel is only a single penny.  Donations can be
-** made for as small as $4 or as high as a $100 block.  Each person who donates
-** will get a link to their own site as well as acknowledgement on the
-** donations blog located here http://www.amillionpixels.blogspot.com/
-**
-** If you wish to contact me you can use the following methods:
-**
-** Skype ID: jratcliff63367
-** Yahoo: jratcliff63367
-** AOL: jratcliff1961
-** email: [email protected]
-**
-**
-** The MIT license:
-**
-** Permission is hereby granted, free of charge, to any person obtaining a copy
-** of this software and associated documentation files (the "Software"), to deal
-** in the Software without restriction, including without limitation the rights
-** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-** copies of the Software, and to permit persons to whom the Software is furnished
-** to do so, subject to the following conditions:
-**
-** The above copyright notice and this permission notice shall be included in all
-** copies or substantial portions of the Software.
-
-** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-#pragma warning(disable:4996)
-
-#include "NvUserMemAlloc.h"
-#include "NvHashMap.h"
-
-namespace CONVEX_DECOMPOSITION
-{
-
-void fm_inverseRT(const REAL matrix[16],const REAL pos[3],REAL t[3]) // inverse rotate translate the point.
-{
-
-	REAL _x = pos[0] - matrix[3*4+0];
-	REAL _y = pos[1] - matrix[3*4+1];
-	REAL _z = pos[2] - matrix[3*4+2];
-
-	// Multiply inverse-translated source vector by inverted rotation transform
-
-	t[0] = (matrix[0*4+0] * _x) + (matrix[0*4+1] * _y) + (matrix[0*4+2] * _z);
-	t[1] = (matrix[1*4+0] * _x) + (matrix[1*4+1] * _y) + (matrix[1*4+2] * _z);
-	t[2] = (matrix[2*4+0] * _x) + (matrix[2*4+1] * _y) + (matrix[2*4+2] * _z);
-
-}
-
-REAL fm_getDeterminant(const REAL matrix[16])
-{
-  REAL tempv[3];
-  REAL p0[3];
-  REAL p1[3];
-  REAL p2[3];
-
-
-	p0[0] = matrix[0*4+0];
-	p0[1] = matrix[0*4+1];
-	p0[2] = matrix[0*4+2];
-
-	p1[0] = matrix[1*4+0];
-	p1[1] = matrix[1*4+1];
-	p1[2] = matrix[1*4+2];
-
-	p2[0] = matrix[2*4+0];
-	p2[1] = matrix[2*4+1];
-	p2[2] = matrix[2*4+2];
-
-  fm_cross(tempv,p1,p2);
-
-  return fm_dot(p0,tempv);
-
-}
-
-REAL fm_squared(REAL x) { return x*x; };
-
-void fm_decomposeTransform(const REAL local_transform[16],REAL trans[3],REAL rot[4],REAL scale[3])
-{
-
-  trans[0] = local_transform[12];
-  trans[1] = local_transform[13];
-  trans[2] = local_transform[14];
-
-  scale[0] = sqrt(fm_squared(local_transform[0*4+0]) + fm_squared(local_transform[0*4+1]) + fm_squared(local_transform[0*4+2]));
-  scale[1] = sqrt(fm_squared(local_transform[1*4+0]) + fm_squared(local_transform[1*4+1]) + fm_squared(local_transform[1*4+2]));
-  scale[2] = sqrt(fm_squared(local_transform[2*4+0]) + fm_squared(local_transform[2*4+1]) + fm_squared(local_transform[2*4+2]));
-
-  REAL m[16];
-  memcpy(m,local_transform,sizeof(REAL)*16);
-
-  REAL sx = 1.0f / scale[0];
-  REAL sy = 1.0f / scale[1];
-  REAL sz = 1.0f / scale[2];
-
-  m[0*4+0]*=sx;
-  m[0*4+1]*=sx;
-  m[0*4+2]*=sx;
-
-  m[1*4+0]*=sy;
-  m[1*4+1]*=sy;
-  m[1*4+2]*=sy;
-
-  m[2*4+0]*=sz;
-  m[2*4+1]*=sz;
-  m[2*4+2]*=sz;
-
-  fm_matrixToQuat(m,rot);
-
-}
-
-void fm_getSubMatrix(NxI32 ki,NxI32 kj,REAL pDst[16],const REAL matrix[16])
-{
-	NxI32 row, col;
-	NxI32 dstCol = 0, dstRow = 0;
-
-	for ( col = 0; col < 4; col++ )
-	{
-		if ( col == kj )
-		{
-			continue;
-		}
-		for ( dstRow = 0, row = 0; row < 4; row++ )
-		{
-			if ( row == ki )
-			{
-				continue;
-			}
-			pDst[dstCol*4+dstRow] = matrix[col*4+row];
-			dstRow++;
-		}
-		dstCol++;
-	}
-}
-
-void  fm_inverseTransform(const REAL matrix[16],REAL inverse_matrix[16])
-{
-	REAL determinant = fm_getDeterminant(matrix);
-	determinant = 1.0f / determinant;
-	for (NxI32 i = 0; i < 4; i++ )
-	{
-		for (NxI32 j = 0; j < 4; j++ )
-		{
-			NxI32 sign = 1 - ( ( i + j ) % 2 ) * 2;
-			REAL subMat[16];
-      fm_identity(subMat);
-			fm_getSubMatrix( i, j, subMat, matrix );
-			REAL subDeterminant = fm_getDeterminant(subMat);
-			inverse_matrix[i*4+j] = ( subDeterminant * sign ) * determinant;
-		}
-	}
-}
-
-void fm_identity(REAL matrix[16]) // set 4x4 matrix to identity.
-{
-	matrix[0*4+0] = 1;
-	matrix[1*4+1] = 1;
-	matrix[2*4+2] = 1;
-	matrix[3*4+3] = 1;
-
-	matrix[1*4+0] = 0;
-	matrix[2*4+0] = 0;
-	matrix[3*4+0] = 0;
-
-	matrix[0*4+1] = 0;
-	matrix[2*4+1] = 0;
-	matrix[3*4+1] = 0;
-
-	matrix[0*4+2] = 0;
-	matrix[1*4+2] = 0;
-	matrix[3*4+2] = 0;
-
-	matrix[0*4+3] = 0;
-	matrix[1*4+3] = 0;
-	matrix[2*4+3] = 0;
-
-}
-
-void  fm_quatToEuler(const REAL quat[4],REAL &ax,REAL &ay,REAL &az)
-{
-  REAL x = quat[0];
-  REAL y = quat[1];
-  REAL z = quat[2];
-  REAL w = quat[3];
-
-	REAL sint	     = (2.0f * w * y) - (2.0f * x * z);
-	REAL cost_temp = 1.0f - (sint * sint);
-	REAL cost	   	 = 0;
-
-	if ( (REAL)fabs(cost_temp) > 0.001f )
-	{
-		cost = sqrt( cost_temp );
-	}
-
-	REAL sinv, cosv, sinf, cosf;
-	if ( (REAL)fabs(cost) > 0.001f )
-	{
-    cost = 1.0f / cost;
-		sinv = ((2.0f * y * z) + (2.0f * w * x)) * cost;
-		cosv = (1.0f - (2.0f * x * x) - (2.0f * y * y)) * cost;
-		sinf = ((2.0f * x * y) + (2.0f * w * z)) * cost;
-		cosf = (1.0f - (2.0f * y * y) - (2.0f * z * z)) * cost;
-	}
-	else
-	{
-		sinv = (2.0f * w * x) - (2.0f * y * z);
-		cosv = 1.0f - (2.0f * x * x) - (2.0f * z * z);
-		sinf = 0;
-		cosf = 1.0f;
-	}
-
-	// compute output rotations
-	ax	= atan2( sinv, cosv );
-	ay	= atan2( sint, cost );
-  az	= atan2( sinf, cosf );
-
-}
-
-void fm_eulerToMatrix(REAL ax,REAL ay,REAL az,REAL *matrix) // convert euler (in radians) to a dest 4x4 matrix (translation set to zero)
-{
-  REAL quat[4];
-  fm_eulerToQuat(ax,ay,az,quat);
-  fm_quatToMatrix(quat,matrix);
-}
-
-void fm_getAABB(NxU32 vcount,const REAL *points,NxU32 pstride,REAL *bmin,REAL *bmax)
-{
-
-  const NxU8 *source = (const NxU8 *) points;
-
-	bmin[0] = points[0];
-	bmin[1] = points[1];
-	bmin[2] = points[2];
-
-	bmax[0] = points[0];
-	bmax[1] = points[1];
-	bmax[2] = points[2];
-
-
-  for (NxU32 i=1; i<vcount; i++)
-  {
-  	source+=pstride;
-  	const REAL *p = (const REAL *) source;
-
-  	if ( p[0] < bmin[0] ) bmin[0] = p[0];
-  	if ( p[1] < bmin[1] ) bmin[1] = p[1];
-  	if ( p[2] < bmin[2] ) bmin[2] = p[2];
-
-		if ( p[0] > bmax[0] ) bmax[0] = p[0];
-		if ( p[1] > bmax[1] ) bmax[1] = p[1];
-		if ( p[2] > bmax[2] ) bmax[2] = p[2];
-
-  }
-}
-
-void  fm_eulerToQuat(const REAL *euler,REAL *quat) // convert euler angles to quaternion.
-{
-  fm_eulerToQuat(euler[0],euler[1],euler[2],quat);
-}
-
-void fm_eulerToQuat(REAL roll,REAL pitch,REAL yaw,REAL *quat) // convert euler angles to quaternion.
-{
-	roll  *= 0.5f;
-	pitch *= 0.5f;
-	yaw   *= 0.5f;
-
-	REAL cr = cos(roll);
-	REAL cp = cos(pitch);
-	REAL cy = cos(yaw);
-
-	REAL sr = sin(roll);
-	REAL sp = sin(pitch);
-	REAL sy = sin(yaw);
-
-	REAL cpcy = cp * cy;
-	REAL spsy = sp * sy;
-	REAL spcy = sp * cy;
-	REAL cpsy = cp * sy;
-
-	quat[0]   = ( sr * cpcy - cr * spsy);
-	quat[1]   = ( cr * spcy + sr * cpsy);
-	quat[2]   = ( cr * cpsy - sr * spcy);
-	quat[3]   = cr * cpcy + sr * spsy;
-}
-
-void fm_quatToMatrix(const REAL *quat,REAL *matrix) // convert quaterinion rotation to matrix, zeros out the translation component.
-{
-
-	REAL xx = quat[0]*quat[0];
-	REAL yy = quat[1]*quat[1];
-	REAL zz = quat[2]*quat[2];
-	REAL xy = quat[0]*quat[1];
-	REAL xz = quat[0]*quat[2];
-	REAL yz = quat[1]*quat[2];
-	REAL wx = quat[3]*quat[0];
-	REAL wy = quat[3]*quat[1];
-	REAL wz = quat[3]*quat[2];
-
-	matrix[0*4+0] = 1 - 2 * ( yy + zz );
-	matrix[1*4+0] =     2 * ( xy - wz );
-	matrix[2*4+0] =     2 * ( xz + wy );
-
-	matrix[0*4+1] =     2 * ( xy + wz );
-	matrix[1*4+1] = 1 - 2 * ( xx + zz );
-	matrix[2*4+1] =     2 * ( yz - wx );
-
-	matrix[0*4+2] =     2 * ( xz - wy );
-	matrix[1*4+2] =     2 * ( yz + wx );
-	matrix[2*4+2] = 1 - 2 * ( xx + yy );
-
-	matrix[3*4+0] = matrix[3*4+1] = matrix[3*4+2] = (REAL) 0.0f;
-	matrix[0*4+3] = matrix[1*4+3] = matrix[2*4+3] = (REAL) 0.0f;
-	matrix[3*4+3] =(REAL) 1.0f;
-
-}
-
-
-void fm_quatRotate(const REAL *quat,const REAL *v,REAL *r) // rotate a vector directly by a quaternion.
-{
-  REAL left[4];
-
-	left[0] =   quat[3]*v[0] + quat[1]*v[2] - v[1]*quat[2];
-	left[1] =   quat[3]*v[1] + quat[2]*v[0] - v[2]*quat[0];
-	left[2] =   quat[3]*v[2] + quat[0]*v[1] - v[0]*quat[1];
-	left[3] = - quat[0]*v[0] - quat[1]*v[1] - quat[2]*v[2];
-
-	r[0] = (left[3]*-quat[0]) + (quat[3]*left[0]) + (left[1]*-quat[2]) - (-quat[1]*left[2]);
-	r[1] = (left[3]*-quat[1]) + (quat[3]*left[1]) + (left[2]*-quat[0]) - (-quat[2]*left[0]);
-	r[2] = (left[3]*-quat[2]) + (quat[3]*left[2]) + (left[0]*-quat[1]) - (-quat[0]*left[1]);
-
-}
-
-
-void fm_getTranslation(const REAL *matrix,REAL *t)
-{
-	t[0] = matrix[3*4+0];
-	t[1] = matrix[3*4+1];
-	t[2] = matrix[3*4+2];
-}
-
-void fm_matrixToQuat(const REAL *matrix,REAL *quat) // convert the 3x3 portion of a 4x4 matrix into a quaterion as x,y,z,w
-{
-
-	REAL tr = matrix[0*4+0] + matrix[1*4+1] + matrix[2*4+2];
-
-	// check the diagonal
-
-	if (tr > 0.0f )
-	{
-		REAL s = (REAL) sqrt ( (NxF64) (tr + 1.0f) );
-		quat[3] = s * 0.5f;
-		s = 0.5f / s;
-		quat[0] = (matrix[1*4+2] - matrix[2*4+1]) * s;
-		quat[1] = (matrix[2*4+0] - matrix[0*4+2]) * s;
-		quat[2] = (matrix[0*4+1] - matrix[1*4+0]) * s;
-
-	}
-	else
-	{
-		// diagonal is negative
-		NxI32 nxt[3] = {1, 2, 0};
-		REAL  qa[4];
-
-		NxI32 i = 0;
-
-		if (matrix[1*4+1] > matrix[0*4+0]) i = 1;
-		if (matrix[2*4+2] > matrix[i*4+i]) i = 2;
-
-		NxI32 j = nxt[i];
-		NxI32 k = nxt[j];
-
-		REAL s = sqrt ( ((matrix[i*4+i] - (matrix[j*4+j] + matrix[k*4+k])) + 1.0f) );
-
-		qa[i] = s * 0.5f;
-
-		if (s != 0.0f ) s = 0.5f / s;
-
-		qa[3] = (matrix[j*4+k] - matrix[k*4+j]) * s;
-		qa[j] = (matrix[i*4+j] + matrix[j*4+i]) * s;
-		qa[k] = (matrix[i*4+k] + matrix[k*4+i]) * s;
-
-		quat[0] = qa[0];
-		quat[1] = qa[1];
-		quat[2] = qa[2];
-		quat[3] = qa[3];
-	}
-
-
-}
-
-
-REAL fm_sphereVolume(REAL radius) // return's the volume of a sphere of this radius (4/3 PI * R cubed )
-{
-	return (4.0f / 3.0f ) * FM_PI * radius * radius * radius;
-}
-
-
-REAL fm_cylinderVolume(REAL radius,REAL h)
-{
-	return FM_PI * radius * radius *h;
-}
-
-REAL fm_capsuleVolume(REAL radius,REAL h)
-{
-	REAL volume = fm_sphereVolume(radius); // volume of the sphere portion.
-	REAL ch = h-radius*2; // this is the cylinder length
-	if ( ch > 0 )
-	{
-		volume+=fm_cylinderVolume(radius,ch);
-	}
-	return volume;
-}
-
-void  fm_transform(const REAL matrix[16],const REAL v[3],REAL t[3]) // rotate and translate this point
-{
-  if ( matrix )
-  {
-    REAL tx = (matrix[0*4+0] * v[0]) +  (matrix[1*4+0] * v[1]) + (matrix[2*4+0] * v[2]) + matrix[3*4+0];
-    REAL ty = (matrix[0*4+1] * v[0]) +  (matrix[1*4+1] * v[1]) + (matrix[2*4+1] * v[2]) + matrix[3*4+1];
-    REAL tz = (matrix[0*4+2] * v[0]) +  (matrix[1*4+2] * v[1]) + (matrix[2*4+2] * v[2]) + matrix[3*4+2];
-    t[0] = tx;
-    t[1] = ty;
-    t[2] = tz;
-  }
-  else
-  {
-    t[0] = v[0];
-    t[1] = v[1];
-    t[2] = v[2];
-  }
-}
-
-void  fm_rotate(const REAL matrix[16],const REAL v[3],REAL t[3]) // rotate and translate this point
-{
-  if ( matrix )
-  {
-    REAL tx = (matrix[0*4+0] * v[0]) +  (matrix[1*4+0] * v[1]) + (matrix[2*4+0] * v[2]);
-    REAL ty = (matrix[0*4+1] * v[0]) +  (matrix[1*4+1] * v[1]) + (matrix[2*4+1] * v[2]);
-    REAL tz = (matrix[0*4+2] * v[0]) +  (matrix[1*4+2] * v[1]) + (matrix[2*4+2] * v[2]);
-    t[0] = tx;
-    t[1] = ty;
-    t[2] = tz;
-  }
-  else
-  {
-    t[0] = v[0];
-    t[1] = v[1];
-    t[2] = v[2];
-  }
-}
-
-
-REAL fm_distance(const REAL *p1,const REAL *p2)
-{
-	REAL dx = p1[0] - p2[0];
-	REAL dy = p1[1] - p2[1];
-	REAL dz = p1[2] - p2[2];
-
-	return sqrt( dx*dx + dy*dy + dz *dz );
-}
-
-REAL fm_distanceSquared(const REAL *p1,const REAL *p2)
-{
-	REAL dx = p1[0] - p2[0];
-	REAL dy = p1[1] - p2[1];
-	REAL dz = p1[2] - p2[2];
-
-	return dx*dx + dy*dy + dz *dz;
-}
-
-
-REAL fm_distanceSquaredXZ(const REAL *p1,const REAL *p2)
-{
-	REAL dx = p1[0] - p2[0];
-	REAL dz = p1[2] - p2[2];
-
-	return dx*dx +  dz *dz;
-}
-
-
-REAL fm_computePlane(const REAL *A,const REAL *B,const REAL *C,REAL *n) // returns D
-{
-	REAL vx = (B[0] - C[0]);
-	REAL vy = (B[1] - C[1]);
-	REAL vz = (B[2] - C[2]);
-
-	REAL wx = (A[0] - B[0]);
-	REAL wy = (A[1] - B[1]);
-	REAL wz = (A[2] - B[2]);
-
-	REAL vw_x = vy * wz - vz * wy;
-	REAL vw_y = vz * wx - vx * wz;
-	REAL vw_z = vx * wy - vy * wx;
-
-	REAL mag = sqrt((vw_x * vw_x) + (vw_y * vw_y) + (vw_z * vw_z));
-
-	if ( mag < 0.000001f )
-	{
-		mag = 0;
-	}
-	else
-	{
-		mag = 1.0f/mag;
-	}
-
-	REAL x = vw_x * mag;
-	REAL y = vw_y * mag;
-	REAL z = vw_z * mag;
-
-
-	REAL D = 0.0f - ((x*A[0])+(y*A[1])+(z*A[2]));
-
-  n[0] = x;
-  n[1] = y;
-  n[2] = z;
-
-	return D;
-}
-
-REAL fm_distToPlane(const REAL *plane,const REAL *p) // computes the distance of this point from the plane.
-{
-  return p[0]*plane[0]+p[1]*plane[1]+p[2]*plane[2]+plane[3];
-}
-
-REAL fm_dot(const REAL *p1,const REAL *p2)
-{
-  return p1[0]*p2[0]+p1[1]*p2[1]+p1[2]*p2[2];
-}
-
-void fm_cross(REAL *cross,const REAL *a,const REAL *b)
-{
-	cross[0] = a[1]*b[2] - a[2]*b[1];
-	cross[1] = a[2]*b[0] - a[0]*b[2];
-	cross[2] = a[0]*b[1] - a[1]*b[0];
-}
-
-void fm_computeNormalVector(REAL *n,const REAL *p1,const REAL *p2)
-{
-  n[0] = p2[0] - p1[0];
-  n[1] = p2[1] - p1[1];
-  n[2] = p2[2] - p1[2];
-  fm_normalize(n);
-}
-
-bool  fm_computeWindingOrder(const REAL *p1,const REAL *p2,const REAL *p3) // returns true if the triangle is clockwise.
-{
-  bool ret = false;
-
-  REAL v1[3];
-  REAL v2[3];
-
-  fm_computeNormalVector(v1,p1,p2); // p2-p1 (as vector) and then normalized
-  fm_computeNormalVector(v2,p1,p3); // p3-p1 (as vector) and then normalized
-
-  REAL cross[3];
-
-  fm_cross(cross, v1, v2 );
-  REAL ref[3] = { 1, 0, 0 };
-
-  REAL d = fm_dot( cross, ref );
-
-
-  if ( d <= 0 )
-    ret = false;
-  else
-    ret = true;
-
-  return ret;
-}
-
-REAL fm_normalize(REAL *n) // normalize this vector
-{
-  REAL dist = (REAL)sqrt(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]);
-  if ( dist > 0.0000001f )
-  {
-    REAL mag = 1.0f / dist;
-    n[0]*=mag;
-    n[1]*=mag;
-    n[2]*=mag;
-  }
-  else
-  {
-    n[0] = 1;
-    n[1] = 0;
-    n[2] = 0;
-  }
-
-  return dist;
-}
-
-
-void  fm_matrixMultiply(const REAL *pA,const REAL *pB,REAL *pM)
-{
-#if 1
-
-  REAL a = pA[0*4+0] * pB[0*4+0] + pA[0*4+1] * pB[1*4+0] + pA[0*4+2] * pB[2*4+0] + pA[0*4+3] * pB[3*4+0];
-  REAL b = pA[0*4+0] * pB[0*4+1] + pA[0*4+1] * pB[1*4+1] + pA[0*4+2] * pB[2*4+1] + pA[0*4+3] * pB[3*4+1];
-  REAL c = pA[0*4+0] * pB[0*4+2] + pA[0*4+1] * pB[1*4+2] + pA[0*4+2] * pB[2*4+2] + pA[0*4+3] * pB[3*4+2];
-  REAL d = pA[0*4+0] * pB[0*4+3] + pA[0*4+1] * pB[1*4+3] + pA[0*4+2] * pB[2*4+3] + pA[0*4+3] * pB[3*4+3];
-
-  REAL e = pA[1*4+0] * pB[0*4+0] + pA[1*4+1] * pB[1*4+0] + pA[1*4+2] * pB[2*4+0] + pA[1*4+3] * pB[3*4+0];
-  REAL f = pA[1*4+0] * pB[0*4+1] + pA[1*4+1] * pB[1*4+1] + pA[1*4+2] * pB[2*4+1] + pA[1*4+3] * pB[3*4+1];
-  REAL g = pA[1*4+0] * pB[0*4+2] + pA[1*4+1] * pB[1*4+2] + pA[1*4+2] * pB[2*4+2] + pA[1*4+3] * pB[3*4+2];
-  REAL h = pA[1*4+0] * pB[0*4+3] + pA[1*4+1] * pB[1*4+3] + pA[1*4+2] * pB[2*4+3] + pA[1*4+3] * pB[3*4+3];
-
-  REAL i = pA[2*4+0] * pB[0*4+0] + pA[2*4+1] * pB[1*4+0] + pA[2*4+2] * pB[2*4+0] + pA[2*4+3] * pB[3*4+0];
-  REAL j = pA[2*4+0] * pB[0*4+1] + pA[2*4+1] * pB[1*4+1] + pA[2*4+2] * pB[2*4+1] + pA[2*4+3] * pB[3*4+1];
-  REAL k = pA[2*4+0] * pB[0*4+2] + pA[2*4+1] * pB[1*4+2] + pA[2*4+2] * pB[2*4+2] + pA[2*4+3] * pB[3*4+2];
-  REAL l = pA[2*4+0] * pB[0*4+3] + pA[2*4+1] * pB[1*4+3] + pA[2*4+2] * pB[2*4+3] + pA[2*4+3] * pB[3*4+3];
-
-  REAL m = pA[3*4+0] * pB[0*4+0] + pA[3*4+1] * pB[1*4+0] + pA[3*4+2] * pB[2*4+0] + pA[3*4+3] * pB[3*4+0];
-  REAL n = pA[3*4+0] * pB[0*4+1] + pA[3*4+1] * pB[1*4+1] + pA[3*4+2] * pB[2*4+1] + pA[3*4+3] * pB[3*4+1];
-  REAL o = pA[3*4+0] * pB[0*4+2] + pA[3*4+1] * pB[1*4+2] + pA[3*4+2] * pB[2*4+2] + pA[3*4+3] * pB[3*4+2];
-  REAL p = pA[3*4+0] * pB[0*4+3] + pA[3*4+1] * pB[1*4+3] + pA[3*4+2] * pB[2*4+3] + pA[3*4+3] * pB[3*4+3];
-
-  pM[0] = a;
-  pM[1] = b;
-  pM[2] = c;
-  pM[3] = d;
-
-  pM[4] = e;
-  pM[5] = f;
-  pM[6] = g;
-  pM[7] = h;
-
-  pM[8] = i;
-  pM[9] = j;
-  pM[10] = k;
-  pM[11] = l;
-
-  pM[12] = m;
-  pM[13] = n;
-  pM[14] = o;
-  pM[15] = p;
-
-
-#else
-	memset(pM, 0, sizeof(REAL)*16);
-	for(NxI32 i=0; i<4; i++ )
-		for(NxI32 j=0; j<4; j++ )
-			for(NxI32 k=0; k<4; k++ )
-				pM[4*i+j] +=  pA[4*i+k] * pB[4*k+j];
-#endif
-}
-
-
-void  fm_eulerToQuatDX(REAL x,REAL y,REAL z,REAL *quat) // convert euler angles to quaternion using the fucked up DirectX method
-{
-  REAL matrix[16];
-  fm_eulerToMatrix(x,y,z,matrix);
-  fm_matrixToQuat(matrix,quat);
-}
-
-// implementation copied from: http://blogs.msdn.com/mikepelton/archive/2004/10/29/249501.aspx
-void  fm_eulerToMatrixDX(REAL x,REAL y,REAL z,REAL *matrix) // convert euler angles to quaternion using the fucked up DirectX method.
-{
-  fm_identity(matrix);
-  matrix[0*4+0] = cos(z)*cos(y) + sin(z)*sin(x)*sin(y);
-  matrix[0*4+1] = sin(z)*cos(x);
-  matrix[0*4+2] = cos(z)*-sin(y) + sin(z)*sin(x)*cos(y);
-
-  matrix[1*4+0] = -sin(z)*cos(y)+cos(z)*sin(x)*sin(y);
-  matrix[1*4+1] = cos(z)*cos(x);
-  matrix[1*4+2] = sin(z)*sin(y) +cos(z)*sin(x)*cos(y);
-
-  matrix[2*4+0] = cos(x)*sin(y);
-  matrix[2*4+1] = -sin(x);
-  matrix[2*4+2] = cos(x)*cos(y);
-}
-
-
-void  fm_scale(REAL x,REAL y,REAL z,REAL *fscale) // apply scale to the matrix.
-{
-  fscale[0*4+0] = x;
-  fscale[1*4+1] = y;
-  fscale[2*4+2] = z;
-}
-
-
-void  fm_composeTransform(const REAL *position,const REAL *quat,const REAL *scale,REAL *matrix)
-{
-  fm_identity(matrix);
-  fm_quatToMatrix(quat,matrix);
-
-  if ( scale && ( scale[0] != 1 || scale[1] != 1 || scale[2] != 1 ) )
-  {
-    REAL work[16];
-    memcpy(work,matrix,sizeof(REAL)*16);
-    REAL mscale[16];
-    fm_identity(mscale);
-    fm_scale(scale[0],scale[1],scale[2],mscale);
-    fm_matrixMultiply(work,mscale,matrix);
-  }
-
-  matrix[12] = position[0];
-  matrix[13] = position[1];
-  matrix[14] = position[2];
-}
-
-
-void  fm_setTranslation(const REAL *translation,REAL *matrix)
-{
-  matrix[12] = translation[0];
-  matrix[13] = translation[1];
-  matrix[14] = translation[2];
-}
-
-static REAL enorm0_3d ( REAL x0, REAL y0, REAL z0, REAL x1, REAL y1, REAL z1 )
-
-/**********************************************************************/
-
-/*
-Purpose:
-
-ENORM0_3D computes the Euclidean norm of (P1-P0) in 3D.
-
-Modified:
-
-18 April 1999
-
-Author:
-
-John Burkardt
-
-Parameters:
-
-Input, REAL X0, Y0, Z0, X1, Y1, Z1, the coordinates of the points 
-P0 and P1.
-
-Output, REAL ENORM0_3D, the Euclidean norm of (P1-P0).
-*/
-{
-  REAL value;
-
-  value = sqrt (
-    ( x1 - x0 ) * ( x1 - x0 ) + 
-    ( y1 - y0 ) * ( y1 - y0 ) + 
-    ( z1 - z0 ) * ( z1 - z0 ) );
-
-  return value;
-}
-
-
-static REAL triangle_area_3d ( REAL x1, REAL y1, REAL z1, REAL x2,REAL y2, REAL z2, REAL x3, REAL y3, REAL z3 )
-
-                        /**********************************************************************/
-
-                        /*
-                        Purpose:
-
-                        TRIANGLE_AREA_3D computes the area of a triangle in 3D.
-
-                        Modified:
-
-                        22 April 1999
-
-                        Author:
-
-                        John Burkardt
-
-                        Parameters:
-
-                        Input, REAL X1, Y1, Z1, X2, Y2, Z2, X3, Y3, Z3, the (X,Y,Z)
-                        coordinates of the corners of the triangle.
-
-                        Output, REAL TRIANGLE_AREA_3D, the area of the triangle.
-                        */
-{
-  REAL a;
-  REAL alpha;
-  REAL area;
-  REAL b;
-  REAL base;
-  REAL c;
-  REAL dot;
-  REAL height;
-  /*
-  Find the projection of (P3-P1) onto (P2-P1).
-  */
-  dot = 
-    ( x2 - x1 ) * ( x3 - x1 ) +
-    ( y2 - y1 ) * ( y3 - y1 ) +
-    ( z2 - z1 ) * ( z3 - z1 );
-
-  base = enorm0_3d ( x1, y1, z1, x2, y2, z2 );
-  /*
-  The height of the triangle is the length of (P3-P1) after its
-  projection onto (P2-P1) has been subtracted.
-  */
-  if ( base == 0.0 ) {
-
-    height = 0.0;
-
-  }
-  else {
-
-    alpha = dot / ( base * base );
-
-    a = x3 - x1 - alpha * ( x2 - x1 );
-    b = y3 - y1 - alpha * ( y2 - y1 );
-    c = z3 - z1 - alpha * ( z2 - z1 );
-
-    height = sqrt ( a * a + b * b + c * c );
-
-  }
-
-  area = 0.5f * base * height;
-
-  return area;
-}
-
-
-REAL fm_computeArea(const REAL *p1,const REAL *p2,const REAL *p3)
-{
-  REAL ret = 0;
-
-  ret = triangle_area_3d(p1[0],p1[1],p1[2],p2[0],p2[1],p2[2],p3[0],p3[1],p3[2]);
-
-  return ret;
-}
-
-
-void  fm_lerp(const REAL *p1,const REAL *p2,REAL *dest,REAL lerpValue)
-{
-  dest[0] = ((p2[0] - p1[0])*lerpValue) + p1[0];
-  dest[1] = ((p2[1] - p1[1])*lerpValue) + p1[1];
-  dest[2] = ((p2[2] - p1[2])*lerpValue) + p1[2];
-}
-
-bool fm_pointTestXZ(const REAL *p,const REAL *i,const REAL *j)
-{
-  bool ret = false;
-
-  if (((( i[2] <= p[2] ) && ( p[2]  < j[2] )) || (( j[2] <= p[2] ) && ( p[2]  < i[2] ))) && ( p[0] < (j[0] - i[0]) * (p[2] - i[2]) / (j[2] - i[2]) + i[0]))
-    ret = true;
-
-  return ret;
-};
-
-
-bool  fm_insideTriangleXZ(const REAL *p,const REAL *p1,const REAL *p2,const REAL *p3)
-{
-  bool ret = false;
-
-  NxI32 c = 0;
-  if ( fm_pointTestXZ(p,p1,p2) ) c = !c;
-  if ( fm_pointTestXZ(p,p2,p3) ) c = !c;
-  if ( fm_pointTestXZ(p,p3,p1) ) c = !c;
-  if ( c ) ret = true;
-
-  return ret;
-}
-
-bool  fm_insideAABB(const REAL *pos,const REAL *bmin,const REAL *bmax)
-{
-  bool ret = false;
-
-  if ( pos[0] >= bmin[0] && pos[0] <= bmax[0] &&
-       pos[1] >= bmin[1] && pos[1] <= bmax[1] &&
-       pos[2] >= bmin[2] && pos[2] <= bmax[2] )
-    ret = true;
-
-  return ret;
-}
-
-
-NxU32 fm_clipTestPoint(const REAL *bmin,const REAL *bmax,const REAL *pos)
-{
-  NxU32 ret = 0;
-
-  if ( pos[0] < bmin[0] )
-    ret|=FMCS_XMIN;
-  else if ( pos[0] > bmax[0] )
-    ret|=FMCS_XMAX;
-
-  if ( pos[1] < bmin[1] )
-    ret|=FMCS_YMIN;
-  else if ( pos[1] > bmax[1] )
-    ret|=FMCS_YMAX;
-
-  if ( pos[2] < bmin[2] )
-    ret|=FMCS_ZMIN;
-  else if ( pos[2] > bmax[2] )
-    ret|=FMCS_ZMAX;
-
-  return ret;
-}
-
-NxU32 fm_clipTestPointXZ(const REAL *bmin,const REAL *bmax,const REAL *pos) // only tests X and Z, not Y
-{
-  NxU32 ret = 0;
-
-  if ( pos[0] < bmin[0] )
-    ret|=FMCS_XMIN;
-  else if ( pos[0] > bmax[0] )
-    ret|=FMCS_XMAX;
-
-  if ( pos[2] < bmin[2] )
-    ret|=FMCS_ZMIN;
-  else if ( pos[2] > bmax[2] )
-    ret|=FMCS_ZMAX;
-
-  return ret;
-}
-
-NxU32 fm_clipTestAABB(const REAL *bmin,const REAL *bmax,const REAL *p1,const REAL *p2,const REAL *p3,NxU32 &andCode)
-{
-  NxU32 orCode = 0;
-
-  andCode = FMCS_XMIN | FMCS_XMAX | FMCS_YMIN | FMCS_YMAX | FMCS_ZMIN | FMCS_ZMAX;
-
-  NxU32 c = fm_clipTestPoint(bmin,bmax,p1);
-  orCode|=c;
-  andCode&=c;
-
-  c = fm_clipTestPoint(bmin,bmax,p2);
-  orCode|=c;
-  andCode&=c;
-
-  c = fm_clipTestPoint(bmin,bmax,p3);
-  orCode|=c;
-  andCode&=c;
-
-  return orCode;
-}
-
-bool intersect(const REAL *si,const REAL *ei,const REAL *bmin,const REAL *bmax,REAL *time)
-{
-  REAL st,et,fst = 0,fet = 1;
-
-  for (NxI32 i = 0; i < 3; i++)
-  {
-    if (*si < *ei)
-    {
-      if (*si > *bmax || *ei < *bmin)
-        return false;
-      REAL di = *ei - *si;
-      st = (*si < *bmin)? (*bmin - *si) / di: 0;
-      et = (*ei > *bmax)? (*bmax - *si) / di: 1;
-    }
-    else
-    {
-      if (*ei > *bmax || *si < *bmin)
-        return false;
-      REAL di = *ei - *si;
-      st = (*si > *bmax)? (*bmax - *si) / di: 0;
-      et = (*ei < *bmin)? (*bmin - *si) / di: 1;
-    }
-
-    if (st > fst) fst = st;
-    if (et < fet) fet = et;
-    if (fet < fst)
-      return false;
-    bmin++; bmax++;
-    si++; ei++;
-  }
-
-  *time = fst;
-  return true;
-}
-
-
-
-bool fm_lineTestAABB(const REAL *p1,const REAL *p2,const REAL *bmin,const REAL *bmax,REAL &time)
-{
-  bool sect = intersect(p1,p2,bmin,bmax,&time);
-  return sect;
-}
-
-
-bool fm_lineTestAABBXZ(const REAL *p1,const REAL *p2,const REAL *bmin,const REAL *bmax,REAL &time)
-{
-  REAL _bmin[3];
-  REAL _bmax[3];
-
-  _bmin[0] = bmin[0];
-  _bmin[1] = -1e9;
-  _bmin[2] = bmin[2];
-
-  _bmax[0] = bmax[0];
-  _bmax[1] = 1e9;
-  _bmax[2] = bmax[2];
-
-  bool sect = intersect(p1,p2,_bmin,_bmax,&time);
-
-  return sect;
-}
-
-void  fm_minmax(const REAL *p,REAL *bmin,REAL *bmax) // accmulate to a min-max value
-{
-
-  if ( p[0] < bmin[0] ) bmin[0] = p[0];
-  if ( p[1] < bmin[1] ) bmin[1] = p[1];
-  if ( p[2] < bmin[2] ) bmin[2] = p[2];
-
-  if ( p[0] > bmax[0] ) bmax[0] = p[0];
-  if ( p[1] > bmax[1] ) bmax[1] = p[1];
-  if ( p[2] > bmax[2] ) bmax[2] = p[2];
-
-}
-
-REAL fm_solveX(const REAL *plane,REAL y,REAL z) // solve for X given this plane equation and the other two components.
-{
-  REAL x = (y*plane[1]+z*plane[2]+plane[3]) / -plane[0];
-  return x;
-}
-
-REAL fm_solveY(const REAL *plane,REAL x,REAL z) // solve for Y given this plane equation and the other two components.
-{
-  REAL y = (x*plane[0]+z*plane[2]+plane[3]) / -plane[1];
-  return y;
-}
-
-
-REAL fm_solveZ(const REAL *plane,REAL x,REAL y) // solve for Y given this plane equation and the other two components.
-{
-  REAL z = (x*plane[0]+y*plane[1]+plane[3]) / -plane[2];
-  return z;
-}
-
-
-void  fm_getAABBCenter(const REAL *bmin,const REAL *bmax,REAL *center)
-{
-  center[0] = (bmax[0]-bmin[0])*0.5f+bmin[0];
-  center[1] = (bmax[1]-bmin[1])*0.5f+bmin[1];
-  center[2] = (bmax[2]-bmin[2])*0.5f+bmin[2];
-}
-
-FM_Axis fm_getDominantAxis(const REAL normal[3])
-{
-  FM_Axis ret = FM_XAXIS;
-
-  REAL x = fabs(normal[0]);
-  REAL y = fabs(normal[1]);
-  REAL z = fabs(normal[2]);
-
-  if ( y > x && y > z )
-    ret = FM_YAXIS;
-  else if ( z > x && z > y )
-    ret = FM_ZAXIS;
-
-  return ret;
-}
-
-
-bool fm_lineSphereIntersect(const REAL *center,REAL radius,const REAL *p1,const REAL *p2,REAL *intersect)
-{
-  bool ret = false;
-
-  REAL dir[3];
-
-  dir[0] = p2[0]-p1[0];
-  dir[1] = p2[1]-p1[1];
-  dir[2] = p2[2]-p1[2];
-
-  REAL distance = sqrt( dir[0]*dir[0]+dir[1]*dir[1]+dir[2]*dir[2]);
-
-  if ( distance > 0 )
-  {
-    REAL recip = 1.0f / distance;
-    dir[0]*=recip;
-    dir[1]*=recip;
-    dir[2]*=recip;
-    ret = fm_raySphereIntersect(center,radius,p1,dir,distance,intersect);
-  }
-  else
-  {
-    dir[0] = center[0]-p1[0];
-    dir[1] = center[1]-p1[1];
-    dir[2] = center[2]-p1[2];
-    REAL d2 = dir[0]*dir[0]+dir[1]*dir[1]+dir[2]*dir[2];
-    REAL r2 = radius*radius;
-    if ( d2 < r2 )
-    {
-      ret = true;
-      if ( intersect )
-      {
-        intersect[0] = p1[0];
-        intersect[1] = p1[1];
-        intersect[2] = p1[2];
-      }
-    }
-  }
-  return ret;
-}
-
-#define DOT(p1,p2) (p1[0]*p2[0]+p1[1]*p2[1]+p1[2]*p2[2])
-
-bool fm_raySphereIntersect(const REAL *center,REAL radius,const REAL *pos,const REAL *dir,REAL distance,REAL *intersect)
-{
-  bool ret = false;
-
-  REAL E0[3];
-
-  E0[0] = center[0] - pos[0];
-  E0[1] = center[1] - pos[1];
-  E0[2] = center[2] - pos[2];
-
-  REAL V[3];
-
-  V[0]  = dir[0];
-  V[1]  = dir[1];
-  V[2]  = dir[2];
-
-
-  REAL dist2   = E0[0]*E0[0] + E0[1]*E0[1] + E0[2] * E0[2];
-  REAL radius2 = radius*radius; // radius squared..
-
-  // Bug Fix For Gem, if origin is *inside* the sphere, invert the
-  // direction vector so that we get a valid intersection location.
-  if ( dist2 < radius2 )
-  {
-    V[0]*=-1;
-    V[1]*=-1;
-    V[2]*=-1;
-  }
-
-
-	REAL v = DOT(E0,V);
-
-	REAL disc = radius2 - (dist2 - v*v);
-
-	if (disc > 0.0f)
-	{
-		if ( intersect )
-		{
-		  REAL d = sqrt(disc);
-      REAL diff = v-d;
-      if ( diff < distance )
-      {
-        intersect[0] = pos[0]+V[0]*diff;
-        intersect[1] = pos[1]+V[1]*diff;
-        intersect[2] = pos[2]+V[2]*diff;
-        ret = true;
-      }
-    }
-	}
-
-	return ret;
-}
-
-
-void fm_catmullRom(REAL *out_vector,const REAL *p1,const REAL *p2,const REAL *p3,const REAL *p4, const REAL s)
-{
-  REAL s_squared = s * s;
-  REAL s_cubed = s_squared * s;
-
-  REAL coefficient_p1 = -s_cubed + 2*s_squared - s;
-  REAL coefficient_p2 = 3 * s_cubed - 5 * s_squared + 2;
-  REAL coefficient_p3 = -3 * s_cubed +4 * s_squared + s;
-  REAL coefficient_p4 = s_cubed - s_squared;
-
-  out_vector[0] = (coefficient_p1 * p1[0] + coefficient_p2 * p2[0] + coefficient_p3 * p3[0] + coefficient_p4 * p4[0])*0.5f;
-  out_vector[1] = (coefficient_p1 * p1[1] + coefficient_p2 * p2[1] + coefficient_p3 * p3[1] + coefficient_p4 * p4[1])*0.5f;
-  out_vector[2] = (coefficient_p1 * p1[2] + coefficient_p2 * p2[2] + coefficient_p3 * p3[2] + coefficient_p4 * p4[2])*0.5f;
-}
-
-bool fm_intersectAABB(const REAL *bmin1,const REAL *bmax1,const REAL *bmin2,const REAL *bmax2)
-{
-  if ((bmin1[0] > bmax2[0]) || (bmin2[0] > bmax1[0])) return false;
-  if ((bmin1[1] > bmax2[1]) || (bmin2[1] > bmax1[1])) return false;
-  if ((bmin1[2] > bmax2[2]) || (bmin2[2] > bmax1[2])) return false;
-  return true;
-
-}
-
-bool  fm_insideAABB(const REAL *obmin,const REAL *obmax,const REAL *tbmin,const REAL *tbmax) // test if bounding box tbmin/tmbax is fully inside obmin/obmax
-{
-  bool ret = false;
-
-  if ( tbmax[0] <= obmax[0] &&
-       tbmax[1] <= obmax[1] &&
-       tbmax[2] <= obmax[2] &&
-       tbmin[0] >= obmin[0] &&
-       tbmin[1] >= obmin[1] &&
-       tbmin[2] >= obmin[2] ) ret = true;
-
-  return ret;
-}
-
-
-// Reference, from Stan Melax in Game Gems I
-//  Quaternion q;
-//  vector3 c = CrossProduct(v0,v1);
-//  REAL   d = DotProduct(v0,v1);
-//  REAL   s = (REAL)sqrt((1+d)*2);
-//  q.x = c.x / s;
-//  q.y = c.y / s;
-//  q.z = c.z / s;
-//  q.w = s /2.0f;
-//  return q;
-void fm_rotationArc(const REAL *v0,const REAL *v1,REAL *quat)
-{
-  REAL cross[3];
-
-  fm_cross(cross,v0,v1);
-  REAL d = fm_dot(v0,v1);
-  REAL s = sqrt((1+d)*2);
-  REAL recip = 1.0f / s;
-
-  quat[0] = cross[0] * recip;
-  quat[1] = cross[1] * recip;
-  quat[2] = cross[2] * recip;
-  quat[3] = s * 0.5f;
-
-}
-
-
-REAL fm_distancePointLineSegment(const REAL *Point,const REAL *LineStart,const REAL *LineEnd,REAL *intersection,LineSegmentType &type,REAL epsilon)
-{
-  REAL ret;
-
-  REAL LineMag = fm_distance( LineEnd, LineStart );
-
-  if ( LineMag > 0 )
-  {
-    REAL U = ( ( ( Point[0] - LineStart[0] ) * ( LineEnd[0] - LineStart[0] ) ) + ( ( Point[1] - LineStart[1] ) * ( LineEnd[1] - LineStart[1] ) ) + ( ( Point[2] - LineStart[2] ) * ( LineEnd[2] - LineStart[2] ) ) ) / ( LineMag * LineMag );
-    if( U < 0.0f || U > 1.0f )
-    {
-      REAL d1 = fm_distanceSquared(Point,LineStart);
-      REAL d2 = fm_distanceSquared(Point,LineEnd);
-      if ( d1 <= d2 )
-      {
-        ret = sqrt(d1);
-        intersection[0] = LineStart[0];
-        intersection[1] = LineStart[1];
-        intersection[2] = LineStart[2];
-        type = LS_START;
-      }
-      else
-      {
-        ret = sqrt(d2);
-        intersection[0] = LineEnd[0];
-        intersection[1] = LineEnd[1];
-        intersection[2] = LineEnd[2];
-        type = LS_END;
-      }
-    }
-    else
-    {
-      intersection[0] = LineStart[0] + U * ( LineEnd[0] - LineStart[0] );
-      intersection[1] = LineStart[1] + U * ( LineEnd[1] - LineStart[1] );
-      intersection[2] = LineStart[2] + U * ( LineEnd[2] - LineStart[2] );
-
-      ret = fm_distance(Point,intersection);
-
-      REAL d1 = fm_distanceSquared(intersection,LineStart);
-      REAL d2 = fm_distanceSquared(intersection,LineEnd);
-	  REAL mag = (epsilon*2)*(epsilon*2);
-
-      if ( d1 < mag ) // if less than 1/100th the total distance, treat is as the 'start'
-      {
-        type = LS_START;
-      }
-      else if ( d2 < mag )
-      {
-        type = LS_END;
-      }
-      else
-      {
-        type = LS_MIDDLE;
-      }
-
-    }
-  }
-  else
-  {
-    ret = LineMag;
-    intersection[0] = LineEnd[0];
-    intersection[1] = LineEnd[1];
-    intersection[2] = LineEnd[2];
-    type = LS_END;
-  }
-
-  return ret;
-}
-
-
-#ifndef BEST_FIT_PLANE_H
-
-#define BEST_FIT_PLANE_H
-
-template <class Type> class Eigen
-{
-public:
-
-
-  void DecrSortEigenStuff(void)
-  {
-    Tridiagonal(); //diagonalize the matrix.
-    QLAlgorithm(); //
-    DecreasingSort();
-    GuaranteeRotation();
-  }
-
-  void Tridiagonal(void)
-  {
-    Type fM00 = mElement[0][0];
-    Type fM01 = mElement[0][1];
-    Type fM02 = mElement[0][2];
-    Type fM11 = mElement[1][1];
-    Type fM12 = mElement[1][2];
-    Type fM22 = mElement[2][2];
-
-    m_afDiag[0] = fM00;
-    m_afSubd[2] = 0;
-    if (fM02 != (Type)0.0)
-    {
-      Type fLength = sqrt(fM01*fM01+fM02*fM02);
-      Type fInvLength = ((Type)1.0)/fLength;
-      fM01 *= fInvLength;
-      fM02 *= fInvLength;
-      Type fQ = ((Type)2.0)*fM01*fM12+fM02*(fM22-fM11);
-      m_afDiag[1] = fM11+fM02*fQ;
-      m_afDiag[2] = fM22-fM02*fQ;
-      m_afSubd[0] = fLength;
-      m_afSubd[1] = fM12-fM01*fQ;
-      mElement[0][0] = (Type)1.0;
-      mElement[0][1] = (Type)0.0;
-      mElement[0][2] = (Type)0.0;
-      mElement[1][0] = (Type)0.0;
-      mElement[1][1] = fM01;
-      mElement[1][2] = fM02;
-      mElement[2][0] = (Type)0.0;
-      mElement[2][1] = fM02;
-      mElement[2][2] = -fM01;
-      m_bIsRotation = false;
-    }
-    else
-    {
-      m_afDiag[1] = fM11;
-      m_afDiag[2] = fM22;
-      m_afSubd[0] = fM01;
-      m_afSubd[1] = fM12;
-      mElement[0][0] = (Type)1.0;
-      mElement[0][1] = (Type)0.0;
-      mElement[0][2] = (Type)0.0;
-      mElement[1][0] = (Type)0.0;
-      mElement[1][1] = (Type)1.0;
-      mElement[1][2] = (Type)0.0;
-      mElement[2][0] = (Type)0.0;
-      mElement[2][1] = (Type)0.0;
-      mElement[2][2] = (Type)1.0;
-      m_bIsRotation = true;
-    }
-  }
-
-  bool QLAlgorithm(void)
-  {
-    const NxI32 iMaxIter = 32;
-
-    for (NxI32 i0 = 0; i0 <3; i0++)
-    {
-      NxI32 i1;
-      for (i1 = 0; i1 < iMaxIter; i1++)
-      {
-        NxI32 i2;
-        for (i2 = i0; i2 <= (3-2); i2++)
-        {
-          Type fTmp = fabs(m_afDiag[i2]) + fabs(m_afDiag[i2+1]);
-          if ( fabs(m_afSubd[i2]) + fTmp == fTmp )
-            break;
-        }
-        if (i2 == i0)
-        {
-          break;
-        }
-
-        Type fG = (m_afDiag[i0+1] - m_afDiag[i0])/(((Type)2.0) * m_afSubd[i0]);
-        Type fR = sqrt(fG*fG+(Type)1.0);
-        if (fG < (Type)0.0)
-        {
-          fG = m_afDiag[i2]-m_afDiag[i0]+m_afSubd[i0]/(fG-fR);
-        }
-        else
-        {
-          fG = m_afDiag[i2]-m_afDiag[i0]+m_afSubd[i0]/(fG+fR);
-        }
-        Type fSin = (Type)1.0, fCos = (Type)1.0, fP = (Type)0.0;
-        for (NxI32 i3 = i2-1; i3 >= i0; i3--)
-        {
-          Type fF = fSin*m_afSubd[i3];
-          Type fB = fCos*m_afSubd[i3];
-          if (fabs(fF) >= fabs(fG))
-          {
-            fCos = fG/fF;
-            fR = sqrt(fCos*fCos+(Type)1.0);
-            m_afSubd[i3+1] = fF*fR;
-            fSin = ((Type)1.0)/fR;
-            fCos *= fSin;
-          }
-          else
-          {
-            fSin = fF/fG;
-            fR = sqrt(fSin*fSin+(Type)1.0);
-            m_afSubd[i3+1] = fG*fR;
-            fCos = ((Type)1.0)/fR;
-            fSin *= fCos;
-          }
-          fG = m_afDiag[i3+1]-fP;
-          fR = (m_afDiag[i3]-fG)*fSin+((Type)2.0)*fB*fCos;
-          fP = fSin*fR;
-          m_afDiag[i3+1] = fG+fP;
-          fG = fCos*fR-fB;
-          for (NxI32 i4 = 0; i4 < 3; i4++)
-          {
-            fF = mElement[i4][i3+1];
-            mElement[i4][i3+1] = fSin*mElement[i4][i3]+fCos*fF;
-            mElement[i4][i3] = fCos*mElement[i4][i3]-fSin*fF;
-          }
-        }
-        m_afDiag[i0] -= fP;
-        m_afSubd[i0] = fG;
-        m_afSubd[i2] = (Type)0.0;
-      }
-      if (i1 == iMaxIter)
-      {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  void DecreasingSort(void)
-  {
-    //sort eigenvalues in decreasing order, e[0] >= ... >= e[iSize-1]
-    for (NxI32 i0 = 0, i1; i0 <= 3-2; i0++)
-    {
-      // locate maximum eigenvalue
-      i1 = i0;
-      Type fMax = m_afDiag[i1];
-      NxI32 i2;
-      for (i2 = i0+1; i2 < 3; i2++)
-      {
-        if (m_afDiag[i2] > fMax)
-        {
-          i1 = i2;
-          fMax = m_afDiag[i1];
-        }
-      }
-
-      if (i1 != i0)
-      {
-        // swap eigenvalues
-        m_afDiag[i1] = m_afDiag[i0];
-        m_afDiag[i0] = fMax;
-        // swap eigenvectors
-        for (i2 = 0; i2 < 3; i2++)
-        {
-          Type fTmp = mElement[i2][i0];
-          mElement[i2][i0] = mElement[i2][i1];
-          mElement[i2][i1] = fTmp;
-          m_bIsRotation = !m_bIsRotation;
-        }
-      }
-    }
-  }
-
-
-  void GuaranteeRotation(void)
-  {
-    if (!m_bIsRotation)
-    {
-      // change sign on the first column
-      for (NxI32 iRow = 0; iRow <3; iRow++)
-      {
-        mElement[iRow][0] = -mElement[iRow][0];
-      }
-    }
-  }
-
-  Type mElement[3][3];
-  Type m_afDiag[3];
-  Type m_afSubd[3];
-  bool m_bIsRotation;
-};
-
-#endif
-
-bool fm_computeBestFitPlane(NxU32 vcount,
-                     const REAL *points,
-                     NxU32 vstride,
-                     const REAL *weights,
-                     NxU32 wstride,
-                     REAL *plane)
-{
-  bool ret = false;
-
-  REAL kOrigin[3] = { 0, 0, 0 };
-
-  REAL wtotal = 0;
-
-  {
-    const char *source  = (const char *) points;
-    const char *wsource = (const char *) weights;
-
-    for (NxU32 i=0; i<vcount; i++)
-    {
-
-      const REAL *p = (const REAL *) source;
-
-      REAL w = 1;
-
-      if ( wsource )
-      {
-        const REAL *ws = (const REAL *) wsource;
-        w = *ws; //
-        wsource+=wstride;
-      }
-
-      kOrigin[0]+=p[0]*w;
-      kOrigin[1]+=p[1]*w;
-      kOrigin[2]+=p[2]*w;
-
-      wtotal+=w;
-
-      source+=vstride;
-    }
-  }
-
-  REAL recip = 1.0f / wtotal; // reciprocol of total weighting
-
-  kOrigin[0]*=recip;
-  kOrigin[1]*=recip;
-  kOrigin[2]*=recip;
-
-
-  REAL fSumXX=0;
-  REAL fSumXY=0;
-  REAL fSumXZ=0;
-
-  REAL fSumYY=0;
-  REAL fSumYZ=0;
-  REAL fSumZZ=0;
-
-
-  {
-    const char *source  = (const char *) points;
-    const char *wsource = (const char *) weights;
-
-    for (NxU32 i=0; i<vcount; i++)
-    {
-
-      const REAL *p = (const REAL *) source;
-
-      REAL w = 1;
-
-      if ( wsource )
-      {
-        const REAL *ws = (const REAL *) wsource;
-        w = *ws; //
-        wsource+=wstride;
-      }
-
-      REAL kDiff[3];
-
-      kDiff[0] = w*(p[0] - kOrigin[0]); // apply vertex weighting!
-      kDiff[1] = w*(p[1] - kOrigin[1]);
-      kDiff[2] = w*(p[2] - kOrigin[2]);
-
-      fSumXX+= kDiff[0] * kDiff[0]; // sume of the squares of the differences.
-      fSumXY+= kDiff[0] * kDiff[1]; // sume of the squares of the differences.
-      fSumXZ+= kDiff[0] * kDiff[2]; // sume of the squares of the differences.
-
-      fSumYY+= kDiff[1] * kDiff[1];
-      fSumYZ+= kDiff[1] * kDiff[2];
-      fSumZZ+= kDiff[2] * kDiff[2];
-
-
-      source+=vstride;
-    }
-  }
-
-  fSumXX *= recip;
-  fSumXY *= recip;
-  fSumXZ *= recip;
-  fSumYY *= recip;
-  fSumYZ *= recip;
-  fSumZZ *= recip;
-
-  // setup the eigensolver
-  Eigen<REAL> kES;
-
-  kES.mElement[0][0] = fSumXX;
-  kES.mElement[0][1] = fSumXY;
-  kES.mElement[0][2] = fSumXZ;
-
-  kES.mElement[1][0] = fSumXY;
-  kES.mElement[1][1] = fSumYY;
-  kES.mElement[1][2] = fSumYZ;
-
-  kES.mElement[2][0] = fSumXZ;
-  kES.mElement[2][1] = fSumYZ;
-  kES.mElement[2][2] = fSumZZ;
-
-  // compute eigenstuff, smallest eigenvalue is in last position
-  kES.DecrSortEigenStuff();
-
-  REAL kNormal[3];
-
-  kNormal[0] = kES.mElement[0][2];
-  kNormal[1] = kES.mElement[1][2];
-  kNormal[2] = kES.mElement[2][2];
-
-  // the minimum energy
-  plane[0] = kNormal[0];
-  plane[1] = kNormal[1];
-  plane[2] = kNormal[2];
-
-  plane[3] = 0 - fm_dot(kNormal,kOrigin);
-
-  ret = true;
-
-  return ret;
-}
-
-
-bool fm_colinear(const REAL a1[3],const REAL a2[3],const REAL b1[3],const REAL b2[3],REAL epsilon)  // true if these two line segments are co-linear.
-{
-  bool ret = false;
-
-  REAL dir1[3];
-  REAL dir2[3];
-
-  dir1[0] = (a2[0] - a1[0]);
-  dir1[1] = (a2[1] - a1[1]);
-  dir1[2] = (a2[2] - a1[2]);
-
-  dir2[0] = (b2[0]-a1[0]) - (b1[0]-a1[0]);
-  dir2[1] = (b2[1]-a1[1]) - (b1[1]-a1[1]);
-  dir2[2] = (b2[2]-a2[2]) - (b1[2]-a2[2]);
-
-  fm_normalize(dir1);
-  fm_normalize(dir2);
-
-  REAL dot = fm_dot(dir1,dir2);
-
-  if ( dot >= epsilon )
-  {
-    ret = true;
-  }
-
-
-  return ret;
-}
-
-bool fm_colinear(const REAL *p1,const REAL *p2,const REAL *p3,REAL epsilon)
-{
-  bool ret = false;
-
-  REAL dir1[3];
-  REAL dir2[3];
-
-  dir1[0] = p2[0] - p1[0];
-  dir1[1] = p2[1] - p1[1];
-  dir1[2] = p2[2] - p1[2];
-
-  dir2[0] = p3[0] - p2[0];
-  dir2[1] = p3[1] - p2[1];
-  dir2[2] = p3[2] - p2[2];
-
-  fm_normalize(dir1);
-  fm_normalize(dir2);
-
-  REAL dot = fm_dot(dir1,dir2);
-
-  if ( dot >= epsilon )
-  {
-    ret = true;
-  }
-
-
-  return ret;
-}
-
-void  fm_initMinMax(const REAL *p,REAL *bmin,REAL *bmax)
-{
-  bmax[0] = bmin[0] = p[0];
-  bmax[1] = bmin[1] = p[1];
-  bmax[2] = bmin[2] = p[2];
-}
-
-IntersectResult fm_intersectLineSegments2d(const REAL *a1,const REAL *a2,const REAL *b1,const REAL *b2,REAL *intersection)
-{
-  IntersectResult ret;
-
-  REAL denom  = ((b2[1] - b1[1])*(a2[0] - a1[0])) - ((b2[0] - b1[0])*(a2[1] - a1[1]));
-  REAL nume_a = ((b2[0] - b1[0])*(a1[1] - b1[1])) - ((b2[1] - b1[1])*(a1[0] - b1[0]));
-  REAL nume_b = ((a2[0] - a1[0])*(a1[1] - b1[1])) - ((a2[1] - a1[1])*(a1[0] - b1[0]));
-  if (denom == 0 )
-  {
-    if(nume_a == 0 && nume_b == 0)
-    {
-      ret = IR_COINCIDENT;
-    }
-    else
-    {
-      ret = IR_PARALLEL;
-    }
-  }
-  else
-  {
-
-    REAL recip = 1 / denom;
-    REAL ua = nume_a * recip;
-    REAL ub = nume_b * recip;
-
-    if(ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1 )
-    {
-      // Get the intersection point.
-      intersection[0] = a1[0] + ua*(a2[0] - a1[0]);
-      intersection[1] = a1[1] + ua*(a2[1] - a1[1]);
-      ret = IR_DO_INTERSECT;
-    }
-    else
-    {
-      ret = IR_DONT_INTERSECT;
-    }
-  }
-  return ret;
-}
-
-IntersectResult fm_intersectLineSegments2dTime(const REAL *a1,const REAL *a2,const REAL *b1,const REAL *b2,REAL &t1,REAL &t2)
-{
-  IntersectResult ret;
-
-  REAL denom  = ((b2[1] - b1[1])*(a2[0] - a1[0])) - ((b2[0] - b1[0])*(a2[1] - a1[1]));
-  REAL nume_a = ((b2[0] - b1[0])*(a1[1] - b1[1])) - ((b2[1] - b1[1])*(a1[0] - b1[0]));
-  REAL nume_b = ((a2[0] - a1[0])*(a1[1] - b1[1])) - ((a2[1] - a1[1])*(a1[0] - b1[0]));
-  if (denom == 0 )
-  {
-    if(nume_a == 0 && nume_b == 0)
-    {
-      ret = IR_COINCIDENT;
-    }
-    else
-    {
-      ret = IR_PARALLEL;
-    }
-  }
-  else
-  {
-
-    REAL recip = 1 / denom;
-    REAL ua = nume_a * recip;
-    REAL ub = nume_b * recip;
-
-    if(ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1 )
-    {
-      t1 = ua;
-      t2 = ub;
-      ret = IR_DO_INTERSECT;
-    }
-    else
-    {
-      ret = IR_DONT_INTERSECT;
-    }
-  }
-  return ret;
-}
-
-//**** Plane Triangle Intersection
-
-
-
-
-
-// assumes that the points are on opposite sides of the plane!
-void fm_intersectPointPlane(const REAL *p1,const REAL *p2,REAL *split,const REAL *plane)
-{
-
-  REAL dp1 = fm_distToPlane(plane,p1);
-
-  REAL dir[3];
-
-  dir[0] = p2[0] - p1[0];
-  dir[1] = p2[1] - p1[1];
-  dir[2] = p2[2] - p1[2];
-
-  REAL dot1 = dir[0]*plane[0] + dir[1]*plane[1] + dir[2]*plane[2];
-  REAL dot2 = dp1 - plane[3];
-
-  REAL    t = -(plane[3] + dot2 ) / dot1;
-
-  split[0] = (dir[0]*t)+p1[0];
-  split[1] = (dir[1]*t)+p1[1];
-  split[2] = (dir[2]*t)+p1[2];
-
-}
-
-PlaneTriResult fm_getSidePlane(const REAL *p,const REAL *plane,REAL epsilon)
-{
-  PlaneTriResult ret = PTR_ON_PLANE;
-
-  REAL d = fm_distToPlane(plane,p);
-
-  if ( d < -epsilon || d > epsilon )
-  {
-    if ( d > 0 )
-  		ret =  PTR_FRONT; // it is 'in front' within the provided epsilon value.
-    else
-      ret = PTR_BACK;
-  }
-
-  return ret;
-}
-
-
-
-#ifndef PLANE_TRIANGLE_INTERSECTION_H
-
-#define PLANE_TRIANGLE_INTERSECTION_H
-
-#define MAXPTS 256
-
-template <class Type> class point
-{
-public:
-
-  void set(const Type *p)
-  {
-    x = p[0];
-    y = p[1];
-    z = p[2];
-  }
-
-  Type x;
-  Type y;
-  Type z;
-};
-
-template <class Type> class plane
-{
-public:
-  plane(const Type *p)
-  {
-    normal.x = p[0];
-    normal.y = p[1];
-    normal.z = p[2];
-    D        = p[3];
-  }
-
-  Type Classify_Point(const point<Type> &p)
-  {
-    return p.x*normal.x + p.y*normal.y + p.z*normal.z + D;
-  }
-
-  point<Type> normal;
-  Type  D;
-};
-
-template <class Type> class polygon
-{
-public:
-  polygon(void)
-  {
-    mVcount = 0;
-  }
-
-  polygon(const Type *p1,const Type *p2,const Type *p3)
-  {
-    mVcount = 3;
-    mVertices[0].set(p1);
-    mVertices[1].set(p2);
-    mVertices[2].set(p3);
-  }
-
-
-  NxI32 NumVertices(void) const { return mVcount; };
-
-  const point<Type>& Vertex(NxI32 index)
-  {
-    if ( index < 0 ) index+=mVcount;
-    return mVertices[index];
-  };
-
-
-  void set(const point<Type> *pts,NxI32 count)
-  {
-    for (NxI32 i=0; i<count; i++)
-    {
-      mVertices[i] = pts[i];
-    }
-    mVcount = count;
-  }
-
-
-  void Split_Polygon(polygon<Type> *poly,plane<Type> *part, polygon<Type> &front, polygon<Type> &back)
-  {
-    NxI32   count = poly->NumVertices ();
-    NxI32   out_c = 0, in_c = 0;
-    point<Type> ptA, ptB,outpts[MAXPTS],inpts[MAXPTS];
-    Type sideA, sideB;
-    ptA = poly->Vertex (count - 1);
-    sideA = part->Classify_Point (ptA);
-    for (NxI32 i = -1; ++i < count;)
-    {
-      ptB = poly->Vertex(i);
-      sideB = part->Classify_Point(ptB);
-      if (sideB > 0)
-      {
-        if (sideA < 0)
-        {
-  			  point<Type> v;
-          fm_intersectPointPlane(&ptB.x, &ptA.x, &v.x, &part->normal.x );
-          outpts[out_c++] = inpts[in_c++] = v;
-        }
-        outpts[out_c++] = ptB;
-      }
-      else if (sideB < 0)
-      {
-        if (sideA > 0)
-        {
-          point<Type> v;
-          fm_intersectPointPlane(&ptB.x, &ptA.x, &v.x, &part->normal.x );
-          outpts[out_c++] = inpts[in_c++] = v;
-        }
-        inpts[in_c++] = ptB;
-      }
-      else
-         outpts[out_c++] = inpts[in_c++] = ptB;
-      ptA = ptB;
-      sideA = sideB;
-    }
-
-    front.set(&outpts[0], out_c);
-    back.set(&inpts[0], in_c);
-  }
-
-  NxI32           mVcount;
-  point<Type>   mVertices[MAXPTS];
-};
-
-
-
-#endif
-
-static inline void add(const REAL *p,REAL *dest,NxU32 tstride,NxU32 &pcount)
-{
-  char *d = (char *) dest;
-  d = d + pcount*tstride;
-  dest = (REAL *) d;
-  dest[0] = p[0];
-  dest[1] = p[1];
-  dest[2] = p[2];
-  pcount++;
-	assert( pcount <= 4 );
-}
-
-
-PlaneTriResult fm_planeTriIntersection(const REAL *_plane,    // the plane equation in Ax+By+Cz+D format
-                                    const REAL *triangle, // the source triangle.
-                                    NxU32 tstride,  // stride in bytes of the input and output *vertices*
-                                    REAL        epsilon,  // the co-planar epsilon value.
-                                    REAL       *front,    // the triangle in front of the
-                                    NxU32 &fcount,  // number of vertices in the 'front' triangle
-                                    REAL       *back,     // the triangle in back of the plane
-                                    NxU32 &bcount) // the number of vertices in the 'back' triangle.
-{
-
-  fcount = 0;
-  bcount = 0;
-
-  const char *tsource = (const char *) triangle;
-
-  // get the three vertices of the triangle.
-  const REAL *p1     = (const REAL *) (tsource);
-  const REAL *p2     = (const REAL *) (tsource+tstride);
-  const REAL *p3     = (const REAL *) (tsource+tstride*2);
-
-
-  PlaneTriResult r1   = fm_getSidePlane(p1,_plane,epsilon); // compute the side of the plane each vertex is on
-  PlaneTriResult r2   = fm_getSidePlane(p2,_plane,epsilon);
-  PlaneTriResult r3   = fm_getSidePlane(p3,_plane,epsilon);
-
-  // If any of the points lay right *on* the plane....
-  if ( r1 == PTR_ON_PLANE || r2 == PTR_ON_PLANE || r3 == PTR_ON_PLANE )
-  {
-    // If the triangle is completely co-planar, then just treat it as 'front' and return!
-    if ( r1 == PTR_ON_PLANE && r2 == PTR_ON_PLANE && r3 == PTR_ON_PLANE )
-    {
-      add(p1,front,tstride,fcount);
-      add(p2,front,tstride,fcount);
-      add(p3,front,tstride,fcount);
-      return PTR_FRONT;
-    }
-    // Decide to place the co-planar points on the same side as the co-planar point.
-    PlaneTriResult r= PTR_ON_PLANE;
-    if ( r1 != PTR_ON_PLANE )
-      r = r1;
-    else if ( r2 != PTR_ON_PLANE )
-      r = r2;
-    else if ( r3 != PTR_ON_PLANE )
-      r = r3;
-
-    if ( r1 == PTR_ON_PLANE ) r1 = r;
-    if ( r2 == PTR_ON_PLANE ) r2 = r;
-    if ( r3 == PTR_ON_PLANE ) r3 = r;
-
-  }
-
-  if ( r1 == r2 && r1 == r3 ) // if all three vertices are on the same side of the plane.
-  {
-    if ( r1 == PTR_FRONT ) // if all three are in front of the plane, then copy to the 'front' output triangle.
-    {
-      add(p1,front,tstride,fcount);
-      add(p2,front,tstride,fcount);
-      add(p3,front,tstride,fcount);
-    }
-    else
-    {
-      add(p1,back,tstride,bcount); // if all three are in 'back' then copy to the 'back' output triangle.
-      add(p2,back,tstride,bcount);
-      add(p3,back,tstride,bcount);
-    }
-    return r1; // if all three points are on the same side of the plane return result
-  }
-
-
-  polygon<REAL> pi(p1,p2,p3);
-  polygon<REAL>  pfront,pback;
-
-  plane<REAL>    part(_plane);
-
-  pi.Split_Polygon(&pi,&part,pfront,pback);
-
-  for (NxI32 i=0; i<pfront.mVcount; i++)
-  {
-    add( &pfront.mVertices[i].x, front, tstride, fcount );
-  }
-
-  for (NxI32 i=0; i<pback.mVcount; i++)
-  {
-    add( &pback.mVertices[i].x, back, tstride, bcount );
-  }
-
-  PlaneTriResult ret = PTR_SPLIT;
-
-  if ( fcount < 3 ) fcount = 0;
-  if ( bcount < 3 ) bcount = 0;
-
-  if ( fcount == 0 && bcount )
-    ret = PTR_BACK;
-
-  if ( bcount == 0 && fcount )
-    ret = PTR_FRONT;
-
-
-  return ret;
-}
-
-// computes the OBB for this set of points relative to this transform matrix.
-void computeOBB(NxU32 vcount,const REAL *points,NxU32 pstride,REAL *sides,REAL *matrix)
-{
-  const char *src = (const char *) points;
-
-  REAL bmin[3] = { 1e9, 1e9, 1e9 };
-  REAL bmax[3] = { -1e9, -1e9, -1e9 };
-
-  for (NxU32 i=0; i<vcount; i++)
-  {
-    const REAL *p = (const REAL *) src;
-    REAL t[3];
-
-    fm_inverseRT(matrix, p, t ); // inverse rotate translate
-
-    if ( t[0] < bmin[0] ) bmin[0] = t[0];
-    if ( t[1] < bmin[1] ) bmin[1] = t[1];
-    if ( t[2] < bmin[2] ) bmin[2] = t[2];
-
-    if ( t[0] > bmax[0] ) bmax[0] = t[0];
-    if ( t[1] > bmax[1] ) bmax[1] = t[1];
-    if ( t[2] > bmax[2] ) bmax[2] = t[2];
-
-    src+=pstride;
-  }
-
-  REAL center[3];
-
-  sides[0] = bmax[0]-bmin[0];
-  sides[1] = bmax[1]-bmin[1];
-  sides[2] = bmax[2]-bmin[2];
-
-  center[0] = sides[0]*0.5f+bmin[0];
-  center[1] = sides[1]*0.5f+bmin[1];
-  center[2] = sides[2]*0.5f+bmin[2];
-
-  REAL ocenter[3];
-
-  fm_rotate(matrix,center,ocenter);
-
-  matrix[12]+=ocenter[0];
-  matrix[13]+=ocenter[1];
-  matrix[14]+=ocenter[2];
-
-}
-
-void fm_computeBestFitOBB(NxU32 vcount,const REAL *points,NxU32 pstride,REAL *sides,REAL *matrix,bool bruteForce)
-{
-  REAL plane[4];
-  fm_computeBestFitPlane(vcount,points,pstride,0,0,plane);
-  fm_planeToMatrix(plane,matrix);
-  computeOBB( vcount, points, pstride, sides, matrix );
-
-  REAL refmatrix[16];
-  memcpy(refmatrix,matrix,16*sizeof(REAL));
-
-  REAL volume = sides[0]*sides[1]*sides[2];
-  if ( bruteForce )
-  {
-    for (REAL a=10; a<180; a+=10)
-    {
-      REAL quat[4];
-      fm_eulerToQuat(0,a*FM_DEG_TO_RAD,0,quat);
-      REAL temp[16];
-      REAL pmatrix[16];
-      fm_quatToMatrix(quat,temp);
-      fm_matrixMultiply(temp,refmatrix,pmatrix);
-      REAL psides[3];
-      computeOBB( vcount, points, pstride, psides, pmatrix );
-      REAL v = psides[0]*psides[1]*psides[2];
-      if ( v < volume )
-      {
-        volume = v;
-        memcpy(matrix,pmatrix,sizeof(REAL)*16);
-        sides[0] = psides[0];
-        sides[1] = psides[1];
-        sides[2] = psides[2];
-      }
-    }
-  }
-}
-
-void fm_computeBestFitOBB(NxU32 vcount,const REAL *points,NxU32 pstride,REAL *sides,REAL *pos,REAL *quat,bool bruteForce)
-{
-  REAL matrix[16];
-  fm_computeBestFitOBB(vcount,points,pstride,sides,matrix,bruteForce);
-  fm_getTranslation(matrix,pos);
-  fm_matrixToQuat(matrix,quat);
-}
-
-void fm_computeBestFitABB(NxU32 vcount,const REAL *points,NxU32 pstride,REAL *sides,REAL *pos)
-{
-	REAL bmin[3];
-	REAL bmax[3];
-
-  bmin[0] = points[0];
-  bmin[1] = points[1];
-  bmin[2] = points[2];
-
-  bmax[0] = points[0];
-  bmax[1] = points[1];
-  bmax[2] = points[2];
-
-	const char *cp = (const char *) points;
-	for (NxU32 i=0; i<vcount; i++)
-	{
-		const REAL *p = (const REAL *) cp;
-
-		if ( p[0] < bmin[0] ) bmin[0] = p[0];
-		if ( p[1] < bmin[1] ) bmin[1] = p[1];
-		if ( p[2] < bmin[2] ) bmin[2] = p[2];
-
-    if ( p[0] > bmax[0] ) bmax[0] = p[0];
-    if ( p[1] > bmax[1] ) bmax[1] = p[1];
-    if ( p[2] > bmax[2] ) bmax[2] = p[2];
-
-    cp+=pstride;
-	}
-
-
-	sides[0] = bmax[0] - bmin[0];
-	sides[1] = bmax[1] - bmin[1];
-	sides[2] = bmax[2] - bmin[2];
-
-	pos[0] = bmin[0]+sides[0]*0.5f;
-	pos[1] = bmin[1]+sides[1]*0.5f;
-	pos[2] = bmin[2]+sides[2]*0.5f;
-
-}
-
-
-void fm_planeToMatrix(const REAL *plane,REAL *matrix) // convert a plane equation to a 4x4 rotation matrix
-{
-  REAL ref[3] = { 0, 1, 0 };
-  REAL quat[4];
-  fm_rotationArc(ref,plane,quat);
-  fm_quatToMatrix(quat,matrix);
-  REAL origin[3] = { 0, -plane[3], 0 };
-  REAL center[3];
-  fm_transform(matrix,origin,center);
-  fm_setTranslation(center,matrix);
-}
-
-void fm_planeToQuat(const REAL *plane,REAL *quat,REAL *pos) // convert a plane equation to a quaternion and translation
-{
-  REAL ref[3] = { 0, 1, 0 };
-  REAL matrix[16];
-  fm_rotationArc(ref,plane,quat);
-  fm_quatToMatrix(quat,matrix);
-  REAL origin[3] = { 0, plane[3], 0 };
-  fm_transform(matrix,origin,pos);
-}
-
-void fm_eulerMatrix(REAL ax,REAL ay,REAL az,REAL *matrix) // convert euler (in radians) to a dest 4x4 matrix (translation set to zero)
-{
-  REAL quat[4];
-  fm_eulerToQuat(ax,ay,az,quat);
-  fm_quatToMatrix(quat,matrix);
-}
-
-
-//**********************************************************
-//**********************************************************
-//**** Vertex Welding
-//**********************************************************
-//**********************************************************
-
-#ifndef VERTEX_INDEX_H
-
-#define VERTEX_INDEX_H
-
-namespace VERTEX_INDEX
-{
-
-class KdTreeNode;
-
-typedef CONVEX_DECOMPOSITION::Array< KdTreeNode * > KdTreeNodeVector;
-
-enum Axes
-{
-  X_AXIS = 0,
-  Y_AXIS = 1,
-  Z_AXIS = 2
-};
-
-class KdTreeFindNode
-{
-public:
-  KdTreeFindNode(void)
-  {
-    mNode = 0;
-    mDistance = 0;
-  }
-  KdTreeNode  *mNode;
-  NxF64        mDistance;
-};
-
-class KdTreeInterface
-{
-public:
-  virtual const NxF64 * getPositionDouble(NxU32 index) const = 0;
-  virtual const NxF32  * getPositionFloat(NxU32 index) const = 0;
-};
-
-class KdTreeNode
-{
-public:
-  KdTreeNode(void)
-  {
-    mIndex = 0;
-    mLeft = 0;
-    mRight = 0;
-  }
-
-  KdTreeNode(NxU32 index)
-  {
-    mIndex = index;
-    mLeft = 0;
-    mRight = 0;
-  };
-
-	~KdTreeNode(void)
-  {
-  }
-
-
-  void addDouble(KdTreeNode *node,Axes dim,const KdTreeInterface *iface)
-  {
-    const NxF64 *nodePosition = iface->getPositionDouble( node->mIndex );
-    const NxF64 *position     = iface->getPositionDouble( mIndex );
-    switch ( dim )
-    {
-      case X_AXIS:
-        if ( nodePosition[0] <= position[0] )
-        {
-          if ( mLeft )
-            mLeft->addDouble(node,Y_AXIS,iface);
-          else
-            mLeft = node;
-        }
-        else
-        {
-          if ( mRight )
-            mRight->addDouble(node,Y_AXIS,iface);
-          else
-            mRight = node;
-        }
-        break;
-      case Y_AXIS:
-        if ( nodePosition[1] <= position[1] )
-        {
-          if ( mLeft )
-            mLeft->addDouble(node,Z_AXIS,iface);
-          else
-            mLeft = node;
-        }
-        else
-        {
-          if ( mRight )
-            mRight->addDouble(node,Z_AXIS,iface);
-          else
-            mRight = node;
-        }
-        break;
-      case Z_AXIS:
-        if ( nodePosition[2] <= position[2] )
-        {
-          if ( mLeft )
-            mLeft->addDouble(node,X_AXIS,iface);
-          else
-            mLeft = node;
-        }
-        else
-        {
-          if ( mRight )
-            mRight->addDouble(node,X_AXIS,iface);
-          else
-            mRight = node;
-        }
-        break;
-    }
-
-  }
-
-
-  void addFloat(KdTreeNode *node,Axes dim,const KdTreeInterface *iface)
-  {
-    const NxF32 *nodePosition = iface->getPositionFloat( node->mIndex );
-    const NxF32 *position     = iface->getPositionFloat( mIndex );
-    switch ( dim )
-    {
-      case X_AXIS:
-        if ( nodePosition[0] <= position[0] )
-        {
-          if ( mLeft )
-            mLeft->addFloat(node,Y_AXIS,iface);
-          else
-            mLeft = node;
-        }
-        else
-        {
-          if ( mRight )
-            mRight->addFloat(node,Y_AXIS,iface);
-          else
-            mRight = node;
-        }
-        break;
-      case Y_AXIS:
-        if ( nodePosition[1] <= position[1] )
-        {
-          if ( mLeft )
-            mLeft->addFloat(node,Z_AXIS,iface);
-          else
-            mLeft = node;
-        }
-        else
-        {
-          if ( mRight )
-            mRight->addFloat(node,Z_AXIS,iface);
-          else
-            mRight = node;
-        }
-        break;
-      case Z_AXIS:
-        if ( nodePosition[2] <= position[2] )
-        {
-          if ( mLeft )
-            mLeft->addFloat(node,X_AXIS,iface);
-          else
-            mLeft = node;
-        }
-        else
-        {
-          if ( mRight )
-            mRight->addFloat(node,X_AXIS,iface);
-          else
-            mRight = node;
-        }
-        break;
-    }
-
-  }
-
-
-  NxU32 getIndex(void) const { return mIndex; };
-
-  void search(Axes axis,const NxF64 *pos,NxF64 radius,NxU32 &count,NxU32 maxObjects,KdTreeFindNode *found,const KdTreeInterface *iface)
-  {
-
-    const NxF64 *position = iface->getPositionDouble(mIndex);
-
-    NxF64 dx = pos[0] - position[0];
-    NxF64 dy = pos[1] - position[1];
-    NxF64 dz = pos[2] - position[2];
-
-    KdTreeNode *search1 = 0;
-    KdTreeNode *search2 = 0;
-
-    switch ( axis )
-    {
-      case X_AXIS:
-       if ( dx <= 0 )     // JWR  if we are to the left
-       {
-        search1 = mLeft; // JWR  then search to the left
-        if ( -dx < radius )  // JWR  if distance to the right is less than our search radius, continue on the right as well.
-          search2 = mRight;
-       }
-       else
-       {
-         search1 = mRight; // JWR  ok, we go down the left tree
-         if ( dx < radius ) // JWR  if the distance from the right is less than our search radius
-	  			search2 = mLeft;
-        }
-        axis = Y_AXIS;
-        break;
-      case Y_AXIS:
-        if ( dy <= 0 )
-        {
-          search1 = mLeft;
-          if ( -dy < radius )
-    				search2 = mRight;
-        }
-        else
-        {
-          search1 = mRight;
-          if ( dy < radius )
-    				search2 = mLeft;
-        }
-        axis = Z_AXIS;
-        break;
-      case Z_AXIS:
-        if ( dz <= 0 )
-        {
-          search1 = mLeft;
-          if ( -dz < radius )
-    				search2 = mRight;
-        }
-        else
-        {
-          search1 = mRight;
-          if ( dz < radius )
-    				search2 = mLeft;
-        }
-        axis = X_AXIS;
-        break;
-    }
-
-    NxF64 r2 = radius*radius;
-    NxF64 m  = dx*dx+dy*dy+dz*dz;
-
-    if ( m < r2 )
-    {
-      switch ( count )
-      {
-        case 0:
-          found[count].mNode = this;
-          found[count].mDistance = m;
-          break;
-        case 1:
-          if ( m < found[0].mDistance )
-          {
-            if ( maxObjects == 1 )
-            {
-              found[0].mNode = this;
-              found[0].mDistance = m;
-            }
-            else
-            {
-              found[1] = found[0];
-              found[0].mNode = this;
-              found[0].mDistance = m;
-            }
-          }
-          else if ( maxObjects > 1)
-          {
-            found[1].mNode = this;
-            found[1].mDistance = m;
-          }
-          break;
-        default:
-          {
-            bool inserted = false;
-
-            for (NxU32 i=0; i<count; i++)
-            {
-              if ( m < found[i].mDistance ) // if this one is closer than a pre-existing one...
-              {
-                // insertion sort...
-                NxU32 scan = count;
-                if ( scan >= maxObjects ) scan=maxObjects-1;
-                for (NxU32 j=scan; j>i; j--)
-                {
-                  found[j] = found[j-1];
-                }
-                found[i].mNode = this;
-                found[i].mDistance = m;
-                inserted = true;
-                break;
-              }
-            }
-
-            if ( !inserted && count < maxObjects )
-            {
-              found[count].mNode = this;
-              found[count].mDistance = m;
-            }
-          }
-          break;
-      }
-      count++;
-      if ( count > maxObjects )
-      {
-        count = maxObjects;
-      }
-    }
-
-
-    if ( search1 )
-  		search1->search( axis, pos,radius, count, maxObjects, found, iface);
-
-    if ( search2 )
-	  	search2->search( axis, pos,radius, count, maxObjects, found, iface);
-
-  }
-
-  void search(Axes axis,const NxF32 *pos,NxF32 radius,NxU32 &count,NxU32 maxObjects,KdTreeFindNode *found,const KdTreeInterface *iface)
-  {
-
-    const NxF32 *position = iface->getPositionFloat(mIndex);
-
-    NxF32 dx = pos[0] - position[0];
-    NxF32 dy = pos[1] - position[1];
-    NxF32 dz = pos[2] - position[2];
-
-    KdTreeNode *search1 = 0;
-    KdTreeNode *search2 = 0;
-
-    switch ( axis )
-    {
-      case X_AXIS:
-       if ( dx <= 0 )     // JWR  if we are to the left
-       {
-        search1 = mLeft; // JWR  then search to the left
-        if ( -dx < radius )  // JWR  if distance to the right is less than our search radius, continue on the right as well.
-          search2 = mRight;
-       }
-       else
-       {
-         search1 = mRight; // JWR  ok, we go down the left tree
-         if ( dx < radius ) // JWR  if the distance from the right is less than our search radius
-	  			search2 = mLeft;
-        }
-        axis = Y_AXIS;
-        break;
-      case Y_AXIS:
-        if ( dy <= 0 )
-        {
-          search1 = mLeft;
-          if ( -dy < radius )
-    				search2 = mRight;
-        }
-        else
-        {
-          search1 = mRight;
-          if ( dy < radius )
-    				search2 = mLeft;
-        }
-        axis = Z_AXIS;
-        break;
-      case Z_AXIS:
-        if ( dz <= 0 )
-        {
-          search1 = mLeft;
-          if ( -dz < radius )
-    				search2 = mRight;
-        }
-        else
-        {
-          search1 = mRight;
-          if ( dz < radius )
-    				search2 = mLeft;
-        }
-        axis = X_AXIS;
-        break;
-    }
-
-    NxF32 r2 = radius*radius;
-    NxF32 m  = dx*dx+dy*dy+dz*dz;
-
-    if ( m < r2 )
-    {
-      switch ( count )
-      {
-        case 0:
-          found[count].mNode = this;
-          found[count].mDistance = m;
-          break;
-        case 1:
-          if ( m < found[0].mDistance )
-          {
-            if ( maxObjects == 1 )
-            {
-              found[0].mNode = this;
-              found[0].mDistance = m;
-            }
-            else
-            {
-              found[1] = found[0];
-              found[0].mNode = this;
-              found[0].mDistance = m;
-            }
-          }
-          else if ( maxObjects > 1)
-          {
-            found[1].mNode = this;
-            found[1].mDistance = m;
-          }
-          break;
-        default:
-          {
-            bool inserted = false;
-
-            for (NxU32 i=0; i<count; i++)
-            {
-              if ( m < found[i].mDistance ) // if this one is closer than a pre-existing one...
-              {
-                // insertion sort...
-                NxU32 scan = count;
-                if ( scan >= maxObjects ) scan=maxObjects-1;
-                for (NxU32 j=scan; j>i; j--)
-                {
-                  found[j] = found[j-1];
-                }
-                found[i].mNode = this;
-                found[i].mDistance = m;
-                inserted = true;
-                break;
-              }
-            }
-
-            if ( !inserted && count < maxObjects )
-            {
-              found[count].mNode = this;
-              found[count].mDistance = m;
-            }
-          }
-          break;
-      }
-      count++;
-      if ( count > maxObjects )
-      {
-        count = maxObjects;
-      }
-    }
-
-
-    if ( search1 )
-  		search1->search( axis, pos,radius, count, maxObjects, found, iface);
-
-    if ( search2 )
-	  	search2->search( axis, pos,radius, count, maxObjects, found, iface);
-
-  }
-
-private:
-
-  void setLeft(KdTreeNode *left) { mLeft = left; };
-  void setRight(KdTreeNode *right) { mRight = right; };
-
-	KdTreeNode *getLeft(void)         { return mLeft; }
-	KdTreeNode *getRight(void)        { return mRight; }
-
-  NxU32          mIndex;
-  KdTreeNode     *mLeft;
-  KdTreeNode     *mRight;
-};
-
-
-#define MAX_BUNDLE_SIZE 1024  // 1024 nodes at a time, to minimize memory allocation and guarentee that pointers are persistent.
-
-class KdTreeNodeBundle : public Memalloc
-{
-public:
-
-  KdTreeNodeBundle(void)
-  {
-    mNext = 0;
-    mIndex = 0;
-  }
-
-  bool isFull(void) const
-  {
-    return (bool)( mIndex == MAX_BUNDLE_SIZE );
-  }
-
-  KdTreeNode * getNextNode(void)
-  {
-    assert(mIndex<MAX_BUNDLE_SIZE);
-    KdTreeNode *ret = &mNodes[mIndex];
-    mIndex++;
-    return ret;
-  }
-
-  KdTreeNodeBundle  *mNext;
-  NxU32             mIndex;
-  KdTreeNode         mNodes[MAX_BUNDLE_SIZE];
-};
-
-
-typedef CONVEX_DECOMPOSITION::Array< NxF64 > DoubleVector;
-typedef CONVEX_DECOMPOSITION::Array< NxF32 >  FloatVector;
-
-class KdTree : public KdTreeInterface, public Memalloc
-{
-public:
-  KdTree(void)
-  {
-    mRoot = 0;
-    mBundle = 0;
-    mVcount = 0;
-    mUseDouble = false;
-  }
-
-  virtual ~KdTree(void)
-  {
-    reset();
-  }
-
-  const NxF64 * getPositionDouble(NxU32 index) const
-  {
-    assert( mUseDouble );
-    assert ( index < mVcount );
-    return  &mVerticesDouble[index*3];
-  }
-
-  const NxF32 * getPositionFloat(NxU32 index) const
-  {
-    assert( !mUseDouble );
-    assert ( index < mVcount );
-    return  &mVerticesFloat[index*3];
-  }
-
-  NxU32 search(const NxF64 *pos,NxF64 radius,NxU32 maxObjects,KdTreeFindNode *found) const
-  {
-    assert( mUseDouble );
-    if ( !mRoot )	return 0;
-    NxU32 count = 0;
-    mRoot->search(X_AXIS,pos,radius,count,maxObjects,found,this);
-    return count;
-  }
-
-  NxU32 search(const NxF32 *pos,NxF32 radius,NxU32 maxObjects,KdTreeFindNode *found) const
-  {
-    assert( !mUseDouble );
-    if ( !mRoot )	return 0;
-    NxU32 count = 0;
-    mRoot->search(X_AXIS,pos,radius,count,maxObjects,found,this);
-    return count;
-  }
-
-  void reset(void)
-  {
-    mRoot = 0;
-    mVerticesDouble.clear();
-    mVerticesFloat.clear();
-    KdTreeNodeBundle *bundle = mBundle;
-    while ( bundle )
-    {
-      KdTreeNodeBundle *next = bundle->mNext;
-      delete bundle;
-      bundle = next;
-    }
-    mBundle = 0;
-    mVcount = 0;
-  }
-
-  NxU32 add(NxF64 x,NxF64 y,NxF64 z)
-  {
-    assert(mUseDouble);
-    NxU32 ret = mVcount;
-    mVerticesDouble.pushBack(x);
-    mVerticesDouble.pushBack(y);
-    mVerticesDouble.pushBack(z);
-    mVcount++;
-    KdTreeNode *node = getNewNode(ret);
-    if ( mRoot )
-    {
-      mRoot->addDouble(node,X_AXIS,this);
-    }
-    else
-    {
-      mRoot = node;
-    }
-    return ret;
-  }
-
-  NxU32 add(NxF32 x,NxF32 y,NxF32 z)
-  {
-    assert(!mUseDouble);
-    NxU32 ret = mVcount;
-    mVerticesFloat.pushBack(x);
-    mVerticesFloat.pushBack(y);
-    mVerticesFloat.pushBack(z);
-    mVcount++;
-    KdTreeNode *node = getNewNode(ret);
-    if ( mRoot )
-    {
-      mRoot->addFloat(node,X_AXIS,this);
-    }
-    else
-    {
-      mRoot = node;
-    }
-    return ret;
-  }
-
-  KdTreeNode * getNewNode(NxU32 index)
-  {
-    if ( mBundle == 0 )
-    {
-      mBundle = MEMALLOC_NEW(KdTreeNodeBundle);
-    }
-    if ( mBundle->isFull() )
-    {
-      KdTreeNodeBundle *bundle = MEMALLOC_NEW(KdTreeNodeBundle);
-      mBundle->mNext = bundle;
-      mBundle = bundle;
-    }
-    KdTreeNode *node = mBundle->getNextNode();
-    new ( node ) KdTreeNode(index);
-    return node;
-  }
-
-  NxU32 getNearest(const NxF64 *pos,NxF64 radius,bool &_found) const // returns the nearest possible neighbor's index.
-  {
-    assert( mUseDouble );
-    NxU32 ret = 0;
-
-    _found = false;
-    KdTreeFindNode found[1];
-    NxU32 count = search(pos,radius,1,found);
-    if ( count )
-    {
-      KdTreeNode *node = found[0].mNode;
-      ret = node->getIndex();
-      _found = true;
-    }
-    return ret;
-  }
-
-  NxU32 getNearest(const NxF32 *pos,NxF32 radius,bool &_found) const // returns the nearest possible neighbor's index.
-  {
-    assert( !mUseDouble );
-    NxU32 ret = 0;
-
-    _found = false;
-    KdTreeFindNode found[1];
-    NxU32 count = search(pos,radius,1,found);
-    if ( count )
-    {
-      KdTreeNode *node = found[0].mNode;
-      ret = node->getIndex();
-      _found = true;
-    }
-    return ret;
-  }
-
-  const NxF64 * getVerticesDouble(void) const
-  {
-    assert( mUseDouble );
-    const NxF64 *ret = 0;
-    if ( !mVerticesDouble.empty() )
-    {
-      ret = &mVerticesDouble[0];
-    }
-    return ret;
-  }
-
-  const NxF32 * getVerticesFloat(void) const
-  {
-    assert( !mUseDouble );
-    const NxF32 * ret = 0;
-    if ( !mVerticesFloat.empty() )
-    {
-      ret = &mVerticesFloat[0];
-    }
-    return ret;
-  }
-
-  NxU32 getVcount(void) const { return mVcount; };
-
-  void setUseDouble(bool useDouble)
-  {
-    mUseDouble = useDouble;
-  }
-
-private:
-  bool                    mUseDouble;
-  KdTreeNode             *mRoot;
-  KdTreeNodeBundle       *mBundle;
-  NxU32                  mVcount;
-  DoubleVector            mVerticesDouble;
-  FloatVector             mVerticesFloat;
-};
-
-}; // end of namespace VERTEX_INDEX
-
-class MyVertexIndex : public fm_VertexIndex, public Memalloc
-{
-public:
-  MyVertexIndex(NxF64 granularity,bool snapToGrid)
-  {
-    mDoubleGranularity = granularity;
-    mFloatGranularity  = (NxF32)granularity;
-    mSnapToGrid        = snapToGrid;
-    mUseDouble         = true;
-    mKdTree.setUseDouble(true);
-  }
-
-  MyVertexIndex(NxF32 granularity,bool snapToGrid)
-  {
-    mDoubleGranularity = granularity;
-    mFloatGranularity  = (NxF32)granularity;
-    mSnapToGrid        = snapToGrid;
-    mUseDouble         = false;
-    mKdTree.setUseDouble(false);
-  }
-
-  virtual ~MyVertexIndex(void)
-  {
-
-  }
-
-
-  NxF64 snapToGrid(NxF64 p)
-  {
-    NxF64 m = fmod(p,mDoubleGranularity);
-    p-=m;
-    return p;
-  }
-
-  NxF32 snapToGrid(NxF32 p)
-  {
-    NxF32 m = fmodf(p,mFloatGranularity);
-    p-=m;
-    return p;
-  }
-
-  NxU32    getIndex(const NxF32 *_p,bool &newPos)  // get index for a vector NxF32
-  {
-    NxU32 ret;
-
-    if ( mUseDouble )
-    {
-      NxF64 p[3];
-      p[0] = _p[0];
-      p[1] = _p[1];
-      p[2] = _p[2];
-      return getIndex(p,newPos);
-    }
-
-    newPos = false;
-
-    NxF32 p[3];
-
-    if ( mSnapToGrid )
-    {
-      p[0] = snapToGrid(_p[0]);
-      p[1] = snapToGrid(_p[1]);
-      p[2] = snapToGrid(_p[2]);
-    }
-    else
-    {
-      p[0] = _p[0];
-      p[1] = _p[1];
-      p[2] = _p[2];
-    }
-
-    bool found;
-    ret = mKdTree.getNearest(p,mFloatGranularity,found);
-    if ( !found )
-    {
-      newPos = true;
-      ret = mKdTree.add(p[0],p[1],p[2]);
-    }
-
-
-    return ret;
-  }
-
-  NxU32    getIndex(const NxF64 *_p,bool &newPos)  // get index for a vector NxF64
-  {
-    NxU32 ret;
-
-    if ( !mUseDouble )
-    {
-      NxF32 p[3];
-      p[0] = (NxF32)_p[0];
-      p[1] = (NxF32)_p[1];
-      p[2] = (NxF32)_p[2];
-      return getIndex(p,newPos);
-    }
-
-    newPos = false;
-
-    NxF64 p[3];
-
-    if ( mSnapToGrid )
-    {
-      p[0] = snapToGrid(_p[0]);
-      p[1] = snapToGrid(_p[1]);
-      p[2] = snapToGrid(_p[2]);
-    }
-    else
-    {
-      p[0] = _p[0];
-      p[1] = _p[1];
-      p[2] = _p[2];
-    }
-
-    bool found;
-    ret = mKdTree.getNearest(p,mDoubleGranularity,found);
-    if ( !found )
-    {
-      newPos = true;
-      ret = mKdTree.add(p[0],p[1],p[2]);
-    }
-
-
-    return ret;
-  }
-
-  const NxF32 *   getVerticesFloat(void) const
-  {
-    const NxF32 * ret = 0;
-
-    assert( !mUseDouble );
-
-    ret = mKdTree.getVerticesFloat();
-
-    return ret;
-  }
-
-  const NxF64 *  getVerticesDouble(void) const
-  {
-    const NxF64 * ret = 0;
-
-    assert( mUseDouble );
-
-    ret = mKdTree.getVerticesDouble();
-
-    return ret;
-  }
-
-  const NxF32 *   getVertexFloat(NxU32 index) const
-  {
-    const NxF32 * ret  = 0;
-    assert( !mUseDouble );
-#ifdef _DEBUG
-    NxU32 vcount = mKdTree.getVcount();
-    assert( index < vcount );
-#endif
-    ret =  mKdTree.getVerticesFloat();
-    ret = &ret[index*3];
-    return ret;
-  }
-
-  const NxF64 *   getVertexDouble(NxU32 index) const
-  {
-    const NxF64 * ret = 0;
-    assert( mUseDouble );
-#ifdef _DEBUG
-    NxU32 vcount = mKdTree.getVcount();
-    assert( index < vcount );
-#endif
-    ret =  mKdTree.getVerticesDouble();
-    ret = &ret[index*3];
-
-    return ret;
-  }
-
-  NxU32    getVcount(void) const
-  {
-    return mKdTree.getVcount();
-  }
-
-  bool isDouble(void) const
-  {
-    return mUseDouble;
-  }
-
-
-  bool            saveAsObj(const char *fname,NxU32 tcount,NxU32 *indices)
-  {
-    bool ret = false;
-
-
-    FILE *fph = fopen(fname,"wb");
-    if ( fph )
-    {
-      ret = true;
-
-      NxU32 vcount    = getVcount();
-      if ( mUseDouble )
-      {
-        const NxF64 *v  = getVerticesDouble();
-        for (NxU32 i=0; i<vcount; i++)
-        {
-          fprintf(fph,"v %0.9f %0.9f %0.9f\r\n", (NxF32)v[0], (NxF32)v[1], (NxF32)v[2] );
-          v+=3;
-        }
-      }
-      else
-      {
-        const NxF32 *v  = getVerticesFloat();
-        for (NxU32 i=0; i<vcount; i++)
-        {
-          fprintf(fph,"v %0.9f %0.9f %0.9f\r\n", v[0], v[1], v[2] );
-          v+=3;
-        }
-      }
-
-      for (NxU32 i=0; i<tcount; i++)
-      {
-        NxU32 i1 = *indices++;
-        NxU32 i2 = *indices++;
-        NxU32 i3 = *indices++;
-        fprintf(fph,"f %d %d %d\r\n", i1+1, i2+1, i3+1 );
-      }
-      fclose(fph);
-    }
-
-    return ret;
-  }
-
-private:
-  bool    mUseDouble:1;
-  bool    mSnapToGrid:1;
-  NxF64  mDoubleGranularity;
-  NxF32   mFloatGranularity;
-  VERTEX_INDEX::KdTree  mKdTree;
-};
-
-fm_VertexIndex * fm_createVertexIndex(NxF64 granularity,bool snapToGrid) // create an indexed vertex system for doubles
-{
-  MyVertexIndex *ret = MEMALLOC_NEW(MyVertexIndex)(granularity,snapToGrid);
-  return static_cast< fm_VertexIndex *>(ret);
-}
-
-fm_VertexIndex * fm_createVertexIndex(NxF32 granularity,bool snapToGrid)  // create an indexed vertext system for floats
-{
-  MyVertexIndex *ret = MEMALLOC_NEW(MyVertexIndex)(granularity,snapToGrid);
-  return static_cast< fm_VertexIndex *>(ret);
-}
-
-void          fm_releaseVertexIndex(fm_VertexIndex *vindex)
-{
-  MyVertexIndex *m = static_cast< MyVertexIndex *>(vindex);
-  delete m;
-}
-
-#endif   // END OF VERTEX WELDING CODE
-
-
-//**********************************************************
-//**********************************************************
-//**** LineSweep Line-Segment Intersection Code
-//**********************************************************
-//**********************************************************
-
-//#ifndef LINE_SWEEP_H
-#if 0
-
-#define LINE_SWEEP_H
-
-class fm_quickSort
-{
-public:
-	void qsort(void **base,NxI32 num); // perform the qsort.
-protected:
-  // -1 less, 0 equal, +1 greater.
-	virtual NxI32 compare(void **p1,void **p2) = 0;
-private:
-	void inline swap(char **a,char **b);
-};
-
-
-void fm_quickSort::swap(char **a,char **b)
-{
-	char *tmp;
-
-	if ( a != b )
-	{
-		tmp = *a;
-		*a++ = *b;
-		*b++ = tmp;
-	}
-}
-
-
-void fm_quickSort::qsort(void **b,NxI32 num)
-{
-	char *lo,*hi;
-	char *mid;
-	char *bottom, *top;
-	NxI32 size;
-	char *lostk[30], *histk[30];
-	NxI32 stkptr;
-	char **base = (char **)b;
-
-	if (num < 2 ) return;
-
-	stkptr = 0;
-
-	lo = (char *)base;
-	hi = (char *)base + sizeof(char **) * (num-1);
-
-nextone:
-
-	size = (NxI32)(hi - lo) / sizeof(char**) + 1;
-
-	mid = lo + (size / 2) * sizeof(char **);
-	swap((char **)mid,(char **)lo);
-	bottom = lo;
-	top = hi + sizeof(char **);
-
-	for (;;)
-	{
-		do
-		{
-			bottom += sizeof(char **);
-		} while (bottom <= hi && compare((void **)bottom,(void **)lo) <= 0);
-
-		do
-		{
-			top -= sizeof(char **);
-		} while (top > lo && compare((void **)top,(void **)lo) >= 0);
-
-		if (top < bottom) break;
-
-		swap((char **)bottom,(char **)top);
-
-	}
-
-	swap((char **)lo,(char **)top);
-
-	if ( top - 1 - lo >= hi - bottom )
-	{
-		if (lo + sizeof(char **) < top)
-		{
-			lostk[stkptr] = lo;
-			histk[stkptr] = top - sizeof(char **);
-			stkptr++;
-		}
-		if (bottom < hi)
-		{
-			lo = bottom;
-			goto nextone;
-		}
-	}
-	else
-	{
-		if ( bottom < hi )
-		{
-			lostk[stkptr] = bottom;
-			histk[stkptr] = hi;
-			stkptr++;
-		}
-		if (lo + sizeof(char **) < top)
-		{
-			hi = top - sizeof(char **);
-			goto nextone; 					/* do small recursion */
-		}
-	}
-
-	stkptr--;
-
-	if (stkptr >= 0)
-	{
-		lo = lostk[stkptr];
-		hi = histk[stkptr];
-		goto nextone;
-	}
-	return;
-}
-
-
-typedef CONVEX_DECOMPOSITION::Array< fm_LineSegment > LineSegmentVector;
-
-static inline void setMinMax(NxF64 &vmin,NxF64 &vmax,NxF64 v1,NxF64 v2)
-{
-  if ( v1 <= v2 )
-  {
-    vmin = v1;
-    vmax = v2;
-  }
-  else
-  {
-    vmin = v2;
-    vmax = v1;
-  }
-}
-
-
-class Intersection
-{
-public:
-  Intersection(void)
-  {
-    mIndex = 0;
-    mTime = 0;
-  }
-  Intersection(NxF64 time,const NxF64 *from,const NxF64 *to,fm_VertexIndex *vpool)
-  {
-    mTime = time;
-    NxF64 pos[3];
-    pos[0] = (to[0]-from[0])*time+from[0];
-    pos[1] = (to[1]-from[1])*time+from[1];
-    pos[2] = (to[2]-from[2])*time+from[2];
-    bool newPos;
-    mIndex = vpool->getIndex(pos,newPos);
-  }
-
-  NxU32    mIndex;
-  NxF64    mTime;
-};
-
-
-typedef CONVEX_DECOMPOSITION::Array< Intersection > IntersectionList;
-
-class MyLineSegment : public fm_LineSegment, public Memalloc
-{
-public:
-
-  void init(const fm_LineSegment &s,fm_VertexIndex *vpool,NxU32 x)
-  {
-    fm_LineSegment *dest = static_cast< fm_LineSegment *>(this);
-    *dest = s;
-
-    mFlipped = false;
-
-    const NxF64 *p1 = vpool->getVertexDouble(mE1);
-    const NxF64 *p2 = vpool->getVertexDouble(mE2);
-
-    setMinMax(mMin[0],mMax[0],p1[0],p2[0]);
-    setMinMax(mMin[1],mMax[1],p1[1],p2[1]);
-    setMinMax(mMin[2],mMax[2],p1[2],p2[2]);
-
-    if ( p1[x] <= p2[x] )
-    {
-      mFrom[0] = p1[0];
-      mFrom[1] = p1[1];
-      mFrom[2] = p1[2];
-
-      mTo[0]   = p2[0];
-      mTo[1]   = p2[1];
-      mTo[2]   = p2[2];
-    }
-    else
-    {
-      mFrom[0] = p2[0];
-      mFrom[1] = p2[1];
-      mFrom[2] = p2[2];
-
-      mTo[0]   = p1[0];
-      mTo[1]   = p1[1];
-      mTo[2]   = p1[2];
-
-      mFlipped = true;
-
-      swap(mE1,mE2);
-    }
-
-  }
-
-  // we already know that the x-extent overlaps or we wouldn't be in this routine..
-  void intersect(MyLineSegment *segment,NxU32 x,NxU32 y,NxU32 /* z */,fm_VertexIndex *vpool)
-  {
-    NxU32 count = 0;
-
-    // if the two segments share any start/end points then they cannot intersect at all!
-
-    if ( segment->mE1 == mE1 || segment->mE1 == mE2 ) count++;
-    if ( segment->mE2 == mE1 || segment->mE2 == mE2 ) count++;
-
-    if ( count == 0 )
-    {
-      if ( mMax[y] < segment->mMin[y] ) // no intersection...
-      {
-
-      }
-      else if ( mMin[y] > segment->mMax[y] ) // no intersection
-      {
-
-      }
-      else
-      {
-
-        NxF64 a1[2];
-        NxF64 a2[2];
-        NxF64 b1[2];
-        NxF64 b2[2];
-
-        a1[0] = mFrom[x];
-        a1[1] = mFrom[y];
-
-        a2[0] = mTo[x];
-        a2[1] = mTo[y];
-
-        b1[0] = segment->mFrom[x];
-        b1[1] = segment->mFrom[y];
-
-        b2[0] = segment->mTo[x];
-        b2[1] = segment->mTo[y];
-
-
-        NxF64 t1,t2;
-        IntersectResult result = fm_intersectLineSegments2dTime(a1,a2,b1,b2,t1,t2);
-
-        if ( result == IR_DO_INTERSECT )
-        {
-          addIntersect(t1,vpool);
-          segment->addIntersect(t2,vpool);
-        }
-
-
-      }
-    }
-  }
-
-  void addIntersect(NxF64 time,fm_VertexIndex *vpool)
-  {
-    Intersection intersect(time,mFrom,mTo,vpool);
-
-    if ( mE1 == intersect.mIndex || mE2 == intersect.mIndex )
-    {
-      //printf("Split too close to the beginning or the end of the line segment.\r\n");
-    }
-    else
-    {
-      if ( mIntersections.empty() )
-      {
-        mIntersections.pushBack(intersect);
-      }
-      else
-      {
-        IntersectionList::Iterator i;
-        for (i=mIntersections.begin(); i!=mIntersections.end(); ++i)
-        {
-          Intersection &it = (*i);
-          if ( it.mIndex == intersect.mIndex )
-          {
-            //printf("Duplicate Intersection, throwing it away.\r\n");
-            break;
-          }
-          else
-          {
-            if ( it.mTime > time )
-            {
-//*** TODO TODO TODO              mIntersections.insert(i,intersect);
-              break;
-            }
-          }
-        }
-        if ( i==mIntersections.end() )
-        {
-          mIntersections.pushBack(intersect);
-        }
-      }
-    }
-  }
-
-  void getResults(LineSegmentVector &results)
-  {
-    if ( mIntersections.empty() )
-    {
-      fm_LineSegment seg(mE1,mE2);
-      if ( mFlipped )
-      {
-        swap(seg.mE1,seg.mE2);
-      }
-      results.pushBack(seg);
-    }
-    else
-    {
-      NxU32 prev = mE1;
-      IntersectionList::Iterator i;
-      for (i=mIntersections.begin(); i!=mIntersections.end(); ++i)
-      {
-        Intersection &it = (*i);
-        fm_LineSegment seg(prev,it.mIndex);
-        if ( mFlipped )
-        {
-          swap(seg.mE1,seg.mE2);
-        }
-        results.pushBack(seg);
-        prev = it.mIndex;
-      }
-      fm_LineSegment seg(prev,mE2);
-      if ( mFlipped )
-      {
-        swap(seg.mE1,seg.mE2);
-      }
-      results.pushBack(seg);
-    }
-  }
-
-  void swap(NxU32 &a,NxU32 &b)
-  {
-    NxU32 temp = a;
-    a = b;
-    b = temp;
-  }
-
-  bool             mFlipped;
-  NxF64            mFrom[3];
-  NxF64            mTo[3];
-  NxF64            mMin[3];
-  NxF64            mMax[3];
-  IntersectionList mIntersections;
-};
-
-typedef CONVEX_DECOMPOSITION::Array< MyLineSegment > MyLineSegmentVector;
-
-class MyLineSweep : public fm_LineSweep, public fm_quickSort, public Memalloc
-{
-public:
-  virtual ~MyLineSweep(void)
-  {
-
-  }
-  fm_LineSegment * performLineSweep(const fm_LineSegment *segments,NxU32 icount,const NxF64 *planeEquation,fm_VertexIndex *pool,NxU32 &scount)
-  {
-    fm_LineSegment *ret = 0;
-
-    FM_Axis axis = fm_getDominantAxis(planeEquation);
-    switch ( axis )
-    {
-      case FM_XAXIS:
-        mX = 1;
-        mY = 2;
-        mZ = 0;
-        break;
-      case FM_YAXIS:
-        mX = 0;
-        mY = 2;
-        mZ = 1;
-        break;
-      case FM_ZAXIS:
-        mX = 0;
-        mY = 1;
-        mZ = 2;
-        break;
-    }
-
-
-    mResults.clear();
-    scount = 0;
-
-    MyLineSegment *mls   = MEMALLOC_NEW(MyLineSegment)[icount];
-    MyLineSegment **mptr = (MyLineSegment **)MEMALLOC_MALLOC(sizeof(MyLineSegment *)*icount);
-
-    for (NxU32 i=0; i<icount; i++)
-    {
-      mls[i].init(segments[i],pool,mX);
-      mptr[i] = &mls[i];
-    }
-
-    qsort((void **)mptr,(NxI32)icount);
-
-    for (NxU32 i=0; i<icount; i++)
-    {
-      MyLineSegment *segment = mptr[i];
-      NxF64 esegment = segment->mTo[mX];
-      for (NxU32 j=i+1; j<icount; j++)
-      {
-        MyLineSegment *test = mptr[j];
-        if ( test->mFrom[mX] >= esegment )
-        {
-          break;
-        }
-        else
-        {
-          test->intersect(segment,mX,mY,mZ,pool);
-        }
-      }
-    }
-
-    for (NxU32 i=0; i<icount; i++)
-    {
-      MyLineSegment *segment = mptr[i];
-      segment->getResults(mResults);
-    }
-
-
-    delete []mls;
-    MEMALLOC_FREE(mptr);
-
-    if ( !mResults.empty() )
-    {
-      scount = (NxU32)mResults.size();
-      ret = &mResults[0];
-    }
-
-    return ret;
-  }
-
-	NxI32 compare(void **p1,void **p2)
-  {
-    NxI32 ret = 0;
-
-    MyLineSegment **m1 = (MyLineSegment **) p1;
-    MyLineSegment **m2 = (MyLineSegment **) p2;
-
-    MyLineSegment *s1 = *m1;
-    MyLineSegment *s2 = *m2;
-
-    if ( s1->mFrom[mX] < s2->mFrom[mX] )
-      ret = -1;
-    else if ( s1->mFrom[mX] > s2->mFrom[mX] )
-      ret = 1;
-    else if ( s1->mFrom[mY] < s2->mFrom[mY] )
-      ret = -1;
-    else if ( s1->mFrom[mY] > s2->mFrom[mY] )
-      ret = 1;
-
-    return ret;
-  }
-
-  NxU32              mX;  // index for the x-axis
-  NxU32              mY;  // index for the y-axis
-  NxU32              mZ;
-  fm_VertexIndex        *mfm_VertexIndex;
-  LineSegmentVector  mResults;
-};
-
-
-fm_LineSweep * fm_createLineSweep(void)
-{
-  MyLineSweep *mls = MEMALLOC_NEW(MyLineSweep);
-  return static_cast< fm_LineSweep *>(mls);
-}
-
-void        fm_releaseLineSweep(fm_LineSweep *sweep)
-{
-  MyLineSweep *mls = static_cast< MyLineSweep *>(sweep);
-  delete mls;
-}
-
-
-
-#endif  // End of LineSweep code
-
-
-
-
-REAL fm_computeBestFitAABB(NxU32 vcount,const REAL *points,NxU32 pstride,REAL *bmin,REAL *bmax) // returns the diagonal distance
-{
-
-  const NxU8 *source = (const NxU8 *) points;
-
-	bmin[0] = points[0];
-	bmin[1] = points[1];
-	bmin[2] = points[2];
-
-	bmax[0] = points[0];
-	bmax[1] = points[1];
-	bmax[2] = points[2];
-
-
-  for (NxU32 i=1; i<vcount; i++)
-  {
-  	source+=pstride;
-  	const REAL *p = (const REAL *) source;
-
-  	if ( p[0] < bmin[0] ) bmin[0] = p[0];
-  	if ( p[1] < bmin[1] ) bmin[1] = p[1];
-  	if ( p[2] < bmin[2] ) bmin[2] = p[2];
-
-		if ( p[0] > bmax[0] ) bmax[0] = p[0];
-		if ( p[1] > bmax[1] ) bmax[1] = p[1];
-		if ( p[2] > bmax[2] ) bmax[2] = p[2];
-
-  }
-
-  REAL dx = bmax[0] - bmin[0];
-  REAL dy = bmax[1] - bmin[1];
-  REAL dz = bmax[2] - bmin[2];
-
-	return (REAL) sqrt( dx*dx + dy*dy + dz*dz );
-
-}
-
-
-
-/* a = b - c */
-#define vector(a,b,c) \
-	(a)[0] = (b)[0] - (c)[0];	\
-	(a)[1] = (b)[1] - (c)[1];	\
-	(a)[2] = (b)[2] - (c)[2];
-
-
-
-#define innerProduct(v,q) \
-		((v)[0] * (q)[0] + \
-		(v)[1] * (q)[1] + \
-		(v)[2] * (q)[2])
-
-#define crossProduct(a,b,c) \
-	(a)[0] = (b)[1] * (c)[2] - (c)[1] * (b)[2]; \
-	(a)[1] = (b)[2] * (c)[0] - (c)[2] * (b)[0]; \
-	(a)[2] = (b)[0] * (c)[1] - (c)[0] * (b)[1];
-
-
-bool fm_lineIntersectsTriangle(const REAL *rayStart,const REAL *rayEnd,const REAL *p1,const REAL *p2,const REAL *p3,REAL *sect)
-{
-	REAL dir[3];
-
-  dir[0] = rayEnd[0] - rayStart[0];
-  dir[1] = rayEnd[1] - rayStart[1];
-  dir[2] = rayEnd[2] - rayStart[2];
-
-  REAL d = (REAL)sqrt(dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2]);
-  REAL r = 1.0f / d;
-
-  dir[0]*=r;
-  dir[1]*=r;
-  dir[2]*=r;
-
-
-  REAL t;
-
-	bool ret = fm_rayIntersectsTriangle(rayStart, dir, p1, p2, p3, t );
-
-	if ( ret )
-	{
-		if ( t > d )
-		{
-			sect[0] = rayStart[0] + dir[0]*t;
-			sect[1] = rayStart[1] + dir[1]*t;
-			sect[2] = rayStart[2] + dir[2]*t;
-		}
-		else
-		{
-			ret = false;
-		}
-	}
-
-  return ret;
-}
-
-
-
-bool fm_rayIntersectsTriangle(const REAL *p,const REAL *d,const REAL *v0,const REAL *v1,const REAL *v2,REAL &t)
-{
-	REAL e1[3],e2[3],h[3],s[3],q[3];
-	REAL a,f,u,v;
-
-	vector(e1,v1,v0);
-	vector(e2,v2,v0);
-	crossProduct(h,d,e2);
-	a = innerProduct(e1,h);
-
-	if (a > -0.00001 && a < 0.00001)
-		return(false);
-
-	f = 1/a;
-	vector(s,p,v0);
-	u = f * (innerProduct(s,h));
-
-	if (u < 0.0 || u > 1.0)
-		return(false);
-
-	crossProduct(q,s,e1);
-	v = f * innerProduct(d,q);
-	if (v < 0.0 || u + v > 1.0)
-		return(false);
-	// at this stage we can compute t to find out where
-	// the intersection point is on the line
-	t = f * innerProduct(e2,q);
-	if (t > 0) // ray intersection
-		return(true);
-	else // this means that there is a line intersection
-		 // but not a ray intersection
-		 return (false);
-}
-
-
-inline REAL det(const REAL *p1,const REAL *p2,const REAL *p3)
-{
-  return  p1[0]*p2[1]*p3[2] + p2[0]*p3[1]*p1[2] + p3[0]*p1[1]*p2[2] -p1[0]*p3[1]*p2[2] - p2[0]*p1[1]*p3[2] - p3[0]*p2[1]*p1[2];
-}
-
-
-REAL  fm_computeMeshVolume(const REAL *vertices,NxU32 tcount,const NxU32 *indices)
-{
-	REAL volume = 0;
-
-	for (NxU32 i=0; i<tcount; i++,indices+=3)
-	{
-  	const REAL *p1 = &vertices[ indices[0]*3 ];
-		const REAL *p2 = &vertices[ indices[1]*3 ];
-		const REAL *p3 = &vertices[ indices[2]*3 ];
-		volume+=det(p1,p2,p3); // compute the volume of the tetrahedran relative to the origin.
-	}
-
-	volume*=(1.0f/6.0f);
-	if ( volume < 0 )
-		volume*=-1;
-	return volume;
-}
-
-
-const REAL * fm_getPoint(const REAL *points,NxU32 pstride,NxU32 index)
-{
-  const NxU8 *scan = (const NxU8 *)points;
-  scan+=(index*pstride);
-  return (REAL *)scan;
-}
-
-
-bool fm_insideTriangle(REAL Ax, REAL Ay,
-                      REAL Bx, REAL By,
-                      REAL Cx, REAL Cy,
-                      REAL Px, REAL Py)
-
-{
-  REAL ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy;
-  REAL cCROSSap, bCROSScp, aCROSSbp;
-
-  ax = Cx - Bx;  ay = Cy - By;
-  bx = Ax - Cx;  by = Ay - Cy;
-  cx = Bx - Ax;  cy = By - Ay;
-  apx= Px - Ax;  apy= Py - Ay;
-  bpx= Px - Bx;  bpy= Py - By;
-  cpx= Px - Cx;  cpy= Py - Cy;
-
-  aCROSSbp = ax*bpy - ay*bpx;
-  cCROSSap = cx*apy - cy*apx;
-  bCROSScp = bx*cpy - by*cpx;
-
-  return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f));
-}
-
-
-REAL fm_areaPolygon2d(NxU32 pcount,const REAL *points,NxU32 pstride)
-{
-  NxI32 n = (NxI32)pcount;
-
-  REAL A=0.0f;
-  for(NxI32 p=n-1,q=0; q<n; p=q++)
-  {
-    const REAL *p1 = fm_getPoint(points,pstride,p);
-    const REAL *p2 = fm_getPoint(points,pstride,q);
-    A+= p1[0]*p2[1] - p2[0]*p1[1];
-  }
-  return A*0.5f;
-}
-
-
-bool  fm_pointInsidePolygon2d(NxU32 pcount,const REAL *points,NxU32 pstride,const REAL *point,NxU32 xindex,NxU32 yindex)
-{
-  NxU32 j = pcount-1;
-  NxI32 oddNodes = 0;
-
-  REAL x = point[xindex];
-  REAL y = point[yindex];
-
-  for (NxU32 i=0; i<pcount; i++)
-  {
-    const REAL *p1 = fm_getPoint(points,pstride,i);
-    const REAL *p2 = fm_getPoint(points,pstride,j);
-
-    REAL x1 = p1[xindex];
-    REAL y1 = p1[yindex];
-
-    REAL x2 = p2[xindex];
-    REAL y2 = p2[yindex];
-
-    if ( y1 < y && y2 >= y ||  y2 < y && y1 >= y )
-    {
-      if (x1+(y-y1)/(y2-y1)*(x2-x1)<x)
-      {
-        oddNodes = 1-oddNodes;
-      }
-    }
-    j = i;
-  }
-
-  return oddNodes ? true : false;
-}
-
-
-NxU32 fm_consolidatePolygon(NxU32 pcount,const REAL *points,NxU32 pstride,REAL *_dest,REAL epsilon) // collapses co-linear edges.
-{
-  NxU32 ret = 0;
-
-
-  if ( pcount >= 3 )
-  {
-    const REAL *prev = fm_getPoint(points,pstride,pcount-1);
-    const REAL *current = points;
-    const REAL *next    = fm_getPoint(points,pstride,1);
-    REAL *dest = _dest;
-
-    for (NxU32 i=0; i<pcount; i++)
-    {
-
-      next = (i+1)==pcount ? points : next;
-
-      if ( !fm_colinear(prev,current,next,epsilon) )
-      {
-        dest[0] = current[0];
-        dest[1] = current[1];
-        dest[2] = current[2];
-
-        dest+=3;
-        ret++;
-      }
-
-      prev = current;
-      current+=3;
-      next+=3;
-
-    }
-  }
-
-  return ret;
-}
-
-
-#ifndef RECT3D_TEMPLATE
-
-#define RECT3D_TEMPLATE
-
-template <class T> class Rect3d
-{
-public:
-  Rect3d(void) { };
-
-  Rect3d(const T *bmin,const T *bmax)
-  {
-
-    mMin[0] = bmin[0];
-    mMin[1] = bmin[1];
-    mMin[2] = bmin[2];
-
-    mMax[0] = bmax[0];
-    mMax[1] = bmax[1];
-    mMax[2] = bmax[2];
-
-  }
-
-  void SetMin(const T *bmin)
-  {
-    mMin[0] = bmin[0];
-    mMin[1] = bmin[1];
-    mMin[2] = bmin[2];
-  }
-
-  void SetMax(const T *bmax)
-  {
-    mMax[0] = bmax[0];
-    mMax[1] = bmax[1];
-    mMax[2] = bmax[2];
-  }
-
-	void SetMin(T x,T y,T z)
-	{
-		mMin[0] = x;
-		mMin[1] = y;
-		mMin[2] = z;
-	}
-
-	void SetMax(T x,T y,T z)
-	{
-		mMax[0] = x;
-		mMax[1] = y;
-		mMax[2] = z;
-	}
-
-  T mMin[3];
-  T mMax[3];
-};
-
-#endif
-
-void splitRect(NxU32 axis,
-						   const Rect3d<REAL> &source,
-							 Rect3d<REAL> &b1,
-							 Rect3d<REAL> &b2,
-							 const REAL *midpoint)
-{
-	switch ( axis )
-	{
-		case 0:
-			b1.SetMin(source.mMin);
-			b1.SetMax( midpoint[0], source.mMax[1], source.mMax[2] );
-
-			b2.SetMin( midpoint[0], source.mMin[1], source.mMin[2] );
-			b2.SetMax(source.mMax);
-
-			break;
-		case 1:
-			b1.SetMin(source.mMin);
-			b1.SetMax( source.mMax[0], midpoint[1], source.mMax[2] );
-
-			b2.SetMin( source.mMin[0], midpoint[1], source.mMin[2] );
-			b2.SetMax(source.mMax);
-
-			break;
-		case 2:
-			b1.SetMin(source.mMin);
-			b1.SetMax( source.mMax[0], source.mMax[1], midpoint[2] );
-
-			b2.SetMin( source.mMin[0], source.mMin[1], midpoint[2] );
-			b2.SetMax(source.mMax);
-
-			break;
-	}
-}
-
-bool fm_computeSplitPlane(NxU32 vcount,
-                          const REAL *vertices,
-                          NxU32 /* tcount */,
-                          const NxU32 * /* indices */,
-                          REAL *plane)
-{
-
-  REAL sides[3];
-  REAL matrix[16];
-
-  fm_computeBestFitOBB( vcount, vertices, sizeof(REAL)*3, sides, matrix );
-
-  REAL bmax[3];
-  REAL bmin[3];
-
-  bmax[0] = sides[0]*0.5f;
-  bmax[1] = sides[1]*0.5f;
-  bmax[2] = sides[2]*0.5f;
-
-  bmin[0] = -bmax[0];
-  bmin[1] = -bmax[1];
-  bmin[2] = -bmax[2];
-
-
-  REAL dx = sides[0];
-  REAL dy = sides[1];
-  REAL dz = sides[2];
-
-
-	REAL laxis = dx;
-
-	NxU32 axis = 0;
-
-	if ( dy > dx )
-	{
-		axis = 1;
-		laxis = dy;
-	}
-
-	if ( dz > dx && dz > dy )
-	{
-		axis = 2;
-		laxis = dz;
-	}
-
-  REAL p1[3];
-  REAL p2[3];
-  REAL p3[3];
-
-  p3[0] = p2[0] = p1[0] = bmin[0] + dx*0.5f;
-  p3[1] = p2[1] = p1[1] = bmin[1] + dy*0.5f;
-  p3[2] = p2[2] = p1[2] = bmin[2] + dz*0.5f;
-
-  Rect3d<REAL> b(bmin,bmax);
-
-  Rect3d<REAL> b1,b2;
-
-  splitRect(axis,b,b1,b2,p1);
-
-
-  switch ( axis )
-  {
-    case 0:
-      p2[1] = bmin[1];
-      p2[2] = bmin[2];
-
-      if ( dz > dy )
-      {
-        p3[1] = bmax[1];
-        p3[2] = bmin[2];
-      }
-      else
-      {
-        p3[1] = bmin[1];
-        p3[2] = bmax[2];
-      }
-
-      break;
-    case 1:
-      p2[0] = bmin[0];
-      p2[2] = bmin[2];
-
-      if ( dx > dz )
-      {
-        p3[0] = bmax[0];
-        p3[2] = bmin[2];
-      }
-      else
-      {
-        p3[0] = bmin[0];
-        p3[2] = bmax[2];
-      }
-
-      break;
-    case 2:
-      p2[0] = bmin[0];
-      p2[1] = bmin[1];
-
-      if ( dx > dy )
-      {
-        p3[0] = bmax[0];
-        p3[1] = bmin[1];
-      }
-      else
-      {
-        p3[0] = bmin[0];
-        p3[1] = bmax[1];
-      }
-
-      break;
-  }
-
-  REAL tp1[3];
-  REAL tp2[3];
-  REAL tp3[3];
-
-  fm_transform(matrix,p1,tp1);
-  fm_transform(matrix,p2,tp2);
-  fm_transform(matrix,p3,tp3);
-
-	plane[3] = fm_computePlane(tp1,tp2,tp3,plane);
-
-  return true;
-
-}
-
-#pragma warning(disable:4100)
-
-void fm_nearestPointInTriangle(const REAL *nearestPoint,const REAL *p1,const REAL *p2,const REAL *p3,REAL *nearest)
-{
-
-}
-
-static REAL Partial(const REAL *a,const REAL *p) 
-{
-	return (a[0]*p[1]) - (p[0]*a[1]);
-}
-
-REAL  fm_areaTriangle(const REAL *p0,const REAL *p1,const REAL *p2)
-{
-  REAL A = Partial(p0,p1);
-	A+= Partial(p1,p2);
-	A+= Partial(p2,p0);
-	return A*0.5f;
-}
-
-void fm_subtract(const REAL *A,const REAL *B,REAL *diff) // compute A-B and store the result in 'diff'
-{
-  diff[0] = A[0]-B[0];
-  diff[1] = A[1]-B[1];
-  diff[2] = A[2]-B[2];
-}
-
-
-void  fm_multiplyTransform(const REAL *pA,const REAL *pB,REAL *pM)
-{
-
-  REAL a = pA[0*4+0] * pB[0*4+0] + pA[0*4+1] * pB[1*4+0] + pA[0*4+2] * pB[2*4+0] + pA[0*4+3] * pB[3*4+0];
-  REAL b = pA[0*4+0] * pB[0*4+1] + pA[0*4+1] * pB[1*4+1] + pA[0*4+2] * pB[2*4+1] + pA[0*4+3] * pB[3*4+1];
-  REAL c = pA[0*4+0] * pB[0*4+2] + pA[0*4+1] * pB[1*4+2] + pA[0*4+2] * pB[2*4+2] + pA[0*4+3] * pB[3*4+2];
-  REAL d = pA[0*4+0] * pB[0*4+3] + pA[0*4+1] * pB[1*4+3] + pA[0*4+2] * pB[2*4+3] + pA[0*4+3] * pB[3*4+3];
-
-  REAL e = pA[1*4+0] * pB[0*4+0] + pA[1*4+1] * pB[1*4+0] + pA[1*4+2] * pB[2*4+0] + pA[1*4+3] * pB[3*4+0];
-  REAL f = pA[1*4+0] * pB[0*4+1] + pA[1*4+1] * pB[1*4+1] + pA[1*4+2] * pB[2*4+1] + pA[1*4+3] * pB[3*4+1];
-  REAL g = pA[1*4+0] * pB[0*4+2] + pA[1*4+1] * pB[1*4+2] + pA[1*4+2] * pB[2*4+2] + pA[1*4+3] * pB[3*4+2];
-  REAL h = pA[1*4+0] * pB[0*4+3] + pA[1*4+1] * pB[1*4+3] + pA[1*4+2] * pB[2*4+3] + pA[1*4+3] * pB[3*4+3];
-
-  REAL i = pA[2*4+0] * pB[0*4+0] + pA[2*4+1] * pB[1*4+0] + pA[2*4+2] * pB[2*4+0] + pA[2*4+3] * pB[3*4+0];
-  REAL j = pA[2*4+0] * pB[0*4+1] + pA[2*4+1] * pB[1*4+1] + pA[2*4+2] * pB[2*4+1] + pA[2*4+3] * pB[3*4+1];
-  REAL k = pA[2*4+0] * pB[0*4+2] + pA[2*4+1] * pB[1*4+2] + pA[2*4+2] * pB[2*4+2] + pA[2*4+3] * pB[3*4+2];
-  REAL l = pA[2*4+0] * pB[0*4+3] + pA[2*4+1] * pB[1*4+3] + pA[2*4+2] * pB[2*4+3] + pA[2*4+3] * pB[3*4+3];
-
-  REAL m = pA[3*4+0] * pB[0*4+0] + pA[3*4+1] * pB[1*4+0] + pA[3*4+2] * pB[2*4+0] + pA[3*4+3] * pB[3*4+0];
-  REAL n = pA[3*4+0] * pB[0*4+1] + pA[3*4+1] * pB[1*4+1] + pA[3*4+2] * pB[2*4+1] + pA[3*4+3] * pB[3*4+1];
-  REAL o = pA[3*4+0] * pB[0*4+2] + pA[3*4+1] * pB[1*4+2] + pA[3*4+2] * pB[2*4+2] + pA[3*4+3] * pB[3*4+2];
-  REAL p = pA[3*4+0] * pB[0*4+3] + pA[3*4+1] * pB[1*4+3] + pA[3*4+2] * pB[2*4+3] + pA[3*4+3] * pB[3*4+3];
-
-  pM[0] = a;  pM[1] = b;  pM[2] = c;  pM[3] = d;
-
-  pM[4] = e;  pM[5] = f;  pM[6] = g;  pM[7] = h;
-
-  pM[8] = i;  pM[9] = j;  pM[10] = k;  pM[11] = l;
-
-  pM[12] = m;  pM[13] = n;  pM[14] = o;  pM[15] = p;
-}
-
-void fm_multiply(REAL *A,REAL scaler)
-{
-  A[0]*=scaler;
-  A[1]*=scaler;
-  A[2]*=scaler;
-}
-
-void fm_add(const REAL *A,const REAL *B,REAL *sum)
-{
-  sum[0] = A[0]+B[0];
-  sum[1] = A[1]+B[1];
-  sum[2] = A[2]+B[2];
-}
-
-void fm_copy3(const REAL *source,REAL *dest)
-{
-  dest[0] = source[0];
-  dest[1] = source[1];
-  dest[2] = source[2];
-}
-
-
-NxU32  fm_copyUniqueVertices(NxU32 vcount,const REAL *input_vertices,REAL *output_vertices,NxU32 tcount,const NxU32 *input_indices,NxU32 *output_indices)
-{
-  NxU32 ret = 0;
-
-  REAL *vertices = (REAL *)MEMALLOC_MALLOC(sizeof(REAL)*vcount*3);
-  memcpy(vertices,input_vertices,sizeof(REAL)*vcount*3);
-  REAL *dest = output_vertices;
-
-  NxU32 *reindex = (NxU32 *)MEMALLOC_MALLOC(sizeof(NxU32)*vcount);
-  memset(reindex,0xFF,sizeof(NxU32)*vcount);
-
-  NxU32 icount = tcount*3;
-
-  for (NxU32 i=0; i<icount; i++)
-  {
-    NxU32 index = *input_indices++;
-
-    assert( index < vcount );
-
-    if ( reindex[index] == 0xFFFFFFFF )
-    {
-      *output_indices++ = ret;
-      reindex[index] = ret;
-      const REAL *pos = &vertices[index*3];
-      dest[0] = pos[0];
-      dest[1] = pos[1];
-      dest[2] = pos[2];
-      dest+=3;
-      ret++;
-    }
-    else
-    {
-      *output_indices++ = reindex[index];
-    }
-  }
-  MEMALLOC_FREE(vertices);
-  MEMALLOC_FREE(reindex);
-  return ret;
-}
-
-bool    fm_isMeshCoplanar(NxU32 tcount,const NxU32 *indices,const REAL *vertices,bool doubleSided) // returns true if this collection of indexed triangles are co-planar!
-{
-  bool ret = true;
-
-  if ( tcount > 0 )
-  {
-    NxU32 i1 = indices[0];
-    NxU32 i2 = indices[1];
-    NxU32 i3 = indices[2];
-    const REAL *p1 = &vertices[i1*3];
-    const REAL *p2 = &vertices[i2*3];
-    const REAL *p3 = &vertices[i3*3];
-    REAL plane[4];
-    plane[3] = fm_computePlane(p1,p2,p3,plane);
-    const NxU32 *scan = &indices[3];
-    for (NxU32 i=1; i<tcount; i++)
-    {
-      i1 = *scan++;
-      i2 = *scan++;
-      i3 = *scan++;
-      p1 = &vertices[i1*3];
-      p2 = &vertices[i2*3];
-      p3 = &vertices[i3*3];
-      REAL _plane[4];
-      _plane[3] = fm_computePlane(p1,p2,p3,_plane);
-      if ( !fm_samePlane(plane,_plane,0.01f,0.001f,doubleSided) )
-      {
-        ret = false;
-        break;
-      }
-    }
-  }
-  return ret;
-}
-
-
-bool fm_samePlane(const REAL p1[4],const REAL p2[4],REAL normalEpsilon,REAL dEpsilon,bool doubleSided)
-{
-  bool ret = false;
-
-  REAL diff = (REAL) fabs(p1[3]-p2[3]);
-  if ( diff < dEpsilon ) // if the plane -d  co-efficient is within our epsilon
-  {
-    REAL dot = fm_dot(p1,p2); // compute the dot-product of the vector normals.
-    if ( doubleSided ) dot = (REAL)fabs(dot);
-    REAL dmin = 1 - normalEpsilon;
-    REAL dmax = 1 + normalEpsilon;
-    if ( dot >= dmin && dot <= dmax )
-    {
-      ret = true; // then the plane equation is for practical purposes identical.
-    }
-  }
-
-  return ret;
-}
-
-
-void  fm_initMinMax(REAL bmin[3],REAL bmax[3])
-{
-  bmin[0] = FLT_MAX;
-  bmin[1] = FLT_MAX;
-  bmin[2] = FLT_MAX;
-  bmax[0] = FLT_MIN;
-  bmax[1] = FLT_MIN;
-  bmax[2] = FLT_MIN;
-}
-
-
-#ifndef TESSELATE_H
-
-#define TESSELATE_H
-
-typedef CONVEX_DECOMPOSITION::Array< NxU32 > UintVector;
-
-class Myfm_Tesselate : public fm_Tesselate, public Memalloc
-{
-public:
-  virtual ~Myfm_Tesselate(void)
-  {
-
-  }
-
-  const NxU32 * tesselate(fm_VertexIndex *vindex,NxU32 tcount,const NxU32 *indices,NxF32 longEdge,NxU32 maxDepth,NxU32 &outcount)
-  {
-    const NxU32 *ret = 0;
-
-    mMaxDepth = maxDepth;
-    mLongEdge  = longEdge*longEdge;
-    mLongEdgeD = mLongEdge;
-    mVertices = vindex;
-
-    if ( mVertices->isDouble() )
-    {
-      NxU32 vcount = mVertices->getVcount();
-      NxF64 *vertices = (NxF64 *)MEMALLOC_MALLOC(sizeof(NxF64)*vcount*3);
-      memcpy(vertices,mVertices->getVerticesDouble(),sizeof(NxF64)*vcount*3);
-
-      for (NxU32 i=0; i<tcount; i++)
-      {
-        NxU32 i1 = *indices++;
-        NxU32 i2 = *indices++;
-        NxU32 i3 = *indices++;
-
-        const NxF64 *p1 = &vertices[i1*3];
-        const NxF64 *p2 = &vertices[i2*3];
-        const NxF64 *p3 = &vertices[i3*3];
-
-        tesselate(p1,p2,p3,0);
-
-      }
-      MEMALLOC_FREE(vertices);
-    }
-    else
-    {
-      NxU32 vcount = mVertices->getVcount();
-      NxF32 *vertices = (NxF32 *)MEMALLOC_MALLOC(sizeof(NxF32)*vcount*3);
-      memcpy(vertices,mVertices->getVerticesFloat(),sizeof(NxF32)*vcount*3);
-
-
-      for (NxU32 i=0; i<tcount; i++)
-      {
-        NxU32 i1 = *indices++;
-        NxU32 i2 = *indices++;
-        NxU32 i3 = *indices++;
-
-        const NxF32 *p1 = &vertices[i1*3];
-        const NxF32 *p2 = &vertices[i2*3];
-        const NxF32 *p3 = &vertices[i3*3];
-
-        tesselate(p1,p2,p3,0);
-
-      }
-      MEMALLOC_FREE(vertices);
-    }
-
-    outcount = (NxU32)(mIndices.size()/3);
-    ret = &mIndices[0];
-
-
-    return ret;
-  }
-
-  void tesselate(const NxF32 *p1,const NxF32 *p2,const NxF32 *p3,NxU32 recurse)
-  {
-  	bool split = false;
-  	NxF32 l1,l2,l3;
-
-    l1 = l2 = l3 = 0;
-
-  	if ( recurse < mMaxDepth )
-  	{
-  	  l1 = fm_distanceSquared(p1,p2);
-    	l2 = fm_distanceSquared(p2,p3);
-    	l3 = fm_distanceSquared(p3,p1);
-
-  	  if (  l1 > mLongEdge || l2 > mLongEdge || l3 > mLongEdge )
-  	  	split = true;
-
-    }
-
-    if ( split )
-  	{
-  		NxU32 edge;
-
-  		if ( l1 >= l2 && l1 >= l3 )
-  			edge = 0;
-  		else if ( l2 >= l1 && l2 >= l3 )
-  			edge = 1;
-  		else
-  			edge = 2;
-
-			NxF32 split[3];
-
-  		switch ( edge )
-  		{
-  			case 0:
-  				{
-            fm_lerp(p1,p2,split,0.5f);
-            tesselate(p1,split,p3, recurse+1 );
-            tesselate(split,p2,p3, recurse+1 );
-  				}
-  				break;
-  			case 1:
-  				{
-            fm_lerp(p2,p3,split,0.5f);
-            tesselate(p1,p2,split, recurse+1 );
-            tesselate(p1,split,p3, recurse+1 );
-  				}
-  				break;
-  			case 2:
-  				{
-  					fm_lerp(p3,p1,split,0.5f);
-            tesselate(p1,p2,split, recurse+1 );
-            tesselate(split,p2,p3, recurse+1 );
-  				}
-  				break;
-  		}
-  	}
-  	else
-  	{
-      bool newp;
-
-      NxU32 i1 = mVertices->getIndex(p1,newp);
-      NxU32 i2 = mVertices->getIndex(p2,newp);
-      NxU32 i3 = mVertices->getIndex(p3,newp);
-
-      mIndices.pushBack(i1);
-      mIndices.pushBack(i2);
-      mIndices.pushBack(i3);
-    }
-
-  }
-
-  void tesselate(const NxF64 *p1,const NxF64 *p2,const NxF64 *p3,NxU32 recurse)
-  {
-  	bool split = false;
-  	NxF64 l1,l2,l3;
-
-    l1 = l2 = l3 = 0;
-
-  	if ( recurse < mMaxDepth )
-  	{
-  	  l1 = fm_distanceSquared(p1,p2);
-    	l2 = fm_distanceSquared(p2,p3);
-    	l3 = fm_distanceSquared(p3,p1);
-
-  	  if (  l1 > mLongEdgeD || l2 > mLongEdgeD || l3 > mLongEdgeD )
-  	  	split = true;
-
-    }
-
-    if ( split )
-  	{
-  		NxU32 edge;
-
-  		if ( l1 >= l2 && l1 >= l3 )
-  			edge = 0;
-  		else if ( l2 >= l1 && l2 >= l3 )
-  			edge = 1;
-  		else
-  			edge = 2;
-
-			NxF64 split[3];
-
-  		switch ( edge )
-  		{
-  			case 0:
-  				{
-            fm_lerp(p1,p2,split,0.5);
-            tesselate(p1,split,p3, recurse+1 );
-            tesselate(split,p2,p3, recurse+1 );
-  				}
-  				break;
-  			case 1:
-  				{
-            fm_lerp(p2,p3,split,0.5);
-            tesselate(p1,p2,split, recurse+1 );
-            tesselate(p1,split,p3, recurse+1 );
-  				}
-  				break;
-  			case 2:
-  				{
-  					fm_lerp(p3,p1,split,0.5);
-            tesselate(p1,p2,split, recurse+1 );
-            tesselate(split,p2,p3, recurse+1 );
-  				}
-  				break;
-  		}
-  	}
-  	else
-  	{
-      bool newp;
-
-      NxU32 i1 = mVertices->getIndex(p1,newp);
-      NxU32 i2 = mVertices->getIndex(p2,newp);
-      NxU32 i3 = mVertices->getIndex(p3,newp);
-
-      mIndices.pushBack(i1);
-      mIndices.pushBack(i2);
-      mIndices.pushBack(i3);
-    }
-
-  }
-
-private:
-  NxF32           mLongEdge;
-  NxF64          mLongEdgeD;
-  fm_VertexIndex *mVertices;
-  UintVector    mIndices;
-  NxU32          mMaxDepth;
-};
-
-fm_Tesselate * fm_createTesselate(void)
-{
-  Myfm_Tesselate *m = MEMALLOC_NEW(Myfm_Tesselate);
-  return static_cast< fm_Tesselate * >(m);
-}
-
-void           fm_releaseTesselate(fm_Tesselate *t)
-{
-  Myfm_Tesselate *m = static_cast< Myfm_Tesselate *>(t);
-  delete m;
-}
-
-#endif
-
-
-#ifndef RAY_ABB_INTERSECT
-
-#define RAY_ABB_INTERSECT
-
-//! Integer representation of a floating-point value.
-#define IR(x)	((NxU32&)x)
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-/**
-*	A method to compute a ray-AABB intersection.
-*	Original code by Andrew Woo, from "Graphics Gems", Academic Press, 1990
-*	Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500)
-*	Epsilon value added by Klaus Hartmann. (discarding it saves a few cycles only)
-*
-*	Hence this version is faster as well as more robust than the original one.
-*
-*	Should work provided:
-*	1) the integer representation of 0.0f is 0x00000000
-*	2) the sign bit of the NxF32 is the most significant one
-*
-*	Report bugs: [email protected]
-*
-*	\param		aabb		[in] the axis-aligned bounding box
-*	\param		origin		[in] ray origin
-*	\param		dir			[in] ray direction
-*	\param		coord		[out] impact coordinates
-*	\return		true if ray intersects AABB
-*/
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#define RAYAABB_EPSILON 0.00001f
-bool fm_intersectRayAABB(const NxF32 MinB[3],const NxF32 MaxB[3],const NxF32 origin[3],const NxF32 dir[3],NxF32 coord[3])
-{
-  bool Inside = true;
-  NxF32 MaxT[3];
-  MaxT[0]=MaxT[1]=MaxT[2]=-1.0f;
-
-  // Find candidate planes.
-  for(NxU32 i=0;i<3;i++)
-  {
-    if(origin[i] < MinB[i])
-    {
-      coord[i]	= MinB[i];
-      Inside		= false;
-
-      // Calculate T distances to candidate planes
-      if(IR(dir[i]))	MaxT[i] = (MinB[i] - origin[i]) / dir[i];
-    }
-    else if(origin[i] > MaxB[i])
-    {
-      coord[i]	= MaxB[i];
-      Inside		= false;
-
-      // Calculate T distances to candidate planes
-      if(IR(dir[i]))	MaxT[i] = (MaxB[i] - origin[i]) / dir[i];
-    }
-  }
-
-  // Ray origin inside bounding box
-  if(Inside)
-  {
-    coord[0] = origin[0];
-    coord[1] = origin[1];
-    coord[2] = origin[2];
-    return true;
-  }
-
-  // Get largest of the maxT's for final choice of intersection
-  NxU32 WhichPlane = 0;
-  if(MaxT[1] > MaxT[WhichPlane])	WhichPlane = 1;
-  if(MaxT[2] > MaxT[WhichPlane])	WhichPlane = 2;
-
-  // Check final candidate actually inside box
-  if(IR(MaxT[WhichPlane])&0x80000000) return false;
-
-  for(NxU32 i=0;i<3;i++)
-  {
-    if(i!=WhichPlane)
-    {
-      coord[i] = origin[i] + MaxT[WhichPlane] * dir[i];
-#ifdef RAYAABB_EPSILON
-      if(coord[i] < MinB[i] - RAYAABB_EPSILON || coord[i] > MaxB[i] + RAYAABB_EPSILON)	return false;
-#else
-      if(coord[i] < MinB[i] || coord[i] > MaxB[i])	return false;
-#endif
-    }
-  }
-  return true;	// ray hits box
-}
-
-bool fm_intersectLineSegmentAABB(const NxF32 bmin[3],const NxF32 bmax[3],const NxF32 p1[3],const NxF32 p2[3],NxF32 intersect[3])
-{
-  bool ret = false;
-
-  NxF32 dir[3];
-  dir[0] = p2[0] - p1[0];
-  dir[1] = p2[1] - p1[1];
-  dir[2] = p2[2] - p1[2];
-  NxF32 dist = fm_normalize(dir);
-  if ( dist > RAYAABB_EPSILON )
-  {
-    ret = fm_intersectRayAABB(bmin,bmax,p1,dir,intersect);
-    if ( ret )
-    {
-      NxF32 d = fm_distanceSquared(p1,intersect);
-      if ( d  > (dist*dist) )
-      {
-        ret = false;
-      }
-    }
-  }
-  return ret;
-}
-
-#endif
-
-#ifndef OBB_TO_AABB
-
-#define OBB_TO_AABB
-
-#pragma warning(disable:4100)
-void    fm_OBBtoAABB(const NxF32 obmin[3],const NxF32 obmax[3],const NxF32 matrix[16],NxF32 abmin[3],NxF32 abmax[3])
-{
-  assert(0); // not yet implemented.
-}
-
-
-const REAL * computePos(NxU32 index,const REAL *vertices,NxU32 vstride)
-{
-  const char *tmp = (const char *)vertices;
-  tmp+=(index*vstride);
-  return (const REAL*)tmp;
-}
-
-void computeNormal(NxU32 index,REAL *normals,NxU32 nstride,const REAL *normal)
-{
-  char *tmp = (char *)normals;
-  tmp+=(index*nstride);
-  REAL *dest = (REAL *)tmp;
-  dest[0]+=normal[0];
-  dest[1]+=normal[1];
-  dest[2]+=normal[2];
-}
-
-void fm_computeMeanNormals(NxU32 vcount,       // the number of vertices
-                           const REAL *vertices,     // the base address of the vertex position data.
-                           NxU32 vstride,      // the stride between position data.
-                           REAL *normals,            // the base address  of the destination for mean vector normals
-                           NxU32 nstride,      // the stride between normals
-                           NxU32 tcount,       // the number of triangles
-                           const NxU32 *indices)     // the triangle indices
-{
-
-  // Step #1 : Zero out the vertex normals
-  char *dest = (char *)normals;
-  for (NxU32 i=0; i<vcount; i++)
-  {
-    REAL *n = (REAL *)dest;
-    n[0] = 0;
-    n[1] = 0;
-    n[2] = 0;
-    dest+=nstride;
-  }
-
-  // Step #2 : Compute the face normals and accumulate them
-  const NxU32 *scan = indices;
-  for (NxU32 i=0; i<tcount; i++)
-  {
-
-    NxU32 i1 = *scan++;
-    NxU32 i2 = *scan++;
-    NxU32 i3 = *scan++;
-
-    const REAL *p1 = computePos(i1,vertices,vstride);
-    const REAL *p2 = computePos(i2,vertices,vstride);
-    const REAL *p3 = computePos(i3,vertices,vstride);
-
-    REAL normal[3];
-    fm_computePlane(p3,p2,p1,normal);
-
-    computeNormal(i1,normals,nstride,normal);
-    computeNormal(i2,normals,nstride,normal);
-    computeNormal(i3,normals,nstride,normal);
-  }
-
-
-  // Normalize the accumulated normals
-  dest = (char *)normals;
-  for (NxU32 i=0; i<vcount; i++)
-  {
-    REAL *n = (REAL *)dest;
-    fm_normalize(n);
-    dest+=nstride;
-  }
-
-}
-
-#endif
-
-
-#define BIGNUMBER 100000000.0  		/* hundred million */
-
-static inline void Set(REAL *n,REAL x,REAL y,REAL z)
-{
-	n[0] = x;
-	n[1] = y;
-	n[2] = z;
-};
-
-static inline void Copy(REAL *dest,const REAL *source)
-{
-	dest[0] = source[0];
-	dest[1] = source[1];
-	dest[2] = source[2];
-}
-
-
-REAL  fm_computeBestFitSphere(NxU32 vcount,const REAL *points,NxU32 pstride,REAL *center)
-{
-  REAL radius;
-  REAL radius2;
-
-	REAL xmin[3];
-	REAL xmax[3];
-	REAL ymin[3];
-	REAL ymax[3];
-	REAL zmin[3];
-	REAL zmax[3];
-	REAL dia1[3];
-	REAL dia2[3];
-
-  /* FIRST PASS: find 6 minima/maxima points */
-  Set(xmin,BIGNUMBER,BIGNUMBER,BIGNUMBER);
-  Set(xmax,-BIGNUMBER,-BIGNUMBER,-BIGNUMBER);
-  Set(ymin,BIGNUMBER,BIGNUMBER,BIGNUMBER);
-  Set(ymax,-BIGNUMBER,-BIGNUMBER,-BIGNUMBER);
-  Set(zmin,BIGNUMBER,BIGNUMBER,BIGNUMBER);
-  Set(zmax,-BIGNUMBER,-BIGNUMBER,-BIGNUMBER);
-
-  const char *scan = (const char *)points;
-
-
-  for (NxU32 i=0; i<vcount; i++)
-	{
-		const REAL *caller_p = (const REAL *)scan;
-
-   	if (caller_p[0]<xmin[0])
-  	  Copy(xmin,caller_p); /* New xminimum point */
-  	if (caller_p[0]>xmax[0])
-  	  Copy(xmax,caller_p);
-  	if (caller_p[1]<ymin[1])
-  	  Copy(ymin,caller_p);
-  	if (caller_p[1]>ymax[1])
-  	  Copy(ymax,caller_p);
-  	if (caller_p[2]<zmin[2])
-  	  Copy(zmin,caller_p);
-  	if (caller_p[2]>zmax[2])
-  	  Copy(zmax,caller_p);
-
-    scan+=pstride;
-	}
-
-  /* Set xspan = distance between the 2 points xmin & xmax (squared) */
-  REAL dx = xmax[0] - xmin[0];
-  REAL dy = xmax[1] - xmin[1];
-  REAL dz = xmax[2] - xmin[2];
-  REAL xspan = dx*dx + dy*dy + dz*dz;
-
-  /* Same for y & z spans */
-  dx = ymax[0] - ymin[0];
-  dy = ymax[1] - ymin[1];
-  dz = ymax[2] - ymin[2];
-  REAL yspan = dx*dx + dy*dy + dz*dz;
-
-  dx = zmax[0] - zmin[0];
-  dy = zmax[1] - zmin[1];
-  dz = zmax[2] - zmin[2];
-  REAL zspan = dx*dx + dy*dy + dz*dz;
-
-  /* Set points dia1 & dia2 to the maximally separated pair */
-  Copy(dia1,xmin);
-  Copy(dia2,xmax); /* assume xspan biggest */
-  REAL maxspan = xspan;
-
-  if (yspan>maxspan)
-	{
-	  maxspan = yspan;
-  	Copy(dia1,ymin);
-  	Copy(dia2,ymax);
-	}
-
-  if (zspan>maxspan)
-	{
-	  Copy(dia1,zmin);
-	  Copy(dia2,zmax);
-	}
-
-
-  /* dia1,dia2 is a diameter of initial sphere */
-  /* calc initial center */
-  center[0] = (dia1[0]+dia2[0])*0.5f;
-  center[1] = (dia1[1]+dia2[1])*0.5f;
-  center[2] = (dia1[2]+dia2[2])*0.5f;
-
-  /* calculate initial radius**2 and radius */
-
-  dx = dia2[0]-center[0]; /* x component of radius vector */
-  dy = dia2[1]-center[1]; /* y component of radius vector */
-  dz = dia2[2]-center[2]; /* z component of radius vector */
-
-  radius2 = dx*dx + dy*dy + dz*dz;
-  radius = REAL(sqrt(radius2));
-
-  /* SECOND PASS: increment current sphere */
-  {
-    const char *scan = (const char *)points;
-	  for (NxU32 i=0; i<vcount; i++)
-		{
-			const REAL *caller_p = (const REAL *)scan;
-
-  		dx = caller_p[0]-center[0];
-		  dy = caller_p[1]-center[1];
-  		dz = caller_p[2]-center[2];
-
-		  REAL old_to_p_sq = dx*dx + dy*dy + dz*dz;
-
-  		if (old_to_p_sq > radius2) 	/* do r**2 test first */
-			{ 	/* this point is outside of current sphere */
-	  		REAL old_to_p = REAL(sqrt(old_to_p_sq));
-			  /* calc radius of new sphere */
-  			radius = (radius + old_to_p) * 0.5f;
-	  		radius2 = radius*radius; 	/* for next r**2 compare */
-  			REAL old_to_new = old_to_p - radius;
-
-	  		/* calc center of new sphere */
-
-  		  REAL recip = 1.0f /old_to_p;
-
-  			REAL cx = (radius*center[0] + old_to_new*caller_p[0]) * recip;
-	  		REAL cy = (radius*center[1] + old_to_new*caller_p[1]) * recip;
-			  REAL cz = (radius*center[2] + old_to_new*caller_p[2]) * recip;
-
-		    Set(center,cx,cy,cz);
-
-        scan+=pstride;
-			}
-		}
-  }
-
-  return radius;
-}
-
-
-void fm_computeBestFitCapsule(NxU32 vcount,const REAL *points,NxU32 pstride,REAL &radius,REAL &height,REAL matrix[16],bool bruteForce)
-{
-  REAL sides[3];
-  REAL omatrix[16];
-  fm_computeBestFitOBB(vcount,points,pstride,sides,omatrix,bruteForce);
-
-  NxI32 axis = 0;
-  if ( sides[0] > sides[1] && sides[0] > sides[2] )
-    axis = 0;
-  else if ( sides[1] > sides[0] && sides[1] > sides[2] )
-    axis = 1;
-  else 
-    axis = 2;
-
-  REAL localTransform[16];
-
-  REAL maxDist = 0;
-  REAL maxLen = 0;
-
-  switch ( axis )
-  {
-    case 0:
-      {
-        fm_eulerMatrix(0,0,FM_PI/2,localTransform);
-        fm_matrixMultiply(localTransform,omatrix,matrix);
-
-        const NxU8 *scan = (const NxU8 *)points;
-        for (NxU32 i=0; i<vcount; i++)
-        {
-          const REAL *p = (const REAL *)scan;
-          REAL t[3];
-          fm_inverseRT(omatrix,p,t);
-          REAL dist = t[1]*t[1]+t[2]*t[2];
-          if ( dist > maxDist )
-          {
-            maxDist = dist;
-          }
-          REAL l = (REAL) fabs(t[0]);
-          if ( l > maxLen )
-          {
-            maxLen = l;
-          }
-          scan+=pstride;
-        }
-      }
-      height = sides[0];
-      break;
-    case 1:
-      {
-        fm_eulerMatrix(0,FM_PI/2,0,localTransform);
-        fm_matrixMultiply(localTransform,omatrix,matrix);
-
-        const NxU8 *scan = (const NxU8 *)points;
-        for (NxU32 i=0; i<vcount; i++)
-        {
-          const REAL *p = (const REAL *)scan;
-          REAL t[3];
-          fm_inverseRT(omatrix,p,t);
-          REAL dist = t[0]*t[0]+t[2]*t[2];
-          if ( dist > maxDist )
-          {
-            maxDist = dist;
-          }
-          REAL l = (REAL) fabs(t[1]);
-          if ( l > maxLen )
-          {
-            maxLen = l;
-          }
-          scan+=pstride;
-        }
-      }
-      height = sides[1];
-      break;
-    case 2:
-      {
-        fm_eulerMatrix(FM_PI/2,0,0,localTransform);
-        fm_matrixMultiply(localTransform,omatrix,matrix);
-
-        const NxU8 *scan = (const NxU8 *)points;
-        for (NxU32 i=0; i<vcount; i++)
-        {
-          const REAL *p = (const REAL *)scan;
-          REAL t[3];
-          fm_inverseRT(omatrix,p,t);
-          REAL dist = t[0]*t[0]+t[1]*t[1];
-          if ( dist > maxDist )
-          {
-            maxDist = dist;
-          }
-          REAL l = (REAL) fabs(t[2]);
-          if ( l > maxLen )
-          {
-            maxLen = l;
-          }
-          scan+=pstride;
-        }
-      }
-      height = sides[2];
-      break;
-  }
-  radius = (REAL)sqrt(maxDist);
-  height = (maxLen*2)-(radius*2);
-}
-
-
-//************* Triangulation
-
-#ifndef TRIANGULATE_H
-
-#define TRIANGULATE_H
-
-typedef NxU32 TU32;
-
-class TVec
-{
-public:
-	TVec(NxF64 _x,NxF64 _y,NxF64 _z) { x = _x; y = _y; z = _z; };
-	TVec(void) { };
-
-  NxF64 x;
-  NxF64 y;
-  NxF64 z;
-};
-
-typedef CONVEX_DECOMPOSITION::Array< TVec >  TVecVector;
-typedef CONVEX_DECOMPOSITION::Array< TU32 >  TU32Vector;
-
-class CTriangulator
-{
-public:
-    ///     Default constructor
-    CTriangulator();
-
-    ///     Default destructor
-    virtual ~CTriangulator();
-
-    ///     Triangulates the contour
-    void triangulate(TU32Vector &indices);
-
-    ///     Returns the given point in the triangulator array
-    inline TVec get(const TU32 id) { return mPoints[id]; }
-
-    virtual void reset(void)
-    {
-        mInputPoints.clear();
-        mPoints.clear();
-        mIndices.clear();
-    }
-
-    virtual void addPoint(NxF64 x,NxF64 y,NxF64 z)
-    {
-        TVec v(x,y,z);
-        // update bounding box...
-        if ( mInputPoints.empty() )
-        {
-            mMin = v;
-            mMax = v;
-        }
-        else
-        {
-            if ( x < mMin.x ) mMin.x = x;
-            if ( y < mMin.y ) mMin.y = y;
-            if ( z < mMin.z ) mMin.z = z;
-
-            if ( x > mMax.x ) mMax.x = x;
-            if ( y > mMax.y ) mMax.y = y;
-            if ( z > mMax.z ) mMax.z = z;
-        }
-        mInputPoints.pushBack(v);
-    }
-
-    // Triangulation happens in 2d.  We could inverse transform the polygon around the normal direction, or we just use the two most signficant axes
-    // Here we find the two longest axes and use them to triangulate.  Inverse transforming them would introduce more doubleing point error and isn't worth it.
-    virtual NxU32 * triangulate(NxU32 &tcount,NxF64 epsilon)
-    {
-        NxU32 *ret = 0;
-        tcount = 0;
-        mEpsilon = epsilon;
-
-        if ( !mInputPoints.empty() )
-        {
-            mPoints.clear();
-
-          NxF64 dx = mMax.x - mMin.x; // locate the first, second and third longest edges and store them in i1, i2, i3
-          NxF64 dy = mMax.y - mMin.y;
-          NxF64 dz = mMax.z - mMin.z;
-
-          NxU32 i1,i2,i3;
-
-          if ( dx > dy && dx > dz )
-          {
-              i1 = 0;
-              if ( dy > dz )
-              {
-                  i2 = 1;
-                  i3 = 2;
-              }
-              else
-              {
-                  i2 = 2;
-                  i3 = 1;
-              }
-          }
-          else if ( dy > dx && dy > dz )
-          {
-              i1 = 1;
-              if ( dx > dz )
-              {
-                  i2 = 0;
-                  i3 = 2;
-              }
-              else
-              {
-                  i2 = 2;
-                  i3 = 0;
-              }
-          }
-          else
-          {
-              i1 = 2;
-              if ( dx > dy )
-              {
-                  i2 = 0;
-                  i3 = 1;
-              }
-              else
-              {
-                  i2 = 1;
-                  i3 = 0;
-              }
-          }
-
-          NxU32 pcount = (NxU32)mInputPoints.size();
-          const NxF64 *points = &mInputPoints[0].x;
-          for (NxU32 i=0; i<pcount; i++)
-          {
-            TVec v( points[i1], points[i2], points[i3] );
-            mPoints.pushBack(v);
-            points+=3;
-          }
-
-          mIndices.clear();
-          triangulate(mIndices);
-          tcount = (NxU32)mIndices.size()/3;
-          if ( tcount )
-          {
-              ret = &mIndices[0];
-          }
-        }
-        return ret;
-    }
-
-    virtual const NxF64 * getPoint(NxU32 index)
-    {
-        return &mInputPoints[index].x;
-    }
-
-
-private:
-    NxF64                  mEpsilon;
-    TVec                   mMin;
-    TVec                   mMax;
-    TVecVector             mInputPoints;
-    TVecVector             mPoints;
-    TU32Vector             mIndices;
-
-    ///     Tests if a point is inside the given triangle
-    bool _insideTriangle(const TVec& A, const TVec& B, const TVec& C,const TVec& P);
-
-    ///     Returns the area of the contour
-    NxF64 _area();
-
-    bool _snip(NxI32 u, NxI32 v, NxI32 w, NxI32 n, NxI32 *V);
-
-    ///     Processes the triangulation
-    void _process(TU32Vector &indices);
-
-};
-
-///     Default constructor
-CTriangulator::CTriangulator(void)
-{
-}
-
-///     Default destructor
-CTriangulator::~CTriangulator()
-{
-}
-
-///     Triangulates the contour
-void CTriangulator::triangulate(TU32Vector &indices)
-{
-    _process(indices);
-}
-
-///     Processes the triangulation
-void CTriangulator::_process(TU32Vector &indices)
-{
-    const NxI32 n = (const NxI32)mPoints.size();
-    if (n < 3)
-        return;
-    NxI32 *V = (NxI32 *)MEMALLOC_MALLOC(sizeof(NxI32)*n);
-
-	bool flipped = false;
-
-    if (0.0f < _area())
-    {
-        for (NxI32 v = 0; v < n; v++)
-            V[v] = v;
-    }
-    else
-    {
-		flipped = true;
-        for (NxI32 v = 0; v < n; v++)
-            V[v] = (n - 1) - v;
-    }
-
-    NxI32 nv = n;
-    NxI32 count = 2 * nv;
-    for (NxI32 m = 0, v = nv - 1; nv > 2;)
-    {
-        if (0 >= (count--))
-            return;
-
-        NxI32 u = v;
-        if (nv <= u)
-            u = 0;
-        v = u + 1;
-        if (nv <= v)
-            v = 0;
-        NxI32 w = v + 1;
-        if (nv <= w)
-            w = 0;
-
-        if (_snip(u, v, w, nv, V))
-        {
-            NxI32 a, b, c, s, t;
-            a = V[u];
-            b = V[v];
-            c = V[w];
-			if ( flipped )
-			{
-				indices.pushBack(a);
-				indices.pushBack(b);
-				indices.pushBack(c);
-			}
-			else
-			{
-				indices.pushBack(c);
-				indices.pushBack(b);
-				indices.pushBack(a);
-			}
-            m++;
-            for (s = v, t = v + 1; t < nv; s++, t++)
-                V[s] = V[t];
-            nv--;
-            count = 2 * nv;
-        }
-    }
-
-    MEMALLOC_FREE(V);
-}
-
-///     Returns the area of the contour
-NxF64 CTriangulator::_area()
-{
-    NxI32 n = (NxU32)mPoints.size();
-    NxF64 A = 0.0f;
-    for (NxI32 p = n - 1, q = 0; q < n; p = q++)
-    {
-        const TVec &pval = mPoints[p];
-        const TVec &qval = mPoints[q];
-        A += pval.x * qval.y - qval.x * pval.y;
-    }
-	A*=0.5f;
-    return A;
-}
-
-bool CTriangulator::_snip(NxI32 u, NxI32 v, NxI32 w, NxI32 n, NxI32 *V)
-{
-    NxI32 p;
-
-    const TVec &A = mPoints[ V[u] ];
-    const TVec &B = mPoints[ V[v] ];
-    const TVec &C = mPoints[ V[w] ];
-
-    if (mEpsilon > (((B.x - A.x) * (C.y - A.y)) - ((B.y - A.y) * (C.x - A.x))) )
-        return false;
-
-    for (p = 0; p < n; p++)
-    {
-        if ((p == u) || (p == v) || (p == w))
-            continue;
-        const TVec &P = mPoints[ V[p] ];
-        if (_insideTriangle(A, B, C, P))
-            return false;
-    }
-    return true;
-}
-
-///     Tests if a point is inside the given triangle
-bool CTriangulator::_insideTriangle(const TVec& A, const TVec& B, const TVec& C,const TVec& P)
-{
-    NxF64 ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy;
-    NxF64 cCROSSap, bCROSScp, aCROSSbp;
-
-    ax = C.x - B.x;  ay = C.y - B.y;
-    bx = A.x - C.x;  by = A.y - C.y;
-    cx = B.x - A.x;  cy = B.y - A.y;
-    apx = P.x - A.x;  apy = P.y - A.y;
-    bpx = P.x - B.x;  bpy = P.y - B.y;
-    cpx = P.x - C.x;  cpy = P.y - C.y;
-
-    aCROSSbp = ax * bpy - ay * bpx;
-    cCROSSap = cx * apy - cy * apx;
-    bCROSScp = bx * cpy - by * cpx;
-
-    return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f));
-}
-
-class Triangulate : public fm_Triangulate, public Memalloc
-{
-public:
-  Triangulate(void)
-  {
-    mPointsFloat = 0;
-    mPointsDouble = 0;
-  }
-
-  virtual ~Triangulate(void)
-  {
-    reset();
-  }
-  void reset(void)
-  {
-    MEMALLOC_FREE(mPointsFloat);
-    MEMALLOC_FREE(mPointsDouble);
-    mPointsFloat = 0;
-    mPointsDouble = 0;
-  }
-
-  virtual const NxF64 *       triangulate3d(NxU32 pcount,
-                                             const NxF64 *_points,
-                                             NxU32 vstride,
-                                             NxU32 &tcount,
-                                             bool consolidate,
-                                             NxF64 epsilon)
-  {
-    reset();
-
-    NxF64 *points = (NxF64 *)MEMALLOC_MALLOC(sizeof(NxF64)*pcount*3);
-    if ( consolidate )
-    {
-      pcount = fm_consolidatePolygon(pcount,_points,vstride,points,1-epsilon);
-    }
-    else
-    {
-      NxF64 *dest = points;
-      for (NxU32 i=0; i<pcount; i++)
-      {
-        const NxF64 *src = fm_getPoint(_points,vstride,i);
-        dest[0] = src[0];
-        dest[1] = src[1];
-        dest[2] = src[2];
-        dest+=3;
-      }
-      vstride = sizeof(NxF64)*3;
-    }
-
-    if ( pcount >= 3 )
-    {
-      CTriangulator ct;
-      for (NxU32 i=0; i<pcount; i++)
-      {
-        const NxF64 *src = fm_getPoint(points,vstride,i);
-        ct.addPoint( src[0], src[1], src[2] );
-      }
-      NxU32 _tcount;
-      NxU32 *indices = ct.triangulate(_tcount,epsilon);
-      if ( indices )
-      {
-        tcount = _tcount;
-        mPointsDouble = (NxF64 *)MEMALLOC_MALLOC(sizeof(NxF64)*tcount*3*3);
-        NxF64 *dest = mPointsDouble;
-        for (NxU32 i=0; i<tcount; i++)
-        {
-          NxU32 i1 = indices[i*3+0];
-          NxU32 i2 = indices[i*3+1];
-          NxU32 i3 = indices[i*3+2];
-          const NxF64 *p1 = ct.getPoint(i1);
-          const NxF64 *p2 = ct.getPoint(i2);
-          const NxF64 *p3 = ct.getPoint(i3);
-
-          dest[0] = p1[0];
-          dest[1] = p1[1];
-          dest[2] = p1[2];
-
-          dest[3] = p2[0];
-          dest[4] = p2[1];
-          dest[5] = p2[2];
-
-          dest[6] = p3[0];
-          dest[7] = p3[1];
-          dest[8] = p3[2];
-          dest+=9;
-        }
-      }
-    }
-    MEMALLOC_FREE(points);
-
-    return mPointsDouble;
-  }
-
-  virtual const NxF32  *       triangulate3d(NxU32 pcount,
-                                             const NxF32  *points,
-                                             NxU32 vstride,
-                                             NxU32 &tcount,
-                                             bool consolidate,
-                                             NxF32 epsilon)
-  {
-    reset();
-
-    NxF64 *temp = (NxF64 *)MEMALLOC_MALLOC(sizeof(NxF64)*pcount*3);
-    NxF64 *dest = temp;
-    for (NxU32 i=0; i<pcount; i++)
-    {
-      const NxF32 *p = fm_getPoint(points,vstride,i);
-      dest[0] = p[0];
-      dest[1] = p[1];
-      dest[2] = p[2];
-      dest+=3;
-    }
-    const NxF64 *results = triangulate3d(pcount,temp,sizeof(NxF64)*3,tcount,consolidate,epsilon);
-    if ( results )
-    {
-      NxU32 fcount = tcount*3*3;
-      mPointsFloat = (NxF32 *)MEMALLOC_MALLOC(sizeof(NxF32)*tcount*3*3);
-      NxF32 *dest = mPointsFloat;
-      for (NxU32 i=0; i<fcount; i++)
-      {
-        dest[i] = (NxF32) results[i];
-      }
-      MEMALLOC_FREE(mPointsDouble);
-      mPointsDouble = 0;
-    }
-    MEMALLOC_FREE(temp);
-
-    return mPointsFloat;
-  }
-
-private:
-  NxF32 *mPointsFloat;
-  NxF64 *mPointsDouble;
-};
-
-fm_Triangulate * fm_createTriangulate(void)
-{
-  Triangulate *t = MEMALLOC_NEW(Triangulate);
-  return static_cast< fm_Triangulate *>(t);
-}
-
-void             fm_releaseTriangulate(fm_Triangulate *t)
-{
-  Triangulate *tt = static_cast< Triangulate *>(t);
-  delete tt;
-}
-
-#endif
-
-bool validDistance(const REAL *p1,const REAL *p2,REAL epsilon)
-{
-	bool ret = true;
-
-	REAL dx = p1[0] - p2[0];
-	REAL dy = p1[1] - p2[1];
-	REAL dz = p1[2] - p2[2];
-	REAL dist = dx*dx+dy*dy+dz*dz;
-	if ( dist < (epsilon*epsilon) )
-	{
-		ret = false;
-	}
-	return ret;
-}
-
-bool fm_isValidTriangle(const REAL *p1,const REAL *p2,const REAL *p3,REAL epsilon)
-{
-  bool ret = false;
-
-  if ( validDistance(p1,p2,epsilon) &&
-	   validDistance(p1,p3,epsilon) &&
-	   validDistance(p2,p3,epsilon) )
-  {
-
-	  REAL area = fm_computeArea(p1,p2,p3);
-	  if ( area > epsilon )
-	  {
-		REAL _vertices[3*3],vertices[64*3];
-
-		_vertices[0] = p1[0];
-		_vertices[1] = p1[1];
-		_vertices[2] = p1[2];
-
-		_vertices[3] = p2[0];
-		_vertices[4] = p2[1];
-		_vertices[5] = p2[2];
-
-		_vertices[6] = p3[0];
-		_vertices[7] = p3[1];
-		_vertices[8] = p3[2];
-
-		NxU32 pcount = fm_consolidatePolygon(3,_vertices,sizeof(REAL)*3,vertices,1-epsilon);
-		if ( pcount == 3 )
-		{
-		  ret = true;
-		}
-	  }
-  }
-  return ret;
-}
-
-
-void  fm_multiplyQuat(const REAL *left,const REAL *right,REAL *quat)
-{
-	REAL a,b,c,d;
-
-	a = left[3]*right[3] - left[0]*right[0] - left[1]*right[1] - left[2]*right[2];
-	b = left[3]*right[0] + right[3]*left[0] + left[1]*right[2] - right[1]*left[2];
-	c = left[3]*right[1] + right[3]*left[1] + left[2]*right[0] - right[2]*left[0];
-	d = left[3]*right[2] + right[3]*left[2] + left[0]*right[1] - right[0]*left[1];
-
-	quat[3] = a;
-	quat[0] = b;
-	quat[1] = c;
-	quat[2] = d;
-}
-
-}; // end of namespace

+ 0 - 1905
Engine/lib/convexDecomp/NvHashMap.h

@@ -1,1905 +0,0 @@
-/*
-
-NvHashMap.h : A simple hash map and array template class to avoid introducing dependencies on the STL for containers.
-
-*/
-
-
-// This code contains NVIDIA Confidential Information and is disclosed
-// under the Mutual Non-Disclosure Agreement.
-//
-// Notice
-// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES
-// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO
-// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT,
-// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE.
-//
-// Information and code furnished is believed to be accurate and reliable.
-// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such
-// information or for any infringement of patents or other rights of third parties that may
-// result from its use. No license is granted by implication or otherwise under any patent
-// or patent rights of NVIDIA Corporation. Details are subject to change without notice.
-// This code supersedes and replaces all information previously supplied.
-// NVIDIA Corporation products are not authorized for use as critical
-// components in life support devices or systems without express written approval of
-// NVIDIA Corporation.
-//
-// Copyright � 2009 NVIDIA Corporation. All rights reserved.
-// Copyright � 2002-2008 AGEIA Technologies, Inc. All rights reserved.
-// Copyright � 2001-2006 NovodeX. All rights reserved.
-
-#ifndef NV_HASH_MAP_H
-#define NV_HASH_MAP_H
-
-#include "NvUserMemAlloc.h"
-
-#if (defined(NX_WINDOWS) | defined(NX_X360))
-#include <typeinfo.h>
-#endif
-
-#include <new>
-#include <typeinfo>
-#include <stdlib.h>
-#include <string.h>
-//******************************************************
-//******************************************************
-//******************************************************
-
-
-#ifndef NV_FOUNDATION_BASIC_TEMPLATES_H
-#define NV_FOUNDATION_BASIC_TEMPLATES_H
-
-#pragma warning(push)
-#pragma warning(disable:4512) // suppress the 'assignment operator could not be generated' warning message.
-
-namespace CONVEX_DECOMPOSITION
-{
-	template<typename A>
-	struct Equal
-	{
-		bool operator()(const A& a, const A& b)	const { return a==b; }
-	};
-
-	template<typename A>
-	struct Less
-	{
-		bool operator()(const A& a, const A& b)	const { return a<b; }
-	};
-
-	template<typename A>
-	struct Greater
-	{
-		bool operator()(const A& a, const A& b)	const { return a>b; }
-	};
-
-
-	template <class F, class S> 
-	class Pair
-	{
-	public:
-		F	first;
-		S	second;
-		Pair(): first(F()), second(S()) {}
-		Pair(const F &f, const S &s): first(f), second(s) {}
-		Pair(const Pair &p): first(p.first), second(p.second) {}
-	};
-
-	template<unsigned int A>	struct LogTwo	{	static const unsigned int value  = LogTwo<(A>>1)>::value + 1; };
-	template<>					struct LogTwo<1>{	static const unsigned int value  = 0;	};
-
-	template<typename T> struct UnConst	{ typedef T Type; };
-	template<typename T> struct UnConst<const T> { typedef T Type; };
-}
-
-#pragma warning(pop)
-
-#endif
-
-#ifndef NV_FOUNDATION_ALLOCATOR
-#define NV_FOUNDATION_ALLOCATOR
-
-#pragma warning(push)
-#pragma warning(disable:4100)
-
-namespace CONVEX_DECOMPOSITION
-{
-
-
-/**
-\brief The return value is the greater of the two specified values.
-*/
-template<class N>
-NX_INLINE N NxMax(N a, N b)							{	return a<b ? b : a;						}
-
-
-/**
-\brief The return value is the greater of the two specified values.
-*/
-template <>
-NX_INLINE NxF32 NxMax(NxF32 a, NxF32 b)				{	return  a > b ? a : b;	}
-
-/**
-\brief The return value is the lesser of the two specified values.
-*/
-template<class N>
-NX_INLINE N NxMin(N a, N b)							{	return a<b ? a : b;						}
-
-/**
-\brief The return value is the lesser of the two specified values.
-*/
-template <>
-NX_INLINE NxF32 NxMin(NxF32 a, NxF32 b)				{	return a < b ? a : b;	}
-
-
-
-	/**
-	Allocator used to access the global NxUserAllocator instance without providing additional information.
-	*/
-	class Allocator
-	{
-	public:
-		Allocator(const char* dummy = 0) 
-		{
-		}
-		void* allocate(size_t size, const char* file, int line)
-		{
-      return MEMALLOC_MALLOC(size);
-		}
-		void deallocate(void* ptr)
-		{
-      MEMALLOC_FREE(ptr);
-		}
-	};
-
-	/**
-	Allocator used to access the global NxUserAllocator instance using a dynamic name.
-	*/
-	class NamedAllocator
-	{
-	public:
-		NamedAllocator(const char* name = 0) 
-			
-		{
-
-    }
-		void* allocate(size_t size, const char* filename, int line)
-		{
-      return MEMALLOC_MALLOC(size);
-		}
-		void deallocate(void* ptr)
-		{
-      MEMALLOC_FREE(ptr);
-		}
-	private:
-	};
-
-	/**
-	Allocator used to access the global NxUserAllocator instance using a static name derived from T.
-	*/
-	template <typename T>
-	class ReflectionAllocator
-	{
-		static const char* getName()
-		{
-#if defined NX_GNUC
-			return __PRETTY_FUNCTION__;
-#else
-			return typeid(T).name();
-#endif
-		}
-	public:
-		ReflectionAllocator(const char* dummy=0) 
-		{
-		}
-		void* allocate(size_t size, const char* filename, int line)
-		{
-      return MEMALLOC_MALLOC(size);
-		}
-		void deallocate(void* ptr)
-		{
-      MEMALLOC_FREE(ptr);
-		}
-	};
-
-	// if you get a build error here, you are trying to NX_NEW a class 
-	// that is neither plain-old-type nor derived from CONVEX_DECOMPOSITION::UserAllocated
-	template <typename T, typename X>
-	union EnableIfPod
-	{
-		int i; T t;
-		typedef X Type;
-	};
-
-}
-
-// Global placement new for ReflectionAllocator templated by plain-old-type. Allows using NX_NEW for pointers and built-in-types.
-// ATTENTION: You need to use NX_DELETE_POD or NX_FREE to deallocate memory, not NX_DELETE. NX_DELETE_POD redirects to NX_FREE.
-// Rationale: NX_DELETE uses global operator delete(void*), which we dont' want to overload. 
-// Any other definition of NX_DELETE couldn't support array syntax 'NX_DELETE([]a);'. 
-// NX_DELETE_POD was preferred over NX_DELETE_ARRAY because it is used less often and applies to both single instances and arrays.
-template <typename T>
-NX_INLINE void* operator new(size_t size, CONVEX_DECOMPOSITION::ReflectionAllocator<T> alloc, const char* fileName, typename CONVEX_DECOMPOSITION::EnableIfPod<T, int>::Type line)
-{
-	return alloc.allocate(size, fileName, line);
-}
-
-template <typename T>
-NX_INLINE void* operator new[](size_t size, CONVEX_DECOMPOSITION::ReflectionAllocator<T> alloc, const char* fileName, typename CONVEX_DECOMPOSITION::EnableIfPod<T, int>::Type line)
-{
-	return alloc.allocate(size, fileName, line);
-}
-
-// If construction after placement new throws, this placement delete is being called.
-template <typename T>
-NX_INLINE void  operator delete(void* ptr, CONVEX_DECOMPOSITION::ReflectionAllocator<T> alloc, const char* fileName, typename CONVEX_DECOMPOSITION::EnableIfPod<T, int>::Type line)
-{
-	alloc.deallocate(ptr);
-}
-
-// If construction after placement new throws, this placement delete is being called.
-template <typename T>
-NX_INLINE void  operator delete[](void* ptr, CONVEX_DECOMPOSITION::ReflectionAllocator<T> alloc, const char* fileName, typename CONVEX_DECOMPOSITION::EnableIfPod<T, int>::Type line)
-{
-	alloc.deallocate(ptr);
-}
-
-#pragma warning(pop)
-
-#endif
-
-
-#ifndef NV_FOUNDATION_USERALLOCATED
-#define NV_FOUNDATION_USERALLOCATED
-
-// an expression that should expand to nothing in _DEBUG builds.  We currently
-// use this only for tagging the purpose of containers for memory use tracking.
-#if defined(_DEBUG)
-#define NV_DEBUG_EXP(x) (x)
-#define NV_DEBUG_EXP_C(x) x,
-#else
-#define NV_DEBUG_EXP(x)
-#define NV_DEBUG_EXP_C(x)
-#endif
-
-#if defined (NX_X360) | defined (NX_WINDOWS) | defined (NX_CELL) | defined (NXLINUX) | defined(NX_WII) 
-// Stack allocation with alloc fallback for large allocations (>50% of default stack size for platform)
-#	define NX_ALLOCA(var, type, number)											\
-		bool alloced_##var = false;												\
-    if (sizeof(type)*number*2 > (CONVEX_DECOMPOSITION::gSystemServices ? gSystemServices->getAllocaThreshold() : 8192)  )	\
-		{																		\
-			var = (type *)MEMALLOC_MALLOC(sizeof(type)*number);					\
-			alloced_##var = true;												\
-		} else {																\
-			var = (type *)MEMALLOC_ALLOCA(sizeof(type)*number);						\
-		}
-#	define NX_FREEA(var) if (alloced_##var) MEMALLOC_FREE(var);
-#else
-#	define NX_ALLOCA(var, type, number)		var = (type *)NxAlloca(sizeof(type)*number);
-#	define NX_FREEA(var)					0;
-#endif
-
-namespace CONVEX_DECOMPOSITION
-{
-	/**
-	Provides new and delete using a UserAllocator.
-	Guarantees that 'delete x;' uses the UserAllocator too.
-	*/
-	class UserAllocated
-	{
-	public:
-
-		template <typename Alloc>
-		NX_INLINE void* operator new(size_t size, Alloc alloc, const char* fileName, int line)
-		{
-      return MEMALLOC_MALLOC(size);
-		}
-		template <typename Alloc>
-		NX_INLINE void* operator new[](size_t size, Alloc alloc, const char* fileName, int line)
-		{
-      return MEMALLOC_MALLOC(size);
-		}
-
-		NX_INLINE void  operator delete(void* ptr)
-		{
-      MEMALLOC_FREE(ptr);
-		}
-		NX_INLINE void  operator delete[](void* ptr)
-		{
-      MEMALLOC_FREE(ptr);
-		}
-	};
-};
-
-#endif
-
-
-#ifndef NV_FOUNDATION_ALIGNEDMALLOC_H
-#define NV_FOUNDATION_ALIGNEDMALLOC_H
-
-/*!
-Allocate aligned memory.
-Alignment must be a power of 2!
--- should be templated by a base allocator
-*/
-
-namespace CONVEX_DECOMPOSITION
-{
-	/**
-	Allocator, which is used to access the global NxUserAllocator instance
-	(used for dynamic data types template instantiation), which can align memory
-	*/
-
-	// SCS: AlignedMalloc with 3 params not found, seems not used on PC either
-	// disabled for now to avoid GCC error
-
-	template<NxU32 N, typename BaseAllocator = Allocator >
-	class AlignedAllocator : public BaseAllocator
-	{
-	public:
-		AlignedAllocator(const BaseAllocator& base = BaseAllocator()) 
-		: BaseAllocator(base) {}
-
-		void* allocate(size_t size, const char* file, int line)
-		{
-			size_t pad = N - 1 + sizeof(size_t); // store offset for delete.
-			NxU8* base = (NxU8*)BaseAllocator::allocate(size+pad, file, line);
-
-			NxU8* ptr = (NxU8*)(size_t(base + pad) & ~(N - 1)); // aligned pointer
-			((size_t*)ptr)[-1] = ptr - base; // store offset
-
-			return ptr;
-		}
-		void deallocate(void* ptr)
-		{
-			if(ptr == NULL)
-				return;
-
-			NxU8* base = ((NxU8*)ptr) - ((size_t*)ptr)[-1];
-			BaseAllocator::deallocate(base);
-		}
-	};
-}
-
-#endif
-
-
-#ifndef NV_FOUNDATION_INLINE_ALLOCATOR_H
-#define NV_FOUNDATION_INLINE_ALLOCATOR_H
-
-namespace CONVEX_DECOMPOSITION
-{
-	// this is used by the array class to allocate some space for a small number
-	// of objects along with the metadata
-	template<NxU32 N, typename BaseAllocator>
-	class InlineAllocator : private BaseAllocator
-	{
-	public:
-
-		InlineAllocator(const BaseAllocator& alloc = BaseAllocator())
-			: BaseAllocator(alloc)
-		{}
-
-		void* allocate(size_t size, const char* filename, int line)
-		{
-			return size <= N ? mBuffer : BaseAllocator::allocate(size, filename, line);
-		}
-
-		void deallocate(void* ptr)
-		{
-			if(ptr != mBuffer)
-				BaseAllocator::deallocate(ptr);
-		}
-
-	private:
-		NxU8 mBuffer[N];
-	};
-}
-
-#endif
-
-
-#ifndef NV_FOUNDATION_NXSTRIDEDDATA
-#define NV_FOUNDATION_NXSTRIDEDDATA
-/** \addtogroup foundation
-  @{
-*/
-
-template<typename T>
-class NvStrideIterator
-{
-	template <typename X>
-	struct StripConst
-	{
-		typedef X Type;
-	};
-
-	template <typename X>
-	struct StripConst<const X>
-	{
-		typedef X Type;
-	};
-
-public:
-	explicit NX_INLINE NvStrideIterator(T* ptr = NULL, NxU32 stride = sizeof(T)) :
-		mPtr(ptr), mStride(stride)
-	{
-		NX_ASSERT(mStride == 0 || sizeof(T) <= mStride);
-	}
-
-	NX_INLINE NvStrideIterator(const NvStrideIterator<typename StripConst<T>::Type>& strideIterator) :
-		mPtr(strideIterator.ptr()), mStride(strideIterator.stride())
-	{
-		NX_ASSERT(mStride == 0 || sizeof(T) <= mStride);
-	}
-
-	NX_INLINE T* ptr() const
-	{
-		return mPtr;
-	}
-
-	NX_INLINE NxU32 stride() const
-	{
-		return mStride;
-	}
-
-	NX_INLINE T& operator*() const
-	{
-		return *mPtr;
-	}
-
-	NX_INLINE T* operator->() const
-	{
-		return mPtr;
-	}
-
-	NX_INLINE T& operator[](int i) const
-	{
-		return *byteAdd(mPtr, i * stride());
-	}
-
-	// preincrement
-	NX_INLINE NvStrideIterator& operator++()
-	{
-		mPtr = byteAdd(mPtr, stride());
-		return *this;
-	}
-
-	// postincrement
-	NX_INLINE NvStrideIterator operator++(int)
-	{
-		NvStrideIterator tmp = *this;
-		mPtr = byteAdd(mPtr, stride());
-		return tmp;
-	}
-
-	// predecrement
-	NX_INLINE NvStrideIterator& operator--()
-	{
-		mPtr = byteSub(mPtr, stride());
-		return *this;
-	}
-
-	// postdecrement
-	NX_INLINE NvStrideIterator operator--(int)
-	{
-		NvStrideIterator tmp = *this;
-		mPtr = byteSub(mPtr, stride());
-		return tmp;
-	}
-
-	NX_INLINE NvStrideIterator& operator+=(int i)
-	{
-		mPtr = byteAdd(mPtr, i * stride());
-		return *this;
-	}
-
-	NX_INLINE NvStrideIterator operator+(int i) const
-	{	
-		return NvStrideIterator(byteAdd(mPtr, i * stride()), stride());
-	}
-
-	NX_INLINE NvStrideIterator& operator-=(int i)
-	{
-		mPtr = byteSub(mPtr, i * stride());
-		return *this;
-	}
-
-	NX_INLINE NvStrideIterator operator-(int i) const
-	{
-		return NvStrideIterator(byteSub(mPtr, i * stride()), stride());
-	}
-
-	// iterator difference
-	NX_INLINE int operator-(const NvStrideIterator& other) const
-	{
-		NX_ASSERT(isCompatible(other));
-		int byteDiff = static_cast<int>(reinterpret_cast<const NxU8*>(mPtr) - reinterpret_cast<const NxU8*>(other.mPtr));
-		return byteDiff / static_cast<int>(stride());
-	}
-
-	NX_INLINE bool operator==(const NvStrideIterator& other) const
-	{
-		NX_ASSERT(isCompatible(other));
-		return mPtr == other.mPtr;
-	}
-
-	NX_INLINE bool operator!=(const NvStrideIterator& other) const
-	{
-		NX_ASSERT(isCompatible(other));
-		return mPtr != other.mPtr;
-	}
-
-	NX_INLINE bool operator<(const NvStrideIterator& other) const
-	{
-		NX_ASSERT(isCompatible(other));
-		return mPtr < other.mPtr;
-	}
-
-	NX_INLINE bool operator>(const NvStrideIterator& other) const
-	{
-		NX_ASSERT(isCompatible(other));
-		return mPtr > other.mPtr;
-	}
-
-	NX_INLINE bool operator<=(const NvStrideIterator& other) const
-	{
-		NX_ASSERT(isCompatible(other));
-		return mPtr <= other.mPtr;
-	}
-
-	NX_INLINE bool operator>=(const NvStrideIterator& other) const
-	{
-		NX_ASSERT(isCompatible(other));
-		return mPtr >= other.mPtr;
-	}
-
-private:
-	NX_INLINE static T* byteAdd(T* ptr, NxU32 bytes) 
-	{ 
-		return const_cast<T*>(reinterpret_cast<const T*>(reinterpret_cast<const NxU8*>(ptr) + bytes));
-	}
-
-	NX_INLINE static T* byteSub(T* ptr, NxU32 bytes)
-	{ 
-		return const_cast<T*>(reinterpret_cast<const T*>(reinterpret_cast<const NxU8*>(ptr) - bytes));
-	}
-
-	NX_INLINE bool isCompatible(const NvStrideIterator& other) const
-	{
-		int byteDiff = static_cast<int>(reinterpret_cast<const NxU8*>(mPtr) - reinterpret_cast<const NxU8*>(other.mPtr));
-		return (stride() == other.stride()) && (abs(byteDiff) % stride() == 0);
-	}
-
-	T* mPtr;
-	NxU32 mStride;
-};
-
-
-template<typename T>
-NX_INLINE NvStrideIterator<T> operator+(int i, NvStrideIterator<T> it)
-{
-	it += i;
-	return it;
-}
-
- /** @} */
-#endif
-
-#ifndef NV_FOUNDATION_ARRAY
-#define NV_FOUNDATION_ARRAY
-
-namespace CONVEX_DECOMPOSITION
-{
-	namespace Internal
-	{
-		template <typename T>
-		struct ArrayMetaData
-		{
-			T*					mData;
-			NxU32				mCapacity;
-			NxU32				mSize;
-			ArrayMetaData(): mSize(0), mCapacity(0), mData(0) {}
-		};
-
-		template <typename T>
-		struct AllocatorTraits
-		{
-#if defined _DEBUG
-			typedef NamedAllocator Type;
-#else
-			typedef ReflectionAllocator<T> Type;
-#endif
-		};
-	}
-
-	/*!
-	An array is a sequential container.
-
-	Implementation note
-	* entries between 0 and size are valid objects
-	* we use inheritance to build this because the array is included inline in a lot
-	  of objects and we want the allocator to take no space if it's not stateful, which
-	  aggregation doesn't allow. Also, we want the metadata at the front for the inline
-	  case where the allocator contains some inline storage space
-	*/
-	template<class T, class Alloc = typename Internal::AllocatorTraits<T>::Type >
-	class Array : private Internal::ArrayMetaData<T>, private Alloc
-	{
-		typedef Internal::ArrayMetaData<T> MetaData;
-
-		using MetaData::mCapacity;
-		using MetaData::mData;
-		using MetaData::mSize;
-
-	public:
-
-		typedef T*			Iterator;
-		typedef const T*	ConstIterator;
-
-		/*!
-		Default array constructor. Initialize an empty array
-		*/
-		NX_INLINE Array(const Alloc& alloc = Alloc()) : Alloc(alloc) {}
-
-		/*!
-		Initialize array with given length
-		*/
-		NX_INLINE  explicit Array(NxU32 capacity, const Alloc& alloc = Alloc())
-		: Alloc(alloc)
-		{
-			if(mCapacity>0)
-				allocate(mCapacity);
-		}
-
-		/*!
-		Copy-constructor. Copy all entries from other array
-		*/
-		template <class A> 
-		NX_INLINE Array(const Array<T,A>& other, const Alloc& alloc = Alloc()) 
-		{
-			if(other.mSize > 0)
-			{
-				mData = allocate(mSize = mCapacity = other.mSize);
-				copy(mData, other.mData, mSize);
-			}
-		}
-
-		/*!
-		Default destructor
-		*/
-		NX_INLINE ~Array()
-		{
-			destroy(0, mSize);
-			if(mCapacity)
-				deallocate(mData);
-		}
-
-		/*!
-		Assignment operator. Copy content (deep-copy)
-		*/
-		template <class A> 
-		NX_INLINE const Array& operator= (const Array<T,A>& t)
-		{
-			if(&t == this)
-				return *this;
-
-			if(mCapacity < t.mSize)
-			{
-				destroy(0,mSize);
-				deallocate(mData);
-
-				mData = allocate(t.mCapacity);
-				mCapacity = t.mCapacity;
-
-				copy(mData,t.mData,t.mSize);
-			}
-			else
-			{
-				NxU32 m = NxMin(t.mSize,mSize);
-				copy(mData,t.mData,m);
-				for(NxU32 i = m; i < mSize;i++)
-					mData[i].~T();
-				for(NxU32 i = m; i < t.mSize; i++)
-					new(mData+i)T(t.mData[i]);
-			}
-
-			mSize = t.mSize;
-			return *this;
-		}
-
-		/*!
-		Array indexing operator.
-		\param i
-		The index of the element that will be returned.
-		\return
-		The element i in the array.
-		*/
-		NX_INLINE const T& operator[] (NxU32 i) const 
-		{
-			return mData[i];
-		}
-
-		/*!
-		Array indexing operator.
-		\param i
-		The index of the element that will be returned.
-		\return
-		The element i in the array.
-		*/
-		NX_INLINE T& operator[] (NxU32 i) 
-		{
-			return mData[i];
-		}
-
-		/*!
-		Returns a pointer to the initial element of the array.
-		\return
-		a pointer to the initial element of the array.
-		*/
-		NX_INLINE ConstIterator begin() const 
-		{
-			return mData;
-		}
-
-		NX_INLINE Iterator begin()
-		{
-			return mData;
-		}
-
-		/*!
-		Returns an iterator beyond the last element of the array. Do not dereference.
-		\return
-		a pointer to the element beyond the last element of the array.
-		*/
-
-		NX_INLINE ConstIterator end() const 
-		{
-			return mData+mSize;
-		}
-
-		NX_INLINE Iterator end()
-		{
-			return mData+mSize;
-		}
-
-		/*!
-		Returns a reference to the first element of the array. Undefined if the array is empty.
-		\return a reference to the first element of the array
-		*/
-
-		NX_INLINE const T& front() const 
-		{
-			NX_ASSERT(mSize);
-			return mData[0];
-		}
-
-		NX_INLINE T& front()
-		{
-			NX_ASSERT(mSize);
-			return mData[0];
-		}
-
-		/*!
-		Returns a reference to the last element of the array. Undefined if the array is empty
-		\return a reference to the last element of the array
-		*/
-
-		NX_INLINE const T& back() const 
-		{
-			NX_ASSERT(mSize);
-			return mData[mSize-1];
-		}
-
-		NX_INLINE T& back()
-		{
-			NX_ASSERT(mSize);
-			return mData[mSize-1];
-		}
-
-
-		/*!
-		Returns the number of entries in the array. This can, and probably will,
-		differ from the array capacity.
-		\return
-		The number of of entries in the array.
-		*/
-		NX_INLINE NxU32 size() const 
-		{
-			return mSize;
-		}
-
-		/*!
-		Clears the array.
-		*/
-		NX_INLINE void clear()
-		{
-			destroy(0,mSize);
-			mSize = 0;
-		}
-
-		/*!
-		Returns whether the array is empty (i.e. whether its size is 0).
-		\return
-		true if the array is empty
-		*/
-		NX_INLINE bool empty() const
-		{
-			return mSize==0;
-		}
-
-		/*!
-		Finds the first occurrence of an element in the array.
-		\param a
-		The element that will be removed. 
-		*/
-
-
-		NX_INLINE Iterator find(const T&a)
-		{
-			NxU32 index;
-			for(index=0;index<mSize && mData[index]!=a;index++)
-				;
-			return mData+index;
-		}
-
-		NX_INLINE ConstIterator find(const T&a) const
-		{
-			NxU32 index;
-			for(index=0;index<mSize && mData[index]!=a;index++)
-				;
-			return mData+index;
-		}
-
-
-		/////////////////////////////////////////////////////////////////////////
-		/*!
-		Adds one element to the end of the array. Operation is O(1).
-		\param a
-		The element that will be added to this array.
-		*/
-		/////////////////////////////////////////////////////////////////////////
-
-		NX_INLINE T& pushBack(const T& a)
-		{
-			if(mCapacity<=mSize) 
-				grow(capacityIncrement());
-
-			new((void*)(mData + mSize)) T(a);
-
-			return mData[mSize++];
-		}
-
-		/////////////////////////////////////////////////////////////////////////
-		/*!
-		Returns the element at the end of the array. Only legal if the array is non-empty.
-		*/
-		/////////////////////////////////////////////////////////////////////////
-		NX_INLINE T popBack() 
-		{
-			NX_ASSERT(mSize);
-			T t = mData[mSize-1];
-			mData[--mSize].~T();
-			return t;
-		}
-
-
-		/////////////////////////////////////////////////////////////////////////
-		/*!
-		Construct one element at the end of the array. Operation is O(1).
-		*/
-		/////////////////////////////////////////////////////////////////////////
-		NX_INLINE T& insert()
-		{
-			if(mCapacity<=mSize) 
-				grow(capacityIncrement());
-
-			return *(new (mData+mSize++)T);
-		}
-
-		/////////////////////////////////////////////////////////////////////////
-		/*!
-		Subtracts the element on position i from the array and replace it with
-		the last element.
-		Operation is O(1)
-		\param i
-		The position of the element that will be subtracted from this array.
-		\return
-		The element that was removed.
-		*/
-		/////////////////////////////////////////////////////////////////////////
-		NX_INLINE void replaceWithLast(NxU32 i)
-		{
-			NX_ASSERT(i<mSize);
-			mData[i] = mData[--mSize];
-			mData[mSize].~T();
-		}
-
-		NX_INLINE void replaceWithLast(Iterator i) 
-		{
-			replaceWithLast(static_cast<NxU32>(i-mData));
-		}
-
-		/////////////////////////////////////////////////////////////////////////
-		/*!
-		Replaces the first occurrence of the element a with the last element
-		Operation is O(n)
-		\param i
-		The position of the element that will be subtracted from this array.
-		\return Returns true if the element has been removed.
-		*/
-		/////////////////////////////////////////////////////////////////////////
-
-		NX_INLINE bool findAndReplaceWithLast(const T& a)
-		{
-			NxU32 index;
-			for(index=0;index<mSize && mData[index]!=a;index++)
-				;
-			if(index >= mSize)
-				return false;
-			replaceWithLast(index);
-			return true;
-		}
-
-		/////////////////////////////////////////////////////////////////////////
-		/*!
-		Subtracts the element on position i from the array. Shift the entire
-		array one step.
-		Operation is O(n)
-		\param i
-		The position of the element that will be subtracted from this array.
-		\return
-		The element that was removed.
-		*/
-		/////////////////////////////////////////////////////////////////////////
-		NX_INLINE void remove(NxU32 i) 
-		{
-			NX_ASSERT(i<mSize);
-			while(i+1<mSize)
-			{
-				mData[i] = mData[i+1];
-				i++;
-			}
-
-			mData[--mSize].~T();
-		}
-
-
-		//////////////////////////////////////////////////////////////////////////
-		/*!
-		Resize array
-		\param compaction
-		If set to true and the specified size is smaller than the capacity, a new
-		memory block which fits the size is allocated and the old one gets freed.
-		*/
-		//////////////////////////////////////////////////////////////////////////
-		NX_INLINE void resize(const NxU32 size, const bool compaction = false, const T& a = T())
-		{
-			if(size > mCapacity)
-			{
-				grow(size);
-			}
-			else if (compaction && (size != mCapacity))
-			{
-				recreate(size, NxMin(mSize, size));
-			}
-
-			for(NxU32 i = mSize; i < size; i++)
-				::new(mData+i)T(a);
-
-			if (!compaction)  // With compaction, these elements have been deleted already
-			{
-				for(NxU32 i = size; i < mSize; i++)
-					mData[i].~T();
-			}
-
-			mSize = size;
-		}
-
-
-		//////////////////////////////////////////////////////////////////////////
-		/*!
-		Resize array such that only as much memory is allocated to hold the 
-		existing elements
-		*/
-		//////////////////////////////////////////////////////////////////////////
-		NX_INLINE void shrink()
-		{
-			resize(mSize, true);
-		}
-
-
-		//////////////////////////////////////////////////////////////////////////
-		/*!
-		Deletes all array elements and frees memory.
-		*/
-		//////////////////////////////////////////////////////////////////////////
-		NX_INLINE void reset()
-		{
-			resize(0, true);
-		}
-
-
-		//////////////////////////////////////////////////////////////////////////
-		/*!
-		Ensure that the array has at least size capacity.
-		*/
-		//////////////////////////////////////////////////////////////////////////
-		NX_INLINE void reserve(const NxU32 size)
-		{
-			if(size > mCapacity)
-				grow(size);
-		}
-
-		//////////////////////////////////////////////////////////////////////////
-		/*!
-		Query the capacity(allocated mem) for the array.
-		*/
-		//////////////////////////////////////////////////////////////////////////
-		NX_INLINE NxU32 capacity()	const
-		{
-			return mCapacity;
-		}
-
-
-	private:
-
-		NX_INLINE T* allocate(size_t capacity)
-		{
-			return (T*)Alloc::allocate(sizeof(T) * capacity, __FILE__, __LINE__);
-		}
-
-		NX_INLINE void deallocate(void *mem)
-		{
-			Alloc::deallocate(mem);
-		}
-
-		NX_INLINE void copy(T* dst, const T* src, size_t count)
-		{
-			for(size_t i=0;i<count;i++)
-				::new (dst+i)T(src[i]);
-		}
-
-		NX_INLINE void destroy(size_t start, size_t end)
-		{
-			for(size_t i = start; i<end; i++)
-				mData[i].~T();
-		}
-
-		// The idea here is to prevent accidental brain-damage with pushBack or insert. Unfortunately
-		// it interacts badly with InlineArrays with smaller inline allocations.
-		// TODO(dsequeira): policy template arg, this is exactly what they're for.
-		NX_INLINE NxU32 capacityIncrement()	const
-		{
-			return mCapacity == 0 ? 1 : mCapacity * 2;
-		}
-
-		/*!
-		Creates a new memory block, copies all entries to the new block and destroys old entries.
-
-		\param capacity
-		The number of entries that the set should be able to hold.
-		\param copyCount
-		The number of entries to copy.
-		*/
-		NX_INLINE void recreate(NxU32 capacity, NxU32 copyCount)
-		{
-			NX_ASSERT(capacity >= copyCount);
-			NX_ASSERT(mSize >= copyCount);
-			T* newData = allocate(capacity);
-			NX_ASSERT(	((newData != NULL) && (capacity > 0)) ||
-						((newData == NULL) && (capacity == 0)) );
-
-			if(mCapacity)
-			{
-				copy(newData,mData,copyCount);
-				destroy(0,mSize);
-				deallocate(mData);
-			}
-
-			mData = newData;
-			mCapacity = capacity;
-		}
-
-		/*!
-		Resizes the available memory for the array.
-
-		\param capacity
-		The number of entries that the set should be able to hold.
-		*/	
-		NX_INLINE void grow(NxU32 capacity) 
-		{
-			NX_ASSERT(mCapacity < capacity);
-			recreate(capacity, mSize);
-		}
-	};
-
-	// array that pre-allocates for N elements
-	template <typename T, NxU32 N, typename Alloc = typename Internal::AllocatorTraits<T>::Type>
-	class InlineArray : public Array<T, InlineAllocator<N * sizeof(T), Alloc> >
-	{
-		typedef InlineAllocator<N * sizeof(T), Alloc> Allocator;
-	public:
-		NX_INLINE InlineArray(const Alloc& alloc = Alloc()) 
-			: Array<T, Allocator>(alloc) 
-		{}
-	};
-}
-
-template <typename T>
-NX_INLINE NvStrideIterator<T> getStrideIterator(CONVEX_DECOMPOSITION::Array<T>& array)
-{
-	return NvStrideIterator<T>(array.begin(), sizeof(T));
-}
-
-template <typename T>
-NX_INLINE NvStrideIterator<const T> getConstStrideIterator(CONVEX_DECOMPOSITION::Array<T>& array)
-{
-	return NvStrideIterator<const T>(array.begin(), sizeof(T));
-}
-
-
-#endif
-
-#ifndef NV_FOUNDATION_BITUTILS_H
-#define NV_FOUNDATION_BITUTILS_H
-
-namespace CONVEX_DECOMPOSITION
-{
-	NX_INLINE NxU32 bitCount32(NxU32 v)
-	{
-		// from http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
-		NxU32 const w = v - ((v >> 1) & 0x55555555);
-		NxU32 const x = (w & 0x33333333) + ((w >> 2) & 0x33333333);
-		return ((x + (x >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
-	}
-
-	/*!
-	Return the index of the highest set bit. Or 0 if no bits are set.
-	*/
-	NX_INLINE NxU32 highestSetBit32(NxU32 v)
-	{
-		for(NxU32 j = 32; j-- > 0;)
-		{
-			if(v&(1<<j))
-				return j;
-		}
-		return 0;
-	}
-
-	NX_INLINE bool isPowerOfTwo(NxU32 x)
-	{
-		return x!=0 && (x & x-1) == 0;
-	}
-
-	// "Next Largest Power of 2
-	// Given a binary integer value x, the next largest power of 2 can be computed by a SWAR algorithm
-	// that recursively "folds" the upper bits into the lower bits. This process yields a bit vector with
-	// the same most significant 1 as x, but all 1's below it. Adding 1 to that value yields the next
-	// largest power of 2. For a 32-bit value:"
-	NX_INLINE NxU32 nextPowerOfTwo(NxU32 x)
-	{
-		x |= (x >> 1);
-		x |= (x >> 2);
-		x |= (x >> 4);
-		x |= (x >> 8);
-		x |= (x >> 16);
-		return x+1;
-	}
-
-	// Helper function to approximate log2 of an integer value (assumes that the input is actually power of two)
-	NX_INLINE NxU32 ilog2(NxU32 num)
-	{
-		for (NxU32 i=0; i<32; i++)
-		{
-			num >>= 1;
-			if (num == 0) return i;
-		}
-
-		NX_ASSERT(0);
-		return (NxU32)-1;
-	}
-
-	NX_INLINE int intChop(const NxF32& f)
-	{
-		NxI32 a			= *reinterpret_cast<const NxI32*>(&f);			// take bit pattern of float into a register
-		NxI32 sign		= (a>>31);										// sign = 0xFFFFFFFF if original value is negative, 0 if positive
-		NxI32 mantissa	= (a&((1<<23)-1))|(1<<23);						// extract mantissa and add the hidden bit
-		NxI32 exponent	= ((a&0x7fffffff)>>23)-127;						// extract the exponent
-		NxI32 r			= ((NxU32)(mantissa)<<8)>>(31-exponent);		// ((1<<exponent)*mantissa)>>24 -- (we know that mantissa > (1<<24))
-		return ((r ^ (sign)) - sign ) &~ (exponent>>31);				// add original sign. If exponent was negative, make return value 0.
-	}
-
-	NX_INLINE int intFloor(const NxF32& f)
-	{
-		NxI32 a			= *reinterpret_cast<const NxI32*>(&f);									// take bit pattern of float into a register
-		NxI32 sign		= (a>>31);																// sign = 0xFFFFFFFF if original value is negative, 0 if positive
-		a&=0x7fffffff;																			// we don't need the sign any more
-		NxI32 exponent	= (a>>23)-127;															// extract the exponent
-		NxI32 expsign   = ~(exponent>>31);														// 0xFFFFFFFF if exponent is positive, 0 otherwise
-		NxI32 imask		= ( (1<<(31-(exponent))))-1;											// mask for true integer values
-		NxI32 mantissa	= (a&((1<<23)-1));														// extract mantissa (without the hidden bit)
-		NxI32 r			= ((NxU32)(mantissa|(1<<23))<<8)>>(31-exponent);						// ((1<<exponent)*(mantissa|hidden bit))>>24 -- (we know that mantissa > (1<<24))
-		r = ((r & expsign) ^ (sign)) + ((!((mantissa<<8)&imask)&(expsign^((a-1)>>31)))&sign);	// if (fabs(value)<1.0) value = 0; copy sign; if (value < 0 && value==(int)(value)) value++;
-		return r;
-	}
-
-	NX_INLINE int intCeil(const NxF32& f)
-	{
-		NxI32 a			= *reinterpret_cast<const NxI32*>(&f) ^ 0x80000000;						// take bit pattern of float into a register
-		NxI32 sign		= (a>>31);																// sign = 0xFFFFFFFF if original value is negative, 0 if positive
-		a&=0x7fffffff;																			// we don't need the sign any more
-		NxI32 exponent	= (a>>23)-127;															// extract the exponent
-		NxI32 expsign   = ~(exponent>>31);														// 0xFFFFFFFF if exponent is positive, 0 otherwise
-		NxI32 imask		= ( (1<<(31-(exponent))))-1;											// mask for true integer values
-		NxI32 mantissa	= (a&((1<<23)-1));														// extract mantissa (without the hidden bit)
-		NxI32 r			= ((NxU32)(mantissa|(1<<23))<<8)>>(31-exponent);						// ((1<<exponent)*(mantissa|hidden bit))>>24 -- (we know that mantissa > (1<<24))
-		r = ((r & expsign) ^ (sign)) + ((!((mantissa<<8)&imask)&(expsign^((a-1)>>31)))&sign);	// if (fabs(value)<1.0) value = 0; copy sign; if (value < 0 && value==(int)(value)) value++;
-		return -r;
-	}
-
-}
-
-#endif
-
-#ifndef NV_FOUNDATION_HASHFUNCTION_H
-#define NV_FOUNDATION_HASHFUNCTION_H
-
-/*!
-Central definition of hash functions
-*/
-
-namespace CONVEX_DECOMPOSITION
-{
-	// Hash functions
-	template<class T>
-	NxU32 hash(const T& key)
-	{
-		return (NxU32)key;
-	}
-
-	// Thomas Wang's 32 bit mix
-	// http://www.cris.com/~Ttwang/tech/inthash.htm
-	template<>
-	NX_INLINE NxU32 hash<NxU32>(const NxU32& key)
-	{
-		NxU32 k = key;
-		k += ~(k << 15);
-		k ^= (k >> 10);
-		k += (k << 3);
-		k ^= (k >> 6);
-		k += ~(k << 11);
-		k ^= (k >> 16);
-		return (NxU32)k;
-	}
-
-	template<>
-	NX_INLINE NxU32 hash<NxI32>(const NxI32& key)
-	{
-		return hash((NxU32)key);
-	}
-
-	// Thomas Wang's 64 bit mix
-	// http://www.cris.com/~Ttwang/tech/inthash.htm
-	template<>
-	NX_INLINE NxU32 hash<NxU64>(const NxU64& key)
-	{
-		NxU64 k = key;
-		k += ~(k << 32);
-		k ^= (k >> 22);
-		k += ~(k << 13);
-		k ^= (k >> 8);
-		k += (k << 3);
-		k ^= (k >> 15);
-		k += ~(k << 27);
-		k ^= (k >> 31);
-		return (NxU32)k;
-	}
-
-	// Helper for pointer hashing
-	template<int size>
-	NxU32 PointerHash(const void* ptr);
-
-	template<>
-	NX_INLINE NxU32 PointerHash<4>(const void* ptr)
-	{
-		return hash<NxU32>(static_cast<NxU32>(reinterpret_cast<size_t>(ptr)));
-	}
-
-
-	template<>
-	NX_INLINE NxU32 PointerHash<8>(const void* ptr)
-	{
-		return hash<NxU64>(reinterpret_cast<size_t>(ptr));
-	}
-
-	// Hash function for pointers
-	template<class T>
-	NX_INLINE NxU32 hash(T* key)
-	{
-		return PointerHash<sizeof(const void*)>(key);
-	}
-
-	// Hash function object for pointers
-	template <class T>
-	struct PointerHashFunctor
-	{
-		NxU32 operator()(const T* t) const
-		{
-			return PointerHash<sizeof(T*)>(t);
-		}
-		bool operator()(const T* t0, const T* t1) const
-		{
-			return t0 == t1;
-		}
-	};
-
-	/*
-	--------------------------------------------------------------------
-	lookup2.c, by Bob Jenkins, December 1996, Public Domain.
-	--------------------------------------------------------------------
-	--------------------------------------------------------------------
-	mix -- mix 3 32-bit values reversibly.
-	For every delta with one or two bit set, and the deltas of all three
-	high bits or all three low bits, whether the original value of a,b,c
-	is almost all zero or is uniformly distributed,
-	* If mix() is run forward or backward, at least 32 bits in a,b,c
-	have at least 1/4 probability of changing.
-	* If mix() is run forward, every bit of c will change between 1/3 and
-	2/3 of the time.  (Well, 22/100 and 78/100 for some 2-bit deltas.)
-	mix() was built out of 36 single-cycle latency instructions in a 
-	structure that could supported 2x parallelism, like so:
-	a -= b; 
-	a -= c; x = (c>>13);
-	b -= c; a ^= x;
-	b -= a; x = (a<<8);
-	c -= a; b ^= x;
-	c -= b; x = (b>>13);
-	...
-	Unfortunately, superscalar Pentiums and Sparcs can't take advantage 
-	of that parallelism.  They've also turned some of those single-cycle
-	latency instructions into multi-cycle latency instructions.  Still,
-	this is the fastest good hash I could find.  There were about 2^^68
-	to choose from.  I only looked at a billion or so.
-	--------------------------------------------------------------------
-	*/
-	NX_INLINE NxU32 hashMix(NxU32 &a, NxU32 &b, NxU32 &c)
-	{
-		a -= b; a -= c; a ^= (c>>13);
-		b -= c; b -= a; b ^= (a<<8);
-		c -= a; c -= b; c ^= (b>>13);
-		a -= b; a -= c; a ^= (c>>12);
-		b -= c; b -= a; b ^= (a<<16);
-		c -= a; c -= b; c ^= (b>>5);
-		a -= b; a -= c; a ^= (c>>3);
-		b -= c; b -= a; b ^= (a<<10);
-		c -= a; c -= b; c ^= (b>>15);
-	}
-
-	NX_INLINE NxU32 hash(const NxU32 *k, NxU32 length)
-	{
-		NxU32 a,b,c,len;
-
-		/* Set up the internal state */
-		len = length;
-		a = b = 0x9e3779b9;  /* the golden ratio; an arbitrary value */
-		c = 0;           /* the previous hash value */
-
-		/*---------------------------------------- handle most of the key */
-		while (len >= 3)
-		{
-			a += k[0];
-			b += k[1];
-			c += k[2];
-			hashMix(a,b,c);
-			k += 3; 
-			len -= 3;
-		}
-
-		/*-------------------------------------- handle the last 2 ub4's */
-		c += length;
-		switch(len)              /* all the case statements fall through */
-		{
-			/* c is reserved for the length */
-		case 2 : b+=k[1];
-		case 1 : a+=k[0];
-			/* case 0: nothing left to add */
-		}
-		hashMix(a,b,c);
-		/*-------------------------------------------- report the result */
-		return c;
-	}
-
-	template <class Key>
-	class Hash
-	{
-	public:
-		NxU32 operator()(const Key &k) const { return hash<Key>(k); }
-		bool operator()(const Key &k0, const Key &k1) const { return k0 == k1; }
-	};
-
-	class NvStringHash
-	{
-	public:
-		NxU32 operator()(const char *string) const
-		{
-			// "DJB" string hash 
-			NxU32 h = 5381;
-			for(const char *ptr = string; *ptr; ptr++)
-				h = ((h<<5)+h)^*ptr;
-			return h;
-		}
-		bool operator()(const char* string0, const char* string1) const
-		{
-			return !strcmp(string0, string1);
-		}
-	};
-}
-
-#endif
-
-
-#ifndef NV_FOUNDATION_HASHINTERNALS
-#define NV_FOUNDATION_HASHINTERNALS
-
-
-#pragma warning(push)
-#pragma warning(disable:4127 4512) // disable the 'conditoinal expression is constant' warning message
-
-namespace CONVEX_DECOMPOSITION
-{
-	namespace Internal
-	{
-		template <class Entry,
-				  class Key,
-				  class HashFn,
-				  class GetKey,
-				  class Allocator,
-				  bool compacting>
-		class HashBase
-		{
-		public:
-			typedef Entry EntryType;
-
-			HashBase(NxU32 initialTableSize = 64, float loadFactor = 0.75f):
-			mLoadFactor(loadFactor),
-				mFreeList((NxU32)EOL),
-				mTimestamp(0),
-				mSize(0),
-				mEntries(Allocator(NV_DEBUG_EXP("hashBaseEntries"))),
-				mNext(Allocator(NV_DEBUG_EXP("hashBaseNext"))),
-				mHash(Allocator(NV_DEBUG_EXP("hashBaseHash")))
-			{
-				if(initialTableSize)
-					reserveInternal(initialTableSize);
-			}
-
-			~HashBase()
-			{
-				for(NxU32 i = 0;i<mHash.size();i++)
-				{				
-					for(NxU32 j = mHash[i]; j != EOL; j = mNext[j])
-						mEntries[j].~Entry();
-				}
-			}
-
-			static const int EOL = 0xffffffff;
-
-			NX_INLINE Entry *create(const Key &k, bool &exists)
-			{
-				NxU32 h=0;
-				if(mHash.size())
-				{
-					h = hash(k);
-					NxU32 index = mHash[h];
-					while(index!=EOL && !HashFn()(GetKey()(mEntries[index]), k))
-						index = mNext[index];
-					exists = index!=EOL;
-					if(exists)
-						return &mEntries[index];
-				}
-
-				if(freeListEmpty())
-				{
-					grow();
-					h = hash(k);
-				}
-
-				NxU32 entryIndex = freeListGetNext();
-
-				mNext[entryIndex] = mHash[h];
-				mHash[h] = entryIndex;
-
-				mSize++;
-				mTimestamp++;
-
-				return &mEntries[entryIndex];
-			}
-
-			NX_INLINE const Entry *find(const Key &k) const
-			{
-				if(!mHash.size())
-					return NULL;
-
-				NxU32 h = hash(k);
-				NxU32 index = mHash[h];
-				while(index!=EOL && !HashFn()(GetKey()(mEntries[index]), k))
-					index = mNext[index];
-				return index != EOL ? &mEntries[index]:0;
-			}
-
-			NX_INLINE bool erase(const Key &k)
-			{
-				if(!mHash.size())
-					return false;
-
-				NxU32 h = hash(k);
-				NxU32 *ptr = &mHash[h];
-				while(*ptr!=EOL && !HashFn()(GetKey()(mEntries[*ptr]), k))
-					ptr = &mNext[*ptr];
-
-				if(*ptr == EOL)
-					return false;
-
-				NxU32 index = *ptr;
-				*ptr = mNext[index];
-
-				mEntries[index].~Entry();
-
-				mSize--;
-				mTimestamp++;
-
-				if(compacting && index!=mSize)
-					replaceWithLast(index);
-
-				freeListAdd(index);
-
-				return true;
-			}
-
-			NX_INLINE NxU32 size() const
-			{ 
-				return mSize; 
-			}
-
-			void clear()
-			{
-				if(!mHash.size())
-					return;
-
-				for(NxU32 i = 0;i<mHash.size();i++)
-					mHash[i] = (NxU32)EOL;
-				for(NxU32 i = 0;i<mEntries.size()-1;i++)
-					mNext[i] = i+1;
-				mNext[mEntries.size()-1] = (NxU32)EOL;
-				mFreeList = 0;
-				mSize = 0;
-			}
-
-			void reserve(NxU32 size)
-			{
-				if(size>mHash.size())
-					reserveInternal(size);
-			}
-
-			NX_INLINE const Entry *getEntries() const
-			{
-				return &mEntries[0];
-			}
-
-		private:
-
-			// free list management - if we're coalescing, then we use mFreeList to hold
-			// the top of the free list and it should always be equal to size(). Otherwise,
-			// we build a free list in the next() pointers.
-
-			NX_INLINE void freeListAdd(NxU32 index)
-			{
-				if(compacting)
-				{
-					mFreeList--;
-					NX_ASSERT(mFreeList == mSize);
-				}
-				else
-				{
-					mNext[index] = mFreeList;
-					mFreeList = index;
-				}
-			}
-
-			NX_INLINE void freeListAdd(NxU32 start, NxU32 end)
-			{
-				if(!compacting)
-				{
-					for(NxU32 i = start; i<end-1; i++)	// add the new entries to the free list
-						mNext[i] = i+1;
-					mNext[end-1] = (NxU32)EOL;
-				}
-				mFreeList = start;
-			}
-
-			NX_INLINE NxU32 freeListGetNext()
-			{
-				NX_ASSERT(!freeListEmpty());
-				if(compacting)
-				{
-					NX_ASSERT(mFreeList == mSize);
-					return mFreeList++;
-				}
-				else
-				{
-					NxU32 entryIndex = mFreeList;
-					mFreeList = mNext[mFreeList];
-					return entryIndex;
-				}
-			}
-
-			NX_INLINE bool freeListEmpty()
-			{
-				if(compacting)
-					return mSize == mEntries.size();
-				else
-					return mFreeList == EOL;
-			}
-
-			NX_INLINE void replaceWithLast(NxU32 index)
-			{
-				new(&mEntries[index])Entry(mEntries[mSize]);
-				mEntries[mSize].~Entry();
-				mNext[index] = mNext[mSize];
-
-				NxU32 h = hash(GetKey()(mEntries[index]));
-				NxU32 *ptr;
-				for(ptr = &mHash[h]; *ptr!=mSize; ptr = &mNext[*ptr])
-					NX_ASSERT(*ptr!=EOL);
-				*ptr = index;
-			}
-
-
-			NX_INLINE NxU32 hash(const Key &k) const
-			{
-				return HashFn()(k)&(mHash.size()-1);
-			}
-
-			void reserveInternal(NxU32 size)
-			{
-				size = nextPowerOfTwo(size);
-				// resize the hash and reset
-				mHash.resize(size);
-				for(NxU32 i=0;i<mHash.size();i++)
-					mHash[i] = (NxU32)EOL;
-
-				NX_ASSERT(!(mHash.size()&(mHash.size()-1)));
-
-				NxU32 oldSize = mEntries.size();
-				NxU32 newSize = NxU32(float(mHash.size())*mLoadFactor);
-
-				mEntries.resize(newSize);
-				mNext.resize(newSize);
-
-				freeListAdd(oldSize,newSize);
-
-				// rehash all the existing entries
-				for(NxU32 i=0;i<oldSize;i++)
-				{
-					NxU32 h = hash(GetKey()(mEntries[i]));
-					mNext[i] = mHash[h];
-					mHash[h] = i;
-				}
-			}
-
-			void grow()
-			{
-				NX_ASSERT(mFreeList == EOL || compacting && mSize == mEntries.size());
-
-				NxU32 size = mHash.size()==0 ? 16 : mHash.size()*2;
-				reserve(size);
-			}
-
-
-			Array<Entry, Allocator>	mEntries;
-			Array<NxU32, Allocator>	mNext;
-			Array<NxU32, Allocator>	mHash;
-			float					mLoadFactor;
-			NxU32					mFreeList;
-			NxU32					mTimestamp;
-			NxU32					mSize;
-
-			friend class Iter;
-
-		public:
-			class Iter
-			{
-			public:
-				NX_INLINE Iter(HashBase &b): mBase(b), mTimestamp(b.mTimestamp), mBucket(0), mEntry((NxU32)b.EOL)
-				{
-					if(mBase.mEntries.size()>0)
-					{
-						mEntry = mBase.mHash[0];
-						skip();
-					}
-				}
-
-				NX_INLINE void check()				{ NX_ASSERT(mTimestamp == mBase.mTimestamp);	}
-				NX_INLINE Entry operator*()			{ check(); return mBase.mEntries[mEntry];		}
-				NX_INLINE Entry *operator->()		{ check(); return &mBase.mEntries[mEntry];		}
-				NX_INLINE Iter operator++()			{ check(); advance(); return *this;				}
-				NX_INLINE Iter operator++(int)		{ check(); Iter i = *this; advance(); return i;	}
-				NX_INLINE bool done()				{ check(); return mEntry == mBase.EOL;			}
-
-			private:
-				NX_INLINE void advance()			{	mEntry = mBase.mNext[mEntry]; skip();		}
-				NX_INLINE void skip()
-				{
-					while(mEntry==mBase.EOL) 
-					{ 
-						if(++mBucket == mBase.mHash.size())
-							break;
-						mEntry = mBase.mHash[mBucket];
-					}
-				}
-
-				NxU32 mBucket;
-				NxU32 mEntry;
-				NxU32 mTimestamp;
-				HashBase &mBase;
-			};
-		};
-
-		template <class Key, 
-				  class HashFn, 
-				  class Allocator = Allocator,
-				  bool Coalesced = false>
-		class HashSetBase
-		{ 
-		public:
-			struct GetKey { NX_INLINE const Key &operator()(const Key &e) {	return e; }	};
-
-			typedef HashBase<Key, Key, HashFn, GetKey, Allocator, Coalesced> BaseMap;
-			typedef typename BaseMap::Iter Iterator;
-
-			HashSetBase(NxU32 initialTableSize = 64, 
-						float loadFactor = 0.75f):	mBase(initialTableSize,loadFactor)	{}
-
-			bool insert(const Key &k)
-			{
-				bool exists;
-				Key *e = mBase.create(k,exists);
-				if(!exists)
-					new(e)Key(k);
-				return !exists;
-			}
-
-			NX_INLINE bool		contains(const Key &k)	const	{	return mBase.find(k)!=0;		}
-			NX_INLINE bool		erase(const Key &k)				{	return mBase.erase(k);			}
-			NX_INLINE NxU32		size()					const	{	return mBase.size();			}
-			NX_INLINE void		reserve(NxU32 size)				{	mBase.reserve(size);			}
-			NX_INLINE void		clear()							{	mBase.clear();					}
-		protected:
-			BaseMap mBase;
-
-		};
-
-		template <class Key, 
-			  class Value,
-			  class HashFn, 
-			  class Allocator = Allocator >
-
-		class HashMapBase
-		{ 
-		public:
-			typedef Pair<const Key,Value> Entry;
-			struct GetKey { NX_INLINE const Key &operator()(const Entry &e) {	return e.first; }	};
-			typedef HashBase<Pair<const Key,Value>, Key, HashFn, GetKey, Allocator, true> BaseMap;
-			typedef typename BaseMap::Iter Iterator;
-
-			HashMapBase(NxU32 initialTableSize = 64, float loadFactor = 0.75f):	mBase(initialTableSize,loadFactor)	{}
-
-			bool insert(const Key &k, const Value &v)
-			{
-				bool exists;
-				Entry *e = mBase.create(k,exists);
-				if(!exists)
-					new(e)Entry(k,v);
-				return !exists;
-			}
-
-			Value &operator [](const Key &k)
-			{
-				bool exists;
-				Entry *e = mBase.create(k, exists);
-				if(!exists)
-					new(e)Entry(k,Value());
-		
-				return e->second;
-			}
-
-			NX_INLINE const Entry *	find(const Key &k)		const	{	return mBase.find(k);			}
-			NX_INLINE bool			erase(const Key &k)				{	return mBase.erase(k);			}
-			NX_INLINE NxU32			size()					const	{	return mBase.size();			}
-			NX_INLINE Iterator		getIterator()					{	return Iterator(mBase);			}
-			NX_INLINE void			reserve(NxU32 size)				{	mBase.reserve(size);			}
-			NX_INLINE void			clear()							{	mBase.clear();					}
-
-		protected:
-			BaseMap mBase;
-		};
-
-	}
-}
-
-#pragma warning(pop)
-
-#endif
-
-#ifndef NV_FOUNDATION_HASHMAP
-#define NV_FOUNDATION_HASHMAP
-
-
-// TODO: make this doxy-format
-//
-// This header defines two hash maps. Hash maps
-// * support custom initial table sizes (rounded up internally to power-of-2)
-// * support custom static allocator objects
-// * auto-resize, based on a load factor (i.e. a 64-entry .75 load factor hash will resize 
-//                                        when the 49th element is inserted)
-// * are based on open hashing
-// * have O(1) contains, erase
-//
-// Maps have STL-like copying semantics, and properly initialize and destruct copies of objects
-// 
-// There are two forms of map: coalesced and uncoalesced. Coalesced maps keep the entries in the
-// initial segment of an array, so are fast to iterate over; however deletion is approximately
-// twice as expensive.
-//
-// HashMap<T>:
-//		bool			insert(const Key &k, const Value &v)	O(1) amortized (exponential resize policy)
-//		Value &			operator[](const Key &k)				O(1) for existing objects, else O(1) amortized
-//		const Entry *	find(const Key &k);						O(1)
-//		bool			erase(const T &k);						O(1)
-//		NxU32			size();									constant
-//		void			reserve(NxU32 size);					O(MAX(currentOccupancy,size))
-//		void			clear();								O(currentOccupancy) (with zero constant for objects without destructors) 
-//      Iterator		getIterator();
-//
-// operator[] creates an entry if one does not exist, initializing with the default constructor.
-// CoalescedHashMap<T> does not support getInterator, but instead supports
-// 		const Key *getEntries();
-//
-// Use of iterators:
-// 
-// for(HashMap::Iterator iter = test.getIterator(); !iter.done(); ++iter)
-//			myFunction(iter->first, iter->second);
-
-namespace CONVEX_DECOMPOSITION
-{
-	template <class Key,
-			  class Value,
-			  class HashFn = Hash<Key>,
-			  class Allocator = Allocator >
-	class HashMap: public Internal::HashMapBase<Key, Value, HashFn, Allocator>
-	{
-	public:
-
-		typedef Internal::HashMapBase<Key, Value, HashFn, Allocator> HashMapBase;
-		typedef typename HashMapBase::Iterator Iterator;
-
-		HashMap(NxU32 initialTableSize = 64, float loadFactor = 0.75f):	HashMapBase(initialTableSize,loadFactor) {}
-		Iterator getIterator() { return Iterator(HashMapBase::mBase); }
-	};
-
-	template <class Key, 
-			  class Value,
-			  class HashFn = Hash<Key>, 
-			  class Allocator = Allocator >
-	class CoalescedHashMap: public Internal::HashMapBase<Key, Value, HashFn, Allocator>
-	{
-		typedef Internal::HashMapBase<Key, Value, HashFn, Allocator> HashMapBase;
-
-		CoalescedHashMap(NxU32 initialTableSize = 64, float loadFactor = 0.75f): HashMapBase(initialTableSize,loadFactor){}
-		const Key *getEntries() const { return HashMapBase::mBase.getEntries(); }
-	};
-
-}
-#endif
-
-#endif

+ 0 - 783
Engine/lib/convexDecomp/NvMeshIslandGeneration.cpp

@@ -1,783 +0,0 @@
-/*
-
-NvMeshIslandGeneration.cpp : This code snippet walks the toplogy of a triangle mesh and detects the set of unique connected 'mesh islands'
-
-*/
-
-/*!
-**
-** Copyright (c) 2009 by John W. Ratcliff mailto:[email protected]
-**
-** Portions of this source has been released with the PhysXViewer application, as well as
-** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
-**
-** If you find this code useful or you are feeling particularily generous I would
-** ask that you please go to http://www.amillionpixels.us and make a donation
-** to Troy DeMolay.
-**
-** DeMolay is a youth group for young men between the ages of 12 and 21.
-** It teaches strong moral principles, as well as leadership skills and
-** public speaking.  The donations page uses the 'pay for pixels' paradigm
-** where, in this case, a pixel is only a single penny.  Donations can be
-** made for as small as $4 or as high as a $100 block.  Each person who donates
-** will get a link to their own site as well as acknowledgement on the
-** donations blog located here http://www.amillionpixels.blogspot.com/
-**
-** If you wish to contact me you can use the following methods:
-**
-** Skype ID: jratcliff63367
-** Yahoo: jratcliff63367
-** AOL: jratcliff1961
-** email: [email protected]
-**
-**
-** The MIT license:
-**
-** Permission is hereby granted, free of charge, to any person obtaining a copy
-** of this software and associated documentation files (the "Software"), to deal
-** in the Software without restriction, including without limitation the rights
-** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-** copies of the Software, and to permit persons to whom the Software is furnished
-** to do so, subject to the following conditions:
-**
-** The above copyright notice and this permission notice shall be included in all
-** copies or substantial portions of the Software.
-
-** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
-#pragma warning(disable:4100 4288)
-#include "NvMeshIslandGeneration.h"
-#include "NvFloatMath.h"
-#include "NvHashMap.h"
-
-namespace CONVEX_DECOMPOSITION
-{
-
-typedef CONVEX_DECOMPOSITION::Array< NxU32 > NxU32Vector;
-
-class Edge;
-class Island;
-
-class AABB
-{
-public:
-  NxF32 mMin[3];
-  NxF32 mMax[3];
-};
-
-class Triangle
-{
-public:
-  Triangle(void)
-  {
-    mConsumed = false;
-    mIsland   = 0;
-    mHandle   = 0;
-    mId       = 0;
-  }
-
-  void minmax(const NxF32 *p,AABB &box)
-  {
-    if ( p[0] < box.mMin[0] ) box.mMin[0] = p[0];
-    if ( p[1] < box.mMin[1] ) box.mMin[1] = p[1];
-    if ( p[2] < box.mMin[2] ) box.mMin[2] = p[2];
-
-    if ( p[0] > box.mMax[0] ) box.mMax[0] = p[0];
-    if ( p[1] > box.mMax[1] ) box.mMax[1] = p[1];
-    if ( p[2] > box.mMax[2] ) box.mMax[2] = p[2];
-  }
-
-  void minmax(const NxF64 *p,AABB &box)
-  {
-    if ( (NxF32)p[0] < box.mMin[0] ) box.mMin[0] = (NxF32)p[0];
-    if ( (NxF32)p[1] < box.mMin[1] ) box.mMin[1] = (NxF32)p[1];
-    if ( (NxF32)p[2] < box.mMin[2] ) box.mMin[2] = (NxF32)p[2];
-    if ( (NxF32)p[0] > box.mMax[0] ) box.mMax[0] = (NxF32)p[0];
-    if ( (NxF32)p[1] > box.mMax[1] ) box.mMax[1] = (NxF32)p[1];
-    if ( (NxF32)p[2] > box.mMax[2] ) box.mMax[2] = (NxF32)p[2];
-  }
-
-  void buildBox(const NxF32 *vertices_f,const NxF64 *vertices_d,NxU32 id);
-
-  void render(NxU32 color)
-  {
-//    gRenderDebug->DebugBound(&mBox.mMin[0],&mBox.mMax[0],color,60.0f);
-  }
-
-  void getTriangle(NxF32 *tri,const NxF32 *vertices_f,const NxF64 *vertices_d);
-
-  NxU32    mHandle;
-  bool      mConsumed;
-  Edge     *mEdges[3];
-  Island   *mIsland;   // identifies which island it is a member of
-  unsigned short  mId;
-  AABB      mBox;
-};
-
-
-class Edge
-{
-public:
-  Edge(void)
-  {
-    mI1 = 0;
-    mI2 = 0;
-    mHash = 0;
-    mNext = 0;
-    mPrevious = 0;
-    mParent = 0;
-    mNextTriangleEdge = 0;
-  }
-
-  void init(NxU32 i1,NxU32 i2,Triangle *parent)
-  {
-    assert( i1 < 65536 );
-    assert( i2 < 65536 );
-
-    mI1 = i1;
-    mI2 = i2;
-    mHash        = (i2<<16)|i1;
-    mReverseHash = (i1<<16)|i2;
-    mNext = 0;
-    mPrevious = 0;
-    mParent = parent;
-  }
-
-  NxU32  mI1;
-  NxU32  mI2;
-  NxU32  mHash;
-  NxU32  mReverseHash;
-
-  Edge     *mNext;
-  Edge     *mPrevious;
-  Edge     *mNextTriangleEdge;
-  Triangle *mParent;
-};
-
-typedef CONVEX_DECOMPOSITION::HashMap< NxU32, Edge * > EdgeHashMap;
-typedef CONVEX_DECOMPOSITION::Array< Triangle * > TriangleVector;
-
-class EdgeCheck
-{
-public:
-  EdgeCheck(Triangle *t,Edge *e)
-  {
-    mTriangle = t;
-    mEdge     = e;
-  }
-
-  Triangle  *mTriangle;
-  Edge      *mEdge;
-};
-
-typedef CONVEX_DECOMPOSITION::Array< EdgeCheck > EdgeCheckQueue;
-
-class Island 
-{
-public:
-  Island(Triangle *t,Triangle *root)
-  {
-    mVerticesFloat = 0;
-    mVerticesDouble = 0;
-    t->mIsland = this;
-    mTriangles.pushBack(t);
-    mCoplanar = false;
-    fm_initMinMax(mMin,mMax);
-  }
-
-  void add(Triangle *t,Triangle *root)
-  {
-    t->mIsland = this;
-    mTriangles.pushBack(t);
-  }
-
-  void merge(Island &isl)
-  {
-    TriangleVector::Iterator i;
-    for (i=isl.mTriangles.begin(); i!=isl.mTriangles.end(); ++i)
-    {
-      Triangle *t = (*i);
-      mTriangles.pushBack(t);
-    }
-    isl.mTriangles.clear();
-  }
-
-  bool isTouching(Island *isl,const NxF32 *vertices_f,const NxF64 *vertices_d)
-  {
-    bool ret = false;
-
-    mVerticesFloat = vertices_f;
-    mVerticesDouble = vertices_d;
-
-    if ( fm_intersectAABB(mMin,mMax,isl->mMin,isl->mMax) ) // if the two islands has an intersecting AABB
-    {
-      // todo..
-    }
-
-
-    return ret;
-  }
-
-
-  void SAP_DeletePair(const void* object0, const void* object1, void* user_data, void* pair_user_data)
-  {
-  }
-
-  void render(NxU32 color)
-  {
-//    gRenderDebug->DebugBound(mMin,mMax,color,60.0f);
-    TriangleVector::Iterator i;
-    for (i=mTriangles.begin(); i!=mTriangles.end(); ++i)
-    {
-      Triangle *t = (*i);
-      t->render(color);
-    }
-  }
-
-
-  const NxF64   *mVerticesDouble;
-  const NxF32    *mVerticesFloat;
-
-  NxF32           mMin[3];
-  NxF32           mMax[3];
-  bool            mCoplanar; // marked as co-planar..
-  TriangleVector  mTriangles;
-};
-
-
-void Triangle::getTriangle(NxF32 *tri,const NxF32 *vertices_f,const NxF64 *vertices_d)
-{
-  NxU32 i1 = mEdges[0]->mI1;
-  NxU32 i2 = mEdges[1]->mI1;
-  NxU32 i3 = mEdges[2]->mI1;
-  if ( vertices_f )
-  {
-    const NxF32 *p1 = &vertices_f[i1*3];
-    const NxF32 *p2 = &vertices_f[i2*3];
-    const NxF32 *p3 = &vertices_f[i3*3];
-    fm_copy3(p1,tri);
-    fm_copy3(p2,tri+3);
-    fm_copy3(p3,tri+6);
-  }
-  else
-  {
-    const NxF64 *p1 = &vertices_d[i1*3];
-    const NxF64 *p2 = &vertices_d[i2*3];
-    const NxF64 *p3 = &vertices_d[i3*3];
-    fm_doubleToFloat3(p1,tri);
-    fm_doubleToFloat3(p2,tri+3);
-    fm_doubleToFloat3(p3,tri+6);
-  }
-}
-
-void Triangle::buildBox(const NxF32 *vertices_f,const NxF64 *vertices_d,NxU32 id)
-{
-  mId = (unsigned short)id;
-  NxU32 i1 = mEdges[0]->mI1;
-  NxU32 i2 = mEdges[1]->mI1;
-  NxU32 i3 = mEdges[2]->mI1;
-
-  if ( vertices_f )
-  {
-    const NxF32 *p1 = &vertices_f[i1*3];
-    const NxF32 *p2 = &vertices_f[i2*3];
-    const NxF32 *p3 = &vertices_f[i3*3];
-    mBox.mMin[0] = p1[0];
-    mBox.mMin[1] = p1[1];
-    mBox.mMin[2] = p1[2];
-    mBox.mMax[0] = p1[0];
-    mBox.mMax[1] = p1[1];
-    mBox.mMax[2] = p1[2];
-    minmax(p2,mBox);
-    minmax(p3,mBox);
-  }
-  else
-  {
-    const NxF64 *p1 = &vertices_d[i1*3];
-    const NxF64 *p2 = &vertices_d[i2*3];
-    const NxF64 *p3 = &vertices_d[i3*3];
-    mBox.mMin[0] = (NxF32)p1[0];
-    mBox.mMin[1] = (NxF32)p1[1];
-    mBox.mMin[2] = (NxF32)p1[2];
-    mBox.mMax[0] = (NxF32)p1[0];
-    mBox.mMax[1] = (NxF32)p1[1];
-    mBox.mMax[2] = (NxF32)p1[2];
-    minmax(p2,mBox);
-    minmax(p3,mBox);
-  }
-
-  assert(mIsland);
-  if ( mIsland )
-  {
-    if ( mBox.mMin[0] < mIsland->mMin[0] ) mIsland->mMin[0] = mBox.mMin[0];
-    if ( mBox.mMin[1] < mIsland->mMin[1] ) mIsland->mMin[1] = mBox.mMin[1];
-    if ( mBox.mMin[2] < mIsland->mMin[2] ) mIsland->mMin[2] = mBox.mMin[2];
-
-    if ( mBox.mMax[0] > mIsland->mMax[0] ) mIsland->mMax[0] = mBox.mMax[0];
-    if ( mBox.mMax[1] > mIsland->mMax[1] ) mIsland->mMax[1] = mBox.mMax[1];
-    if ( mBox.mMax[2] > mIsland->mMax[2] ) mIsland->mMax[2] = mBox.mMax[2];
-  }
-
-}
-
-
-typedef CONVEX_DECOMPOSITION::Array< Island * > IslandVector;
-
-class MyMeshIslandGeneration : public MeshIslandGeneration
-{
-public:
-  MyMeshIslandGeneration(void)
-  {
-    mTriangles = 0;
-    mEdges     = 0;
-    mVerticesDouble = 0;
-    mVerticesFloat  = 0;
-  }
-
-  ~MyMeshIslandGeneration(void)
-  {
-    reset();
-  }
-
-  void reset(void)
-  {
-    delete []mTriangles;
-    delete []mEdges;
-    mTriangles = 0;
-    mEdges = 0;
-    mTriangleEdges.clear();
-    IslandVector::Iterator i;
-    for (i=mIslands.begin(); i!=mIslands.end(); ++i)
-    {
-      Island *_i = (*i);
-      delete _i;
-    }
-    mIslands.clear();
-  }
-
-  NxU32 islandGenerate(NxU32 tcount,const NxU32 *indices,const NxF64 *vertices)
-  {
-    mVerticesDouble = vertices;
-    mVerticesFloat  = 0;
-    return islandGenerate(tcount,indices);
-  }
-
-  NxU32 islandGenerate(NxU32 tcount,const NxU32 *indices,const NxF32 *vertices)
-  {
-    mVerticesDouble = 0;
-    mVerticesFloat  = vertices;
-    return islandGenerate(tcount,indices);
-  }
-
-  NxU32 islandGenerate(NxU32 tcount,const NxU32 *indices)
-  {
-    NxU32 ret = 0;
-
-    reset();
-
-    mTcount = tcount;
-    mTriangles = new Triangle[tcount];
-    mEdges     = new Edge[tcount*3];
-    Edge *e = mEdges;
-
-    for (NxU32 i=0; i<tcount; i++)
-    {
-      Triangle &t = mTriangles[i];
-
-      NxU32 i1 = *indices++;
-      NxU32 i2 = *indices++;
-      NxU32 i3 = *indices++;
-
-      t.mEdges[0] = e;
-      t.mEdges[1] = e+1;
-      t.mEdges[2] = e+2;
-
-      e = addEdge(e,&t,i1,i2);
-      e = addEdge(e,&t,i2,i3);
-      e = addEdge(e,&t,i3,i1);
-
-    }
-
-    // while there are still edges to process...
-    while ( mTriangleEdges.size() != 0 )
-    {
-
-      EdgeHashMap::Iterator iter = mTriangleEdges.getIterator();
-
-      Triangle *t = iter->second->mParent;
-
-      Island *i = new Island(t,mTriangles);  // the initial triangle...
-      removeTriangle(t); // remove this triangle from the triangle-edges hashmap
-
-      mIslands.pushBack(i);
-
-      // now keep adding to this island until we can no longer walk any shared edges..
-      addEdgeCheck(t,t->mEdges[0]);
-      addEdgeCheck(t,t->mEdges[1]);
-      addEdgeCheck(t,t->mEdges[2]);
-
-      while ( !mEdgeCheckQueue.empty() )
-      {
-
-        EdgeCheck e = mEdgeCheckQueue.popBack();
-
-        // Process all triangles which share this edge
-        Edge *edge = locateSharedEdge(e.mEdge);
-
-        while ( edge )
-        {
-          Triangle *t = edge->mParent;
-          assert(!t->mConsumed);
-          i->add(t,mTriangles);
-          removeTriangle(t); // remove this triangle from the triangle-edges hashmap
-
-          // now keep adding to this island until we can no longer walk any shared edges..
-
-          if ( edge != t->mEdges[0] )
-          {
-            addEdgeCheck(t,t->mEdges[0]);
-          }
-
-          if ( edge != t->mEdges[1] )
-          {
-            addEdgeCheck(t,t->mEdges[1]);
-          }
-
-          if ( edge != t->mEdges[2] )
-          {
-            addEdgeCheck(t,t->mEdges[2]);
-          }
-
-          edge = locateSharedEdge(e.mEdge); // keep going until all shared edges have been processed!
-        }
-
-      }
-    }
-
-    ret = (NxU32)mIslands.size();
-
-    return ret;
-  }
-
-  NxU32 *   getIsland(NxU32 index,NxU32 &otcount)
-  {
-    NxU32 *ret  = 0;
-
-    mIndices.clear();
-    if ( index < mIslands.size() )
-    {
-      Island *i = mIslands[index];
-      otcount = (NxU32)i->mTriangles.size();
-      TriangleVector::Iterator j;
-      for (j=i->mTriangles.begin(); j!=i->mTriangles.end(); ++j)
-      {
-        Triangle *t = (*j);
-        mIndices.pushBack(t->mEdges[0]->mI1);
-        mIndices.pushBack(t->mEdges[1]->mI1);
-        mIndices.pushBack(t->mEdges[2]->mI1);
-      }
-      ret = &mIndices[0];
-    }
-
-    return ret;
-  }
-
-private:
-
-  void removeTriangle(Triangle *t)
-  {
-    t->mConsumed = true;
-
-    removeEdge(t->mEdges[0]);
-    removeEdge(t->mEdges[1]);
-    removeEdge(t->mEdges[2]);
-
-  }
-
-
-  Edge * locateSharedEdge(Edge *e)
-  {
-    Edge *ret = 0;
-
-    const EdgeHashMap::Entry *found = mTriangleEdges.find( e->mReverseHash );
-    if ( found != NULL )
-    {
-      ret = (*found).second;
-      assert( ret->mHash == e->mReverseHash );
-    }
-    return ret;
-  }
-
-  void removeEdge(Edge *e)
-  {
-    const EdgeHashMap::Entry *found = mTriangleEdges.find( e->mHash );
-
-    if ( found != NULL )
-    {
-      Edge *prev = 0;
-      Edge *scan = (*found).second;
-      while ( scan && scan != e )
-      {
-        prev = scan;
-        scan = scan->mNextTriangleEdge;
-      }
-
-      if ( scan )
-      {
-        if ( prev == 0 )
-        {
-          if ( scan->mNextTriangleEdge )
-          {
-            mTriangleEdges.erase(e->mHash);
-            mTriangleEdges[e->mHash] = scan->mNextTriangleEdge;
-          }
-          else
-          {
-            mTriangleEdges.erase(e->mHash); // no more polygons have an edge here
-          }
-        }
-        else
-        {
-          prev->mNextTriangleEdge = scan->mNextTriangleEdge;
-        }
-      }
-      else
-      {
-        assert(0);
-      }
-    }
-    else
-    {
-      assert(0); // impossible!
-    }
-  }
-
-
-  Edge * addEdge(Edge *e,Triangle *t,NxU32 i1,NxU32 i2)
-  {
-
-    e->init(i1,i2,t);
-
-    const EdgeHashMap::Entry *found = mTriangleEdges.find(e->mHash);
-    if ( found == NULL )
-    {
-      mTriangleEdges[ e->mHash ] = e;
-    }
-    else
-    {
-      Edge *pn = (*found).second;
-      e->mNextTriangleEdge = pn;
-      mTriangleEdges.erase(e->mHash);
-      mTriangleEdges[e->mHash] = e;
-    }
-
-    e++;
-
-    return e;
-  }
-
-  void addEdgeCheck(Triangle *t,Edge *e)
-  {
-    EdgeCheck ec(t,e);
-    mEdgeCheckQueue.pushBack(ec);
-  }
-
-  NxU32 mergeCoplanarIslands(const NxF32 *vertices)
-  {
-    mVerticesFloat = vertices;
-    mVerticesDouble = 0;
-    return mergeCoplanarIslands();
-  }
-
-  NxU32 mergeCoplanarIslands(const NxF64 *vertices)
-  {
-    mVerticesDouble = vertices;
-    mVerticesFloat = 0;
-    return mergeCoplanarIslands();
-  }
-
-  // this island needs to be merged
-  void mergeTouching(Island *isl)
-  {
-    Island *touching = 0;
-
-    IslandVector::Iterator i;
-    for (i=mIslands.begin(); i!=mIslands.end(); ++i)
-    {
-      Island *_i = (*i);
-      if ( !_i->mCoplanar ) // can't merge with coplanar islands!
-      {
-        if ( _i->isTouching(isl,mVerticesFloat,mVerticesDouble) )
-        {
-          touching = _i;
-        }
-      }
-    }
-  }
-
-  NxU32 mergeCoplanarIslands(void)
-  {
-    NxU32  ret = 0;
-
-    if ( !mIslands.empty() )
-    {
-
-
-      NxU32  cp_count  = 0;
-      NxU32  npc_count = 0;
-
-      NxU32  count = (NxU32)mIslands.size();
-
-      for (NxU32 i=0; i<count; i++)
-      {
-
-        NxU32 otcount;
-        const NxU32 *oindices = getIsland(i,otcount);
-
-        if ( otcount )
-        {
-
-          bool isCoplanar;
-
-          if ( mVerticesFloat )
-            isCoplanar = fm_isMeshCoplanar(otcount, oindices, mVerticesFloat, true);
-          else
-            isCoplanar = fm_isMeshCoplanar(otcount, oindices, mVerticesDouble, true);
-
-          if ( isCoplanar )
-          {
-            Island *isl = mIslands[i];
-            isl->mCoplanar = true;
-            cp_count++;
-          }
-          else
-          {
-            npc_count++;
-          }
-        }
-        else
-        {
-          assert(0);
-        }
-      }
-
-      if ( cp_count )
-      {
-        if ( npc_count == 0 ) // all islands are co-planar!
-        {
-          IslandVector temp = mIslands;
-          mIslands.clear();
-          Island *isl = mIslands[0];
-          mIslands.pushBack(isl);
-          for (NxU32 i=1; i<cp_count; i++)
-          {
-            Island *_i = mIslands[i];
-            isl->merge(*_i);
-            delete _i;
-          }
-        }
-        else
-        {
-
-
-          Triangle *t = mTriangles;
-          for (NxU32 i=0; i<mTcount; i++)
-          {
-            t->buildBox(mVerticesFloat,mVerticesDouble,i);
-            t++;
-          }
-
-          IslandVector::Iterator i;
-          for (i=mIslands.begin(); i!=mIslands.end(); ++i)
-          {
-            Island *isl = (*i);
-
-            NxU32 color = 0x00FF00;
-
-            if ( isl->mCoplanar )
-            {
-              color = 0xFFFF00;
-            }
-
-            mergeTouching(isl);
-
-          }
-
-          IslandVector temp = mIslands;
-          mIslands.clear();
-          for (i=temp.begin(); i!=temp.end(); i++)
-          {
-            Island *isl = (*i);
-            if ( isl->mCoplanar )
-            {
-              delete isl; // kill it
-            }
-            else
-            {
-              mIslands.pushBack(isl);
-            }
-          }
-          ret = (NxU32)mIslands.size();
-        }
-      }
-      else
-      {
-        ret = npc_count;
-      }
-    }
-
-
-    return ret;
-  }
-
-  NxU32 mergeTouchingIslands(const NxF32 *vertices)
-  {
-    NxU32 ret = 0;
-
-    return ret;
-  }
-
-  NxU32 mergeTouchingIslands(const NxF64 *vertices)
-  {
-    NxU32 ret = 0;
-
-    return ret;
-  }
-
-  NxU32           mTcount;
-  Triangle        *mTriangles;
-  Edge            *mEdges;
-  EdgeHashMap      mTriangleEdges;
-  IslandVector     mIslands;
-  EdgeCheckQueue   mEdgeCheckQueue;
-  const NxF64    *mVerticesDouble;
-  const NxF32     *mVerticesFloat;
-  NxU32Vector     mIndices;
-};
-
-
-MeshIslandGeneration * createMeshIslandGeneration(void)
-{
-  MyMeshIslandGeneration *mig = new MyMeshIslandGeneration;
-  return static_cast< MeshIslandGeneration *>(mig);
-}
-
-void                   releaseMeshIslandGeneration(MeshIslandGeneration *cm)
-{
-  MyMeshIslandGeneration *mig = static_cast< MyMeshIslandGeneration *>(cm);
-  delete mig;
-}
-
-}; // end of namespace
-

+ 0 - 91
Engine/lib/convexDecomp/NvMeshIslandGeneration.h

@@ -1,91 +0,0 @@
-#ifndef MESH_ISLAND_GENERATION_H
-
-#define MESH_ISLAND_GENERATION_H
-
-/*
-
-NvMeshIslandGeneration.h : This code snippet walks the toplogy of a triangle mesh and detects the set of unique connected 'mesh islands'
-
-*/
-
-
-#include "NvUserMemAlloc.h"
-
-/*!
-**
-** Copyright (c) 2009 by John W. Ratcliff mailto:[email protected]
-**
-** Portions of this source has been released with the PhysXViewer application, as well as
-** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
-**
-** If you find this code useful or you are feeling particularily generous I would
-** ask that you please go to http://www.amillionpixels.us and make a donation
-** to Troy DeMolay.
-**
-** DeMolay is a youth group for young men between the ages of 12 and 21.
-** It teaches strong moral principles, as well as leadership skills and
-** public speaking.  The donations page uses the 'pay for pixels' paradigm
-** where, in this case, a pixel is only a single penny.  Donations can be
-** made for as small as $4 or as high as a $100 block.  Each person who donates
-** will get a link to their own site as well as acknowledgement on the
-** donations blog located here http://www.amillionpixels.blogspot.com/
-**
-** If you wish to contact me you can use the following methods:
-**
-** Skype ID: jratcliff63367
-** Yahoo: jratcliff63367
-** AOL: jratcliff1961
-** email: [email protected]
-**
-**
-** The MIT license:
-**
-** Permission is hereby granted, free of charge, to any person obtaining a copy
-** of this software and associated documentation files (the "Software"), to deal
-** in the Software without restriction, including without limitation the rights
-** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-** copies of the Software, and to permit persons to whom the Software is furnished
-** to do so, subject to the following conditions:
-**
-** The above copyright notice and this permission notice shall be included in all
-** copies or substantial portions of the Software.
-
-** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-namespace CONVEX_DECOMPOSITION
-{
-
-class MeshIslandGeneration
-{
-public:
-
-  virtual NxU32 islandGenerate(NxU32 tcount,const NxU32 *indices,const NxF32 *vertices) = 0;
-  virtual NxU32 islandGenerate(NxU32 tcount,const NxU32 *indices,const NxF64 *vertices) = 0;
-
-  // sometimes island generation can produce co-planar islands.  Slivers if you will.  If you are passing these islands into a geometric system
-  // that wants to turn them into physical objects, they may not be acceptable.  In this case it may be preferable to merge the co-planar islands with
-  // other islands that it 'touches'.
-  virtual NxU32 mergeCoplanarIslands(const NxF32 *vertices) = 0;
-  virtual NxU32 mergeCoplanarIslands(const NxF64 *vertices) = 0;
-
-  virtual NxU32 mergeTouchingIslands(const NxF32 *vertices) = 0;
-  virtual NxU32 mergeTouchingIslands(const NxF64 *vertices) = 0;
-
-  virtual NxU32 *   getIsland(NxU32 index,NxU32 &tcount) = 0;
-
-
-};
-
-MeshIslandGeneration * createMeshIslandGeneration(void);
-void                   releaseMeshIslandGeneration(MeshIslandGeneration *cm);
-
-}; // end of namespace
-
-#endif

+ 0 - 153
Engine/lib/convexDecomp/NvRayCast.cpp

@@ -1,153 +0,0 @@
-/*
-
-NvRayCast.cpp : A code snippet to cast a ray against a triangle mesh. This implementation does not use any acceleration data structures.  That is a 'to do' item.
-
-*/
-/*!
-**
-** Copyright (c) 2009 by John W. Ratcliff mailto:[email protected]
-**
-** Portions of this source has been released with the PhysXViewer application, as well as
-** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
-**
-** If you find this code useful or you are feeling particularily generous I would
-** ask that you please go to http://www.amillionpixels.us and make a donation
-** to Troy DeMolay.
-**
-** DeMolay is a youth group for young men between the ages of 12 and 21.
-** It teaches strong moral principles, as well as leadership skills and
-** public speaking.  The donations page uses the 'pay for pixels' paradigm
-** where, in this case, a pixel is only a single penny.  Donations can be
-** made for as small as $4 or as high as a $100 block.  Each person who donates
-** will get a link to their own site as well as acknowledgement on the
-** donations blog located here http://www.amillionpixels.blogspot.com/
-**
-** If you wish to contact me you can use the following methods:
-**
-** Skype ID: jratcliff63367
-** Yahoo: jratcliff63367
-** AOL: jratcliff1961
-** email: [email protected]
-**
-**
-** The MIT license:
-**
-** Permission is hereby granted, free of charge, to any person obtaining a copy
-** of this software and associated documentation files (the "Software"), to deal
-** in the Software without restriction, including without limitation the rights
-** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-** copies of the Software, and to permit persons to whom the Software is furnished
-** to do so, subject to the following conditions:
-**
-** The above copyright notice and this permission notice shall be included in all
-** copies or substantial portions of the Software.
-
-** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-#include "NvRayCast.h"
-#include "NvUserMemAlloc.h"
-#include "NvFloatMath.h"
-
-#pragma warning(disable:4100)
-
-namespace CONVEX_DECOMPOSITION
-{
-
-class RayCast : public iRayCast, public Memalloc
-{
-public:
-	RayCast(const NxF32 *vertices,NxU32 tcount,const NxU32 *indices)
-	{
-		mVertices = vertices;
-		mTcount	  = tcount;
-		mIndices  = indices;
-	}
-
-	~RayCast(void)
-	{
-	}
-
-	virtual bool castRay(const NxF32 *orig,const NxF32 *dir,NxF32 *dest,NxF32 *hitNormal)
-	{
-		bool ret = false;
-
-    	NxF32	p2[3];
-
-    	const NxF32 RAY_DIST=50;
-
-    	dest[0] = p2[0] = orig[0]+ dir[0]*RAY_DIST;
-    	dest[1] = p2[1] = orig[1]+ dir[1]*RAY_DIST;
-    	dest[2] = p2[2] = orig[2]+ dir[2]*RAY_DIST;
-
-    	NxF32 nearest = 1e9;
-    	NxU32 near_face=0;
-
-
-    	for (NxU32 i=0; i<mTcount; i++)
-    	{
-    		NxU32 i1 = mIndices[i*3+0];
-    		NxU32 i2 = mIndices[i*3+1];
-    		NxU32 i3 = mIndices[i*3+2];
-
-    		const NxF32 *t1 = &mVertices[i1*3];
-    		const NxF32 *t2 = &mVertices[i2*3];
-    		const NxF32 *t3 = &mVertices[i3*3];
-
-    		NxF32 t;
-    		if ( fm_rayIntersectsTriangle(orig,dir,t1,t2,t3,t) )
-    		{
-    			if ( t < nearest )
-    			{
-    				dest[0] = orig[0]+dir[0]*t;
-    				dest[1] = orig[1]+dir[1]*t;
-    				dest[2] = orig[2]+dir[2]*t;
-    				ret = true;
-    				near_face = i;
-    				nearest = t;
-    			}
-    		}
-    	}
-    	if ( ret )
-    	{
-    		// If the nearest face we hit was back-facing, then reject this cast!
-    		NxU32 i1 = mIndices[near_face*3+0];
-    		NxU32 i2 = mIndices[near_face*3+1];
-    		NxU32 i3 = mIndices[near_face*3+2];
-
-    		const NxF32 *t1 = &mVertices[i1*3];
-    		const NxF32 *t2 = &mVertices[i2*3];
-    		const NxF32 *t3 = &mVertices[i3*3];
-
-    		fm_computePlane(t3,t2,t1,hitNormal);
-    	}
-
-		return ret;
-	}
-private:
-	const	NxF32	*mVertices;
-	NxU32			 mTcount;
-	const   NxU32	*mIndices;
-
-};
-
-
-iRayCast *createRayCast(const NxF32 *vertices,NxU32 tcount,const NxU32 *indices)
-{
-	RayCast *rc = MEMALLOC_NEW(RayCast)(vertices,tcount,indices);
-	return static_cast< iRayCast *>(rc);
-}
-
-void	  releaseRayCast(iRayCast *rc)
-{
-	RayCast *r = static_cast< RayCast *>(rc);
-	delete r;
-}
-
-};
-

+ 0 - 79
Engine/lib/convexDecomp/NvRayCast.h

@@ -1,79 +0,0 @@
-#ifndef NV_RAYCAST_H
-
-#define NV_RAYCAST_H
-
-/*
-
-NvRayCast.h : A code snippet to cast a ray against a triangle mesh. This implementation does not use any acceleration data structures.  That is a 'to do' item.
-
-*/
-
-
-#include "NvSimpleTypes.h"
-
-/*!
-**
-** Copyright (c) 2009 by John W. Ratcliff mailto:[email protected]
-**
-** Portions of this source has been released with the PhysXViewer application, as well as
-** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
-**
-** If you find this code useful or you are feeling particularily generous I would
-** ask that you please go to http://www.amillionpixels.us and make a donation
-** to Troy DeMolay.
-**
-** DeMolay is a youth group for young men between the ages of 12 and 21.
-** It teaches strong moral principles, as well as leadership skills and
-** public speaking.  The donations page uses the 'pay for pixels' paradigm
-** where, in this case, a pixel is only a single penny.  Donations can be
-** made for as small as $4 or as high as a $100 block.  Each person who donates
-** will get a link to their own site as well as acknowledgement on the
-** donations blog located here http://www.amillionpixels.blogspot.com/
-**
-** If you wish to contact me you can use the following methods:
-**
-** Skype ID: jratcliff63367
-** Yahoo: jratcliff63367
-** AOL: jratcliff1961
-** email: [email protected]
-**
-**
-** The MIT license:
-**
-** Permission is hereby granted, free of charge, to any person obtaining a copy
-** of this software and associated documentation files (the "Software"), to deal
-** in the Software without restriction, including without limitation the rights
-** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-** copies of the Software, and to permit persons to whom the Software is furnished
-** to do so, subject to the following conditions:
-**
-** The above copyright notice and this permission notice shall be included in all
-** copies or substantial portions of the Software.
-
-** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-namespace CONVEX_DECOMPOSITION
-{
-
-class iRayCast
-{
-public:
-	virtual bool castRay(const NxF32 *orig,const NxF32 *dir,NxF32 *hitPoint,NxF32 *hitNormal) = 0;
-protected:
-	virtual ~iRayCast(void) { };
-};
-
-
-iRayCast *createRayCast(const NxF32 *vertices,NxU32 tcount,const NxU32 *indices);
-void	  releaseRayCast(iRayCast *rc);
-
-};
-
-#endif

+ 0 - 713
Engine/lib/convexDecomp/NvRemoveTjunctions.cpp

@@ -1,713 +0,0 @@
-/*
-
-NvRemoveTjunctions.cpp : A code snippet to remove tjunctions from a triangle mesh.  This version is currently disabled as it appears to have a bug.
-
-*/
-
-/*!
-**
-** Copyright (c) 2009 by John W. Ratcliff mailto:[email protected]
-**
-** Portions of this source has been released with the PhysXViewer application, as well as
-** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
-**
-** If you find this code useful or you are feeling particularily generous I would
-** ask that you please go to http://www.amillionpixels.us and make a donation
-** to Troy DeMolay.
-**
-** DeMolay is a youth group for young men between the ages of 12 and 21.
-** It teaches strong moral principles, as well as leadership skills and
-** public speaking.  The donations page uses the 'pay for pixels' paradigm
-** where, in this case, a pixel is only a single penny.  Donations can be
-** made for as small as $4 or as high as a $100 block.  Each person who donates
-** will get a link to their own site as well as acknowledgement on the
-** donations blog located here http://www.amillionpixels.blogspot.com/
-**
-** If you wish to contact me you can use the following methods:
-**
-** Skype ID: jratcliff63367
-** Yahoo: jratcliff63367
-** AOL: jratcliff1961
-** email: [email protected]
-**
-**
-** The MIT license:
-**
-** Permission is hereby granted, free of charge, to any person obtaining a copy
-** of this software and associated documentation files (the "Software"), to deal
-** in the Software without restriction, including without limitation the rights
-** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-** copies of the Software, and to permit persons to whom the Software is furnished
-** to do so, subject to the following conditions:
-**
-** The above copyright notice and this permission notice shall be included in all
-** copies or substantial portions of the Software.
-
-** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#pragma warning(disable:4702)
-#pragma warning(disable:4127) //conditional expression is constant (because _HAS_EXCEPTIONS=0)
-#include <vector>
-#if defined( __APPLE__ ) || defined( __FreeBSD__)
-   #include <ext/hash_map>
-#elif LINUX
-   #include <hash_map>
-#elif _MSC_VER < 1500
-   #include <hash_map>
-#elif _MSC_VER > 1800
-   #include <unordered_map>
-#endif
-#include "NvUserMemAlloc.h"
-#include "NvHashMap.h"
-#include "NvRemoveTjunctions.h"
-#include "NvFloatMath.h"
-#ifdef LINUX
-   #include <climits>
-#endif
-
-#pragma warning(disable:4189)
-
-using namespace CONVEX_DECOMPOSITION;
-
-namespace CONVEX_DECOMPOSITION
-{
-
-class AABB
-{
-public:
-  NxF32 mMin[3];
-  NxF32 mMax[3];
-};
-
-bool gDebug=false;
-NxU32 gCount=0;
-
-typedef CONVEX_DECOMPOSITION::Array< NxU32 > NxU32Vector;
-
-class Triangle
-{
-public:
-  Triangle(void)
-  {
-    mPending = false;
-    mSplit = false;
-    mI1 = mI2 = mI3 = 0xFFFFFFFF;
-    mId = 0;
-  }
-
-  Triangle(NxU32 i1,NxU32 i2,NxU32 i3,const float *vertices,NxU32 id)
-  {
-    mPending = false;
-    init(i1,i2,i3,vertices,id);
-    mSplit = false;
-  }
-
-  void init(NxU32 i1,NxU32 i2,NxU32 i3,const float *vertices,NxU32 id)
-  {
-    mSplit = false;
-    mI1 = i1;
-    mI2 = i2;
-    mI3 = i3;
-    mId = id;
-
-    const float *p1 = &vertices[mI1*3];
-    const float *p2 = &vertices[mI2*3];
-    const float *p3 = &vertices[mI3*3];
-
-    initMinMax(p1,p2,p3);
-  }
-
-  void initMinMax(const float *p1,const float *p2,const float *p3)
-  {
-    fm_copy3(p1,mBmin);
-    fm_copy3(p1,mBmax);
-    fm_minmax(p2,mBmin,mBmax);
-    fm_minmax(p3,mBmin,mBmax);
-  }
-
-  void init(const NxU32 *idx,const float *vertices,NxU32 id)
-  {
-    mSplit = false;
-    mI1 = idx[0];
-    mI2 = idx[1];
-    mI3 = idx[2];
-    mId = id;
-
-    const float *p1 = &vertices[mI1*3];
-    const float *p2 = &vertices[mI2*3];
-    const float *p3 = &vertices[mI3*3];
-
-    initMinMax(p1,p2,p3);
-
-  }
-
-  bool intersects(const float *pos,const float *p1,const float *p2,float epsilon) const
-  {
-    bool ret = false;
-
-    float sect[3];
-    LineSegmentType type;
-
-    float dist = fm_distancePointLineSegment(pos,p1,p2,sect,type,epsilon);
-
-    if ( type == LS_MIDDLE && dist < epsilon )
-    {
-      ret = true;
-    }
-
-    return ret;
-  }
-
-  bool intersects(NxU32 i,const float *vertices,NxU32 &edge,float epsilon) const
-  {
-    bool ret = true;
-
-    const float *pos = &vertices[i*3];
-    const float *p1  = &vertices[mI1*3];
-    const float *p2  = &vertices[mI2*3];
-    const float *p3  = &vertices[mI3*3];
-    if ( intersects(pos,p1,p2,epsilon) )
-    {
-      edge = 0;
-    }
-    else if ( intersects(pos,p2,p3,epsilon) )
-    {
-      edge = 1;
-    }
-    else if ( intersects(pos,p3,p1,epsilon) )
-    {
-      edge = 2;
-    }
-    else
-    {
-      ret = false;
-    }
-    return ret;
-  }
-
-  bool intersects(const Triangle *t,const float *vertices,NxU32 &intersection_index,NxU32 &edge,float epsilon)
-  {
-    bool ret = false;
-
-    if ( fm_intersectAABB(mBmin,mBmax,t->mBmin,t->mBmax) ) // only if the AABB's of the two triangles intersect...
-    {
-
-      if ( t->intersects(mI1,vertices,edge,epsilon) )
-      {
-        intersection_index = mI1;
-        ret = true;
-      }
-
-      if ( t->intersects(mI2,vertices,edge,epsilon) )
-      {
-        intersection_index = mI2;
-        ret = true;
-      }
-
-      if ( t->intersects(mI3,vertices,edge,epsilon) )
-      {
-        intersection_index = mI3;
-        ret = true;
-      }
-
-    }
-
-    return ret;
-  }
-
-  bool    mSplit:1;
-  bool    mPending:1;
-  NxU32   mI1;
-  NxU32   mI2;
-  NxU32   mI3;
-  NxU32   mId;
-  float   mBmin[3];
-  float   mBmax[3];
-};
-
-class RtEdge
-{
-public:
-  RtEdge(void)
-  {
-    mNextEdge = 0;
-    mTriangle = 0;
-    mHash = 0;
-  }
-
-  NxU32 init(Triangle *t,NxU32 i1,NxU32 i2)
-  {
-    mTriangle = t;
-    mNextEdge = 0;
-    NX_ASSERT( i1 < 65536 );
-    NX_ASSERT( i2 < 65536 );
-    if ( i1 < i2 )
-    {
-      mHash = (i1<<16)|i2;
-    }
-    else
-    {
-      mHash = (i2<<16)|i1;
-    }
-    return mHash;
-  }
-  RtEdge     *mNextEdge;
-  Triangle *mTriangle;
-  NxU32    mHash;
-};
-
-
-typedef CONVEX_DECOMPOSITION::Array< Triangle * > TriangleVector;
-typedef CONVEX_DECOMPOSITION::HashMap< NxU32, RtEdge * > EdgeMap;
-
-class MyRemoveTjunctions : public RemoveTjunctions
-{
-public:
-  MyRemoveTjunctions(void)
-  {
-    mInputTriangles = 0;
-    mEdges = 0;
-    mVcount = 0;
-    mVertices = 0;
-    mEdgeCount = 0;
-  }
-  ~MyRemoveTjunctions(void)
-  {
-    release();
-  }
-
-  virtual NxU32 removeTjunctions(RemoveTjunctionsDesc &desc)
-  {
-    NxU32 ret = 0;
-
-	mEpsilon = desc.mEpsilon;
-
-	size_t TcountOut;
-
-    desc.mIndicesOut = removeTjunctions(desc.mVcount, desc.mVertices, desc.mTcount, desc.mIndices, TcountOut, desc.mIds);
-
-#ifdef WIN32
-#	pragma warning(push)
-#	pragma warning(disable:4267)
-#endif
-
-	NX_ASSERT( TcountOut < UINT_MAX );
-	desc.mTcountOut = TcountOut;
-
-#ifdef WIN32
-#	pragma warning(pop)
-#endif
-
-    if ( !mIds.empty() )
-    {
-      desc.mIdsOut = &mIds[0];
-    }
-
-    ret = desc.mTcountOut;
-
-    bool check = ret != desc.mTcount;
-#if 0
-    while ( check )
-    {
-        NxU32 tcount = ret;
-        NxU32 *indices  = new NxU32[tcount*3];
-        NxU32 *ids      = new NxU32[tcount];
-        memcpy(indices,desc.mIndicesOut,sizeof(NxU32)*ret*3);
-        memcpy(ids,desc.mIdsOut,sizeof(NxU32)*ret);
-        desc.mIndicesOut = removeTjunctions(desc.mVcount, desc.mVertices, tcount, indices, desc.mTcountOut, ids );
-        if ( !mIds.empty() )
-        {
-          desc.mIdsOut = &mIds[0];
-        }
-        ret = desc.mTcountOut;
-        delete []indices;
-        delete []ids;
-        check = ret != tcount;
-    }
-#endif
-    return ret;
-  }
-
-  RtEdge * addEdge(Triangle *t,RtEdge *e,NxU32 i1,NxU32 i2)
-  {
-    NxU32 hash = e->init(t,i1,i2);
-    const EdgeMap::Entry *found = mEdgeMap.find(hash);
-    if ( found == NULL )
-    {
-      mEdgeMap[hash] = e;
-    }
-    else
-    {
-      RtEdge *old_edge = (*found).second;
-      e->mNextEdge = old_edge;
-      mEdgeMap.erase(hash);
-      mEdgeMap[hash] = e;
-    }
-    e++;
-    mEdgeCount++;
-    return e;
-  }
-
-  RtEdge * init(Triangle *t,const NxU32 *indices,const float *vertices,RtEdge *e,NxU32 id)
-  {
-    t->init(indices,vertices,id);
-    e = addEdge(t,e,t->mI1,t->mI2);
-    e = addEdge(t,e,t->mI2,t->mI3);
-    e = addEdge(t,e,t->mI3,t->mI1);
-    return e;
-  }
-
-  void release(void)
-  {
-    mIds.clear();
-    mEdgeMap.clear();
-    mIndices.clear();
-    mSplit.clear();
-    delete []mInputTriangles;
-    delete []mEdges;
-    mInputTriangles = 0;
-    mEdges = 0;
-    mVcount = 0;
-    mVertices = 0;
-    mEdgeCount = 0;
-
-  }
-
-  virtual NxU32 * removeTjunctions(NxU32 vcount,
-                                    const float *vertices,
-                                    size_t tcount,
-                                    const NxU32 *indices,
-                                    size_t &tcount_out,
-                                    const NxU32 * ids)
-  {
-    NxU32 *ret  = 0;
-
-    release();
-
-    mVcount   = vcount;
-    mVertices = vertices;
-    mTcount   = (NxU32)tcount;
-    tcount_out = 0;
-
-    mTcount         = (NxU32)tcount;
-    mMaxTcount      = (NxU32)tcount*2;
-    mInputTriangles = new Triangle[mMaxTcount];
-    Triangle *t     = mInputTriangles;
-
-    mEdges          = new RtEdge[mMaxTcount*3];
-    mEdgeCount      = 0;
-
-    NxU32 id = 0;
-
-    RtEdge *e = mEdges;
-    for (NxU32 i=0; i<tcount; i++)
-    {
-      if ( ids ) id = *ids++;
-      e =init(t,indices,vertices,e,id);
-      indices+=3;
-      t++;
-    }
-
-    {
-      TriangleVector test;
-      for (EdgeMap::Iterator i = mEdgeMap.getIterator(); !i.done(); ++i)
-      {
-        RtEdge *e = (*i).second;
-        if ( e->mNextEdge == 0 ) // open edge!
-        {
-          Triangle *t = e->mTriangle;
-          if ( !t->mPending )
-          {
-            test.pushBack(t);
-            t->mPending = true;
-          }
-        }
-      }
-
-      if ( !test.empty() )
-      {
-        TriangleVector::Iterator i;
-        for (i=test.begin(); i!=test.end(); ++i)
-        {
-          Triangle *t = (*i);
-          locateIntersection(t);
-        }
-      }
-
-    }
-
-    while ( !mSplit.empty() )
-    {
-      TriangleVector scan = mSplit;
-      mSplit.clear();
-      TriangleVector::Iterator i;
-      for (i=scan.begin(); i!=scan.end(); ++i)
-      {
-        Triangle *t = (*i);
-        locateIntersection(t);
-      }
-    }
-
-
-    mIndices.clear();
-    mIds.clear();
-
-    t = mInputTriangles;
-    for (NxU32 i=0; i<mTcount; i++)
-    {
-      mIndices.pushBack(t->mI1);
-      mIndices.pushBack(t->mI2);
-      mIndices.pushBack(t->mI3);
-      mIds.pushBack(t->mId);
-      t++;
-    }
-
-
-   mEdgeMap.clear();
-
-   delete []mEdges;
-   mEdges = 0;
-   delete []mInputTriangles;
-   mInputTriangles = 0;
-   tcount_out = mIndices.size()/3;
-   ret = tcount_out ? &mIndices[0] : 0;
-#ifdef _DEBUG
-   if ( ret )
-   {
-	   const NxU32 *scan = ret;
-	   for (NxU32 i=0; i<tcount_out; i++)
-	   {
-		   NxU32 i1 = scan[0];
-		   NxU32 i2 = scan[1];
-		   NxU32 i3 = scan[2];
-		   assert( i1 != i2 && i1 != i3 && i2 != i3 );
-		   scan+=3;
-	   }
-   }
-#endif
-    return ret;
-  }
-
-  Triangle * locateIntersection(Triangle *scan,Triangle *t)
-  {
-    Triangle *ret = 0;
-
-    NxU32 t1 = (NxU32)(scan-mInputTriangles);
-    NxU32 t2 = (NxU32)(t-mInputTriangles);
-
-    NX_ASSERT( t1 < mTcount );
-    NX_ASSERT( t2 < mTcount );
-
-    NX_ASSERT( scan->mI1 < mVcount );
-    NX_ASSERT( scan->mI2 < mVcount );
-    NX_ASSERT( scan->mI3 < mVcount );
-
-    NX_ASSERT( t->mI1 < mVcount );
-    NX_ASSERT( t->mI2 < mVcount );
-    NX_ASSERT( t->mI3 < mVcount );
-
-
-    NxU32 intersection_index;
-    NxU32 edge;
-
-    if ( scan != t && scan->intersects(t,mVertices,intersection_index,edge,mEpsilon) )
-    {
-
-	  if ( t->mI1 == intersection_index || t->mI2 == intersection_index || t->mI3 == intersection_index )
-	  {
-	  }
-	  else
-	  {
-		  // here is where it intersects!
-		  NxU32 i1,i2,i3;
-		  NxU32 j1,j2,j3;
-		  NxU32 id = t->mId;
-
-		  switch ( edge )
-		  {
-			case 0:
-			  i1 = t->mI1;
-			  i2 = intersection_index;
-			  i3 = t->mI3;
-			  j1 = intersection_index;
-			  j2 = t->mI2;
-			  j3 = t->mI3;
-			  break;
-			case 1:
-			  i1 = t->mI2;
-			  i2 = intersection_index;
-			  i3 = t->mI1;
-			  j1 = intersection_index;
-			  j2 = t->mI3;
-			  j3 = t->mI1;
-			  break;
-			case 2:
-			  i1 = t->mI3;
-			  i2 = intersection_index;
-			  i3 = t->mI2;
-			  j1 = intersection_index;
-			  j2 = t->mI1;
-			  j3 = t->mI2;
-			  break;
-			default:
-			  NX_ASSERT(0);
-			  i1 = i2 = i3 = 0;
-			  j1 = j2 = j3 = 0;
-			  break;
-		  }
-
-		  if ( mTcount < mMaxTcount )
-		  {
-			t->init(i1,i2,i3,mVertices,id);
-			Triangle *newt = &mInputTriangles[mTcount];
-			newt->init(j1,j2,j3,mVertices,id);
-			mTcount++;
-			t->mSplit = true;
-			newt->mSplit = true;
-
-			mSplit.pushBack(t);
-			mSplit.pushBack(newt);
-			ret = scan;
-		  }
-	    }
-    }
-    return ret;
-  }
-
-  Triangle * testIntersection(Triangle *scan,Triangle *t)
-  {
-    Triangle *ret = 0;
-
-    NxU32 t1 = (NxU32)(scan-mInputTriangles);
-    NxU32 t2 = (NxU32)(t-mInputTriangles);
-
-    NX_ASSERT( t1 < mTcount );
-    NX_ASSERT( t2 < mTcount );
-
-    NX_ASSERT( scan->mI1 < mVcount );
-    NX_ASSERT( scan->mI2 < mVcount );
-    NX_ASSERT( scan->mI3 < mVcount );
-
-    NX_ASSERT( t->mI1 < mVcount );
-    NX_ASSERT( t->mI2 < mVcount );
-    NX_ASSERT( t->mI3 < mVcount );
-
-
-    NxU32 intersection_index;
-    NxU32 edge;
-
-    assert( scan != t );
-
-    if ( scan->intersects(t,mVertices,intersection_index,edge,mEpsilon) )
-    {
-      // here is where it intersects!
-      NxU32 i1,i2,i3;
-      NxU32 j1,j2,j3;
-      NxU32 id = t->mId;
-
-      switch ( edge )
-      {
-        case 0:
-          i1 = t->mI1;
-          i2 = intersection_index;
-          i3 = t->mI3;
-          j1 = intersection_index;
-          j2 = t->mI2;
-          j3 = t->mI3;
-          break;
-        case 1:
-          i1 = t->mI2;
-          i2 = intersection_index;
-          i3 = t->mI1;
-          j1 = intersection_index;
-          j2 = t->mI3;
-          j3 = t->mI1;
-          break;
-        case 2:
-          i1 = t->mI3;
-          i2 = intersection_index;
-          i3 = t->mI2;
-          j1 = intersection_index;
-          j2 = t->mI1;
-          j3 = t->mI2;
-          break;
-        default:
-          NX_ASSERT(0);
-          i1 = i2 = i3 = 0;
-          j1 = j2 = j3 = 0;
-          break;
-      }
-
-      if ( mTcount < mMaxTcount )
-      {
-        t->init(i1,i2,i3,mVertices,id);
-        Triangle *newt = &mInputTriangles[mTcount];
-        newt->init(j1,j2,j3,mVertices,id);
-        mTcount++;
-        t->mSplit = true;
-        newt->mSplit = true;
-
-        mSplit.pushBack(t);
-        mSplit.pushBack(newt);
-        ret = scan;
-      }
-    }
-    return ret;
-  }
-
-  Triangle * locateIntersection(Triangle *t)
-  {
-    Triangle *ret = 0;
-
-    Triangle *scan = mInputTriangles;
-
-    for (NxU32 i=0; i<mTcount; i++)
-    {
-      ret = locateIntersection(scan,t);
-      if ( ret )
-        break;
-      scan++;
-    }
-    return ret;
-  }
-
-
-  Triangle             *mInputTriangles;
-  NxU32                mVcount;
-  NxU32                mMaxTcount;
-  NxU32                mTcount;
-  const float          *mVertices;
-  NxU32Vector          mIndices;
-  NxU32Vector          mIds;
-  TriangleVector        mSplit;
-  NxU32                mEdgeCount;
-  RtEdge                 *mEdges;
-  EdgeMap               mEdgeMap;
-  NxF32                 mEpsilon;
-};
-
-RemoveTjunctions * createRemoveTjunctions(void)
-{
-  MyRemoveTjunctions *m = new MyRemoveTjunctions;
-  return static_cast< RemoveTjunctions *>(m);
-}
-
-void               releaseRemoveTjunctions(RemoveTjunctions *tj)
-{
-  MyRemoveTjunctions *m = static_cast< MyRemoveTjunctions *>(tj);
-  delete m;
-}
-
-
-}; // end of namespace
-

+ 0 - 110
Engine/lib/convexDecomp/NvRemoveTjunctions.h

@@ -1,110 +0,0 @@
-#ifndef REMOVE_TJUNCTIONS_H
-
-#define REMOVE_TJUNCTIONS_H
-
-/*
-
-NvRemoveTjunctions.h : A code snippet to remove tjunctions from a triangle mesh.  This version is currently disabled as it appears to have a bug.
-
-*/
-
-
-#include "NvUserMemAlloc.h"
-
-/*!
-**
-** Copyright (c) 2009 by John W. Ratcliff mailto:[email protected]
-**
-** Portions of this source has been released with the PhysXViewer application, as well as
-** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
-**
-** If you find this code useful or you are feeling particularily generous I would
-** ask that you please go to http://www.amillionpixels.us and make a donation
-** to Troy DeMolay.
-**
-** DeMolay is a youth group for young men between the ages of 12 and 21.
-** It teaches strong moral principles, as well as leadership skills and
-** public speaking.  The donations page uses the 'pay for pixels' paradigm
-** where, in this case, a pixel is only a single penny.  Donations can be
-** made for as small as $4 or as high as a $100 block.  Each person who donates
-** will get a link to their own site as well as acknowledgement on the
-** donations blog located here http://www.amillionpixels.blogspot.com/
-**
-** If you wish to contact me you can use the following methods:
-**
-** Skype ID: jratcliff63367
-** Yahoo: jratcliff63367
-** AOL: jratcliff1961
-** email: [email protected]
-**
-**
-** The MIT license:
-**
-** Permission is hereby granted, free of charge, to any person obtaining a copy
-** of this software and associated documentation files (the "Software"), to deal
-** in the Software without restriction, including without limitation the rights
-** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-** copies of the Software, and to permit persons to whom the Software is furnished
-** to do so, subject to the following conditions:
-**
-** The above copyright notice and this permission notice shall be included in all
-** copies or substantial portions of the Software.
-
-** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-namespace CONVEX_DECOMPOSITION
-{
-
-class RemoveTjunctionsDesc
-{
-public:
-  RemoveTjunctionsDesc(void)
-  {
-    mVcount = 0;
-    mVertices = 0;
-    mTcount = 0;
-    mIndices = 0;
-    mIds = 0;
-    mTcountOut = 0;
-    mIndicesOut = 0;
-    mIdsOut = 0;
-	mEpsilon = 0.00000001f;
-  }
-
-// input
-  NxF32        mEpsilon;
-  NxF32        mDistanceEpsilon;
-  NxU32        mVcount;  		// input vertice count.
-  const NxF32 *mVertices; 		// input vertices as NxF32s or...
-  NxU32        mTcount;    		// number of input triangles.
-  const NxU32 *mIndices;   		// triangle indices.
-  const NxU32 *mIds;       		// optional triangle Id numbers.
-// output..
-  NxU32        mTcountOut;  // number of output triangles.
-  const NxU32 *mIndicesOut; // output triangle indices
-  const NxU32 *mIdsOut;     // output retained id numbers.
-};
-
-// Removes t-junctions from an input mesh.  Does not generate any new data points, but may possible produce additional triangles and new indices.
-class RemoveTjunctions
-{
-public:
-
-   virtual NxU32 removeTjunctions(RemoveTjunctionsDesc &desc) =0; // returns number of triangles output and the descriptor is filled with the appropriate results.
-
-
-};
-
-RemoveTjunctions * createRemoveTjunctions(void);
-void               releaseRemoveTjunctions(RemoveTjunctions *tj);
-
-}; // end of namespace
-
-#endif

+ 0 - 189
Engine/lib/convexDecomp/NvSimpleTypes.h

@@ -1,189 +0,0 @@
-#ifndef NV_SIMPLE_TYPES_H
-
-#define NV_SIMPLE_TYPES_H
-
-/*
-
-NvSimpleTypes.h : Defines basic data types for integers and floats.
-
-*/
-
-
-/*!
-**
-** Copyright (c) 2009 by John W. Ratcliff mailto:[email protected]
-**
-** Portions of this source has been released with the PhysXViewer application, as well as
-** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
-**
-** If you find this code useful or you are feeling particularily generous I would
-** ask that you please go to http://www.amillionpixels.us and make a donation
-** to Troy DeMolay.
-**
-** DeMolay is a youth group for young men between the ages of 12 and 21.
-** It teaches strong moral principles, as well as leadership skills and
-** public speaking.  The donations page uses the 'pay for pixels' paradigm
-** where, in this case, a pixel is only a single penny.  Donations can be
-** made for as small as $4 or as high as a $100 block.  Each person who donates
-** will get a link to their own site as well as acknowledgement on the
-** donations blog located here http://www.amillionpixels.blogspot.com/
-**
-** If you wish to contact me you can use the following methods:
-**
-** Skype ID: jratcliff63367
-** Yahoo: jratcliff63367
-** AOL: jratcliff1961
-** email: [email protected]
-**
-**
-** The MIT license:
-**
-** Permission is hereby granted, free of charge, to any person obtaining a copy
-** of this software and associated documentation files (the "Software"), to deal
-** in the Software without restriction, including without limitation the rights
-** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-** copies of the Software, and to permit persons to whom the Software is furnished
-** to do so, subject to the following conditions:
-**
-** The above copyright notice and this permission notice shall be included in all
-** copies or substantial portions of the Software.
-
-** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-
-#if defined(__APPLE__)
-   #include <sys/malloc.h>
-#else
-#if defined( __FreeBSD__)
-   #include <stdlib.h>
-#else
-   #include <malloc.h>
-#endif
-#endif
-#include <assert.h>
-
-#if defined(__APPLE__) || defined(__CELLOS_LV2__) || defined(LINUX)
-
-#ifndef stricmp
-#define stricmp(a, b) strcasecmp((a), (b))
-#define _stricmp(a, b) strcasecmp((a), (b))
-#endif
-
-#endif
-
-#if defined(WIN32)
-	typedef __int64				NxI64;
-	typedef signed int			NxI32;
-	typedef signed short		NxI16;
-	typedef signed char			NxI8;
-
-	typedef unsigned __int64	NxU64;
-	typedef unsigned int		NxU32;
-	typedef unsigned short		NxU16;
-	typedef unsigned char		NxU8;
-
-	typedef float				NxF32;
-	typedef double				NxF64;
-
-#elif defined(LINUX)
-	typedef long long			NxI64;
-	typedef signed int			NxI32;
-	typedef signed short		NxI16;
-	typedef signed char			NxI8;
-
-	typedef unsigned long long	NxU64;
-	typedef unsigned int		NxU32;
-	typedef unsigned short		NxU16;
-	typedef unsigned char		NxU8;
-
-	typedef float				NxF32;
-	typedef double				NxF64;
-
-#elif defined(__APPLE__)
-	typedef long long			NxI64;
-	typedef signed int			NxI32;
-	typedef signed short		NxI16;
-	typedef signed char			NxI8;
-
-	typedef unsigned long long	NxU64;
-	typedef unsigned int		NxU32;
-	typedef unsigned short		NxU16;
-	typedef unsigned char		NxU8;
-
-	typedef float				NxF32;
-	typedef double				NxF64;
-
-#elif defined(__FreeBSD__)
-	typedef long long			NxI64;
-	typedef signed int			NxI32;
-	typedef signed short		NxI16;
-	typedef signed char			NxI8;
-
-	typedef unsigned long long	NxU64;
-	typedef unsigned int		NxU32;
-	typedef unsigned short		NxU16;
-	typedef unsigned char		NxU8;
-
-	typedef float				NxF32;
-	typedef double				NxF64;
-
-#elif defined(__CELLOS_LV2__)
-	typedef long long			NxI64;
-	typedef signed int			NxI32;
-	typedef signed short		NxI16;
-	typedef signed char			NxI8;
-
-	typedef unsigned long long	NxU64;
-	typedef unsigned int		NxU32;
-	typedef unsigned short		NxU16;
-	typedef unsigned char		NxU8;
-
-	typedef float				NxF32;
-	typedef double				NxF64;
-
-#elif defined(_XBOX)
-	typedef __int64				NxI64;
-	typedef signed int			NxI32;
-	typedef signed short		NxI16;
-	typedef signed char			NxI8;
-
-	typedef unsigned __int64	NxU64;
-	typedef unsigned int		NxU32;
-	typedef unsigned short		NxU16;
-	typedef unsigned char		NxU8;
-
-	typedef float				NxF32;
-	typedef double				NxF64;
-
-#elif defined(__PPCGEKKO__)
-	typedef long long			NxI64;
-	typedef signed int			NxI32;
-	typedef signed short		NxI16;
-	typedef signed char			NxI8;
-
-	typedef unsigned long long	NxU64;
-	typedef unsigned int		NxU32;
-	typedef unsigned short		NxU16;
-	typedef unsigned char		NxU8;
-
-	typedef float				NxF32;
-	typedef double				NxF64;
-
-#else
-	#error Unknown platform!
-#endif
-
-#ifndef NX_INLINE
-#define NX_INLINE inline
-#define NX_ASSERT assert
-#endif
-
-
-#endif

+ 0 - 224
Engine/lib/convexDecomp/NvSplitMesh.cpp

@@ -1,224 +0,0 @@
-/*
-
-NvSplitMesh.cpp : A code snippet to split a mesh into two seperate meshes based on a slicing plane.
-
-*/
-
-/*!
-**
-** Copyright (c) 2009 by John W. Ratcliff mailto:[email protected]
-**
-** Portions of this source has been released with the PhysXViewer application, as well as
-** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
-**
-** If you find this code useful or you are feeling particularily generous I would
-** ask that you please go to http://www.amillionpixels.us and make a donation
-** to Troy DeMolay.
-**
-** DeMolay is a youth group for young men between the ages of 12 and 21.
-** It teaches strong moral principles, as well as leadership skills and
-** public speaking.  The donations page uses the 'pay for pixels' paradigm
-** where, in this case, a pixel is only a single penny.  Donations can be
-** made for as small as $4 or as high as a $100 block.  Each person who donates
-** will get a link to their own site as well as acknowledgement on the
-** donations blog located here http://www.amillionpixels.blogspot.com/
-**
-** If you wish to contact me you can use the following methods:
-**
-** Skype ID: jratcliff63367
-** Yahoo: jratcliff63367
-** AOL: jratcliff1961
-** email: [email protected]
-**
-**
-** The MIT license:
-**
-** Permission is hereby granted, free of charge, to any person obtaining a copy
-** of this software and associated documentation files (the "Software"), to deal
-** in the Software without restriction, including without limitation the rights
-** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-** copies of the Software, and to permit persons to whom the Software is furnished
-** to do so, subject to the following conditions:
-**
-** The above copyright notice and this permission notice shall be included in all
-** copies or substantial portions of the Software.
-
-** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-#define SHOW_DEBUG 0
-#if SHOW_DEBUG
-#include "RenderDebug.h"
-#endif
-
-#include "NvSplitMesh.h"
-#include "NvFloatMath.h"
-#include "NvHashMap.h"
-
-#pragma warning(disable:4100)
-
-namespace CONVEX_DECOMPOSITION
-{
-
-
-typedef Array< NxU32 > NxU32Array;
-
-class SplitMesh : public iSplitMesh, public Memalloc
-{
-public:
-	SplitMesh(void)
-	{
-		mLeftVertices = 0;
-		mRightVertices = 0;
-	}
-
-	~SplitMesh(void)
-	{
-		reset();
-	}
-
-	void reset(void)
-	{
-		if ( mLeftVertices )
-		{
-			fm_releaseVertexIndex(mLeftVertices);
-			mLeftVertices = 0;
-		}
-		if ( mRightVertices )
-		{
-			fm_releaseVertexIndex(mRightVertices);
-			mRightVertices = 0;
-		}
-		mLeftIndices.clear();
-		mRightIndices.clear();
-	}
-
-
-	virtual void splitMesh(const NvSplitMesh &source,NvSplitMesh &leftMesh,NvSplitMesh &rightMesh,const NxF32 *planeEquation,NxF32 precision)
-	{
-		reset();
-
-		mLeftVertices 	= fm_createVertexIndex(precision,false);
-		mRightVertices 	= fm_createVertexIndex(precision,false);
-
-		for (NxU32 i=0; i<source.mTcount; i++)
-		{
-			NxU32 i1 = source.mIndices[i*3+0];
-			NxU32 i2 = source.mIndices[i*3+1];
-			NxU32 i3 = source.mIndices[i*3+2];
-
-			const NxF32 *p1 = &source.mVertices[i1*3];
-			const NxF32 *p2 = &source.mVertices[i2*3];
-			const NxF32 *p3 = &source.mVertices[i3*3];
-
-			NxF32 source_tri[3*3];
-
-			source_tri[0] = p1[0];
-			source_tri[1] = p1[1];
-			source_tri[2] = p1[2];
-
-			source_tri[3] = p2[0];
-			source_tri[4] = p2[1];
-			source_tri[5] = p2[2];
-
-			source_tri[6] = p3[0];
-			source_tri[7] = p3[1];
-			source_tri[8] = p3[2];
-
-			NxF32 	front_tri[3*5];
-			NxF32 	back_tri[3*5];
-
-			NxU32	fcount,bcount;
-
-			fm_planeTriIntersection(planeEquation,source_tri,sizeof(NxF32)*3,0.000001f,front_tri,fcount,back_tri,bcount);
-			bool newPos;
-
-			if ( fcount )
-			{
-				NxU32 i1,i2,i3,i4;
-				i1 = mLeftVertices->getIndex( &front_tri[0],newPos );
-				i2 = mLeftVertices->getIndex( &front_tri[3],newPos );
-				i3 = mLeftVertices->getIndex( &front_tri[6],newPos );
-				mLeftIndices.pushBack(i1);
-				mLeftIndices.pushBack(i2);
-				mLeftIndices.pushBack(i3);
-				#if SHOW_DEBUG
-				NVSHARE::gRenderDebug->setCurrentColor(0xFFFFFF);
-				NVSHARE::gRenderDebug->DebugTri(&front_tri[0],&front_tri[3],&front_tri[6]);
-				#endif
-				if ( fcount == 4 )
-				{
-					i4 = mLeftVertices->getIndex( &front_tri[9],newPos );
-					mLeftIndices.pushBack(i1);
-					mLeftIndices.pushBack(i3);
-					mLeftIndices.pushBack(i4);
- 							#if SHOW_DEBUG
-					NVSHARE::gRenderDebug->setCurrentColor(0xFFFF00);
- 							NVSHARE::gRenderDebug->DebugTri(&front_tri[0],&front_tri[6],&front_tri[9]);
- 							#endif
-				}
-			}
-			if ( bcount )
-			{
-				NxU32 i1,i2,i3,i4;
-				i1 = mRightVertices->getIndex( &back_tri[0],newPos );
-				i2 = mRightVertices->getIndex( &back_tri[3],newPos );
-				i3 = mRightVertices->getIndex( &back_tri[6],newPos );
-				mRightIndices.pushBack(i1);
-				mRightIndices.pushBack(i2);
-				mRightIndices.pushBack(i3);
-				#if SHOW_DEBUG
-				NVSHARE::gRenderDebug->setCurrentColor(0xFF8080);
-				NVSHARE::gRenderDebug->DebugTri(&back_tri[0],&back_tri[3],&back_tri[6]);
-				#endif
-				if ( bcount == 4 )
-				{
-					i4 = mRightVertices->getIndex( &back_tri[9],newPos );
-					mRightIndices.pushBack(i1);
-					mRightIndices.pushBack(i3);
-					mRightIndices.pushBack(i4);
- 							#if SHOW_DEBUG
-					NVSHARE::gRenderDebug->setCurrentColor(0x00FF00);
- 							NVSHARE::gRenderDebug->DebugTri(&back_tri[0],&back_tri[6],&back_tri[9]);
- 							#endif
-				}
-			}
-		}
-
-		leftMesh.mVcount 	= mLeftVertices->getVcount();
-		leftMesh.mVertices 	= mLeftVertices->getVerticesFloat();
-		leftMesh.mTcount	= mLeftIndices.size()/3;
-		leftMesh.mIndices	= &mLeftIndices[0];
-
-		rightMesh.mVcount	= mRightVertices->getVcount();
-		rightMesh.mVertices	= mRightVertices->getVerticesFloat();
-		rightMesh.mTcount	= mRightIndices.size()/3;
-		rightMesh.mIndices	= &mRightIndices[0];
-
-	}
-
-
-	fm_VertexIndex	*mLeftVertices;
-	fm_VertexIndex	*mRightVertices;
- 	NxU32Array		 mLeftIndices;
- 	NxU32Array		 mRightIndices;
-};
-
-iSplitMesh *createSplitMesh(void)
-{
-	SplitMesh *sm = MEMALLOC_NEW(SplitMesh);
-	return static_cast< iSplitMesh *>(sm);
-}
-
-void        releaseSplitMesh(iSplitMesh *splitMesh)
-{
-	SplitMesh *sm = static_cast< SplitMesh *>(splitMesh);
-	delete sm;
-}
-
-}; // end of namespace

+ 0 - 88
Engine/lib/convexDecomp/NvSplitMesh.h

@@ -1,88 +0,0 @@
-#ifndef NV_SPLIT_MESH_H
-
-#define NV_SPLIT_MESH_H
-
-/*
-
-NvSplitMesh.h : A code snippet to split a mesh into two seperate meshes based on a slicing plane.
-
-*/
-
-
-#include "NvUserMemAlloc.h"
-
-/*!
-**
-** Copyright (c) 2009 by John W. Ratcliff mailto:[email protected]
-**
-** Portions of this source has been released with the PhysXViewer application, as well as
-** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
-**
-** If you find this code useful or you are feeling particularily generous I would
-** ask that you please go to http://www.amillionpixels.us and make a donation
-** to Troy DeMolay.
-**
-** DeMolay is a youth group for young men between the ages of 12 and 21.
-** It teaches strong moral principles, as well as leadership skills and
-** public speaking.  The donations page uses the 'pay for pixels' paradigm
-** where, in this case, a pixel is only a single penny.  Donations can be
-** made for as small as $4 or as high as a $100 block.  Each person who donates
-** will get a link to their own site as well as acknowledgement on the
-** donations blog located here http://www.amillionpixels.blogspot.com/
-**
-** If you wish to contact me you can use the following methods:
-**
-** Skype ID: jratcliff63367
-** Yahoo: jratcliff63367
-** AOL: jratcliff1961
-** email: [email protected]
-**
-**
-** The MIT license:
-**
-** Permission is hereby granted, free of charge, to any person obtaining a copy
-** of this software and associated documentation files (the "Software"), to deal
-** in the Software without restriction, including without limitation the rights
-** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-** copies of the Software, and to permit persons to whom the Software is furnished
-** to do so, subject to the following conditions:
-**
-** The above copyright notice and this permission notice shall be included in all
-** copies or substantial portions of the Software.
-
-** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-namespace CONVEX_DECOMPOSITION
-{
-
-struct NvSplitMesh
-{
-	NxU32	mVcount;
-	const NxF32	*mVertices;
-	NxU32	mTcount;
-	const NxU32	*mIndices;
-};
-
-
-class iSplitMesh
-{
-public:
-	virtual void splitMesh(const NvSplitMesh &source,NvSplitMesh &leftMesh,NvSplitMesh &rightMesh,const NxF32 *planeEquation,NxF32 precision) = 0;
-protected:
-	virtual ~iSplitMesh(void) { };
-};
-
-iSplitMesh *createSplitMesh(void);
-void        releaseSplitMesh(iSplitMesh *splitMesh);
-
-
-}; // end of namespace
-
-#endif

+ 0 - 3464
Engine/lib/convexDecomp/NvStanHull.cpp

@@ -1,3464 +0,0 @@
-
-/*
-
-NvStanHull.cpp : A convex hull generator written by Stan Melax
-
-*/
-/*!
-**
-** Copyright (c) 2009 by John W. Ratcliff mailto:[email protected]
-**
-** Portions of this source has been released with the PhysXViewer application, as well as
-** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
-**
-** If you find this code useful or you are feeling particularily generous I would
-** ask that you please go to http://www.amillionpixels.us and make a donation
-** to Troy DeMolay.
-**
-** DeMolay is a youth group for young men between the ages of 12 and 21.
-** It teaches strong moral principles, as well as leadership skills and
-** public speaking.  The donations page uses the 'pay for pixels' paradigm
-** where, in this case, a pixel is only a single penny.  Donations can be
-** made for as small as $4 or as high as a $100 block.  Each person who donates
-** will get a link to their own site as well as acknowledgement on the
-** donations blog located here http://www.amillionpixels.blogspot.com/
-**
-** If you wish to contact me you can use the following methods:
-**
-** Skype ID: jratcliff63367
-** Yahoo: jratcliff63367
-** AOL: jratcliff1961
-** email: [email protected]
-**
-**
-** The MIT license:
-**
-** Permission is hereby granted, free of charge, to any person obtaining a copy
-** of this software and associated documentation files (the "Software"), to deal
-** in the Software without restriction, including without limitation the rights
-** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-** copies of the Software, and to permit persons to whom the Software is furnished
-** to do so, subject to the following conditions:
-**
-** The above copyright notice and this permission notice shall be included in all
-** copies or substantial portions of the Software.
-
-** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <math.h>
-#include <float.h>
-
-
-#include <stdarg.h>
-#include <setjmp.h>
-
-#include "NvStanHull.h"
-
-namespace CONVEX_DECOMPOSITION
-{
-
-//*****************************************************
-//*** DARRAY.H
-//*****************************************************
-
-template <class Type> class ArrayRet;
-template <class Type> class Array
-{
-	public:
-				Array(NxI32 s=0);
-				Array(Array<Type> &array);
-				Array(ArrayRet<Type> &array);
-				~Array();
-	void		allocate(NxI32 s);
-	void		SetSize(NxI32 s);
-	void		Pack();
-	Type&		Add(Type);
-	void		AddUnique(Type);
-	NxI32 		Contains(Type);
-	void		Insert(Type,NxI32);
-	NxI32			IndexOf(Type);
-	void		Remove(Type);
-	void		DelIndex(NxI32 i);
-	Type *		element;
-	NxI32			count;
-	NxI32			array_size;
-	const Type	&operator[](NxI32 i) const { assert(i>=0 && i<count);  return element[i]; }
-	Type		&operator[](NxI32 i)  { assert(i>=0 && i<count);  return element[i]; }
-	Type		&Pop() { assert(count); count--;  return element[count]; }
-	Array<Type> &operator=(Array<Type> &array);
-	Array<Type> &operator=(ArrayRet<Type> &array);
-	// operator ArrayRet<Type> &() { return *(ArrayRet<Type> *)this;} // this worked but i suspect could be dangerous
-};
-
-template <class Type> class ArrayRet:public Array<Type>
-{
-};
-
-template <class Type> Array<Type>::Array(NxI32 s)
-{
-	count=0;
-	array_size = 0;
-	element = NULL;
-	if(s)
-	{
-		allocate(s);
-	}
-}
-
-
-template <class Type> Array<Type>::Array(Array<Type> &array)
-{
-	count=0;
-	array_size = 0;
-	element = NULL;
-	for(NxI32 i=0;i<array.count;i++)
-	{
-		Add(array[i]);
-	}
-}
-
-
-template <class Type> Array<Type>::Array(ArrayRet<Type> &array)
-{
-	*this = array;
-}
-template <class Type> Array<Type> &Array<Type>::operator=(ArrayRet<Type> &array)
-{
-	count=array.count;
-	array_size = array.array_size;
-	element = array.element;
-	array.element=NULL;
-	array.count=0;
-	array.array_size=0;
-	return *this;
-}
-
-
-template <class Type> Array<Type> &Array<Type>::operator=(Array<Type> &array)
-{
-	count=0;
-	for(NxI32 i=0;i<array.count;i++)
-	{
-		Add(array[i]);
-	}
-	return *this;
-}
-
-template <class Type> Array<Type>::~Array()
-{
-	if (element != NULL)
-	{
-	  MEMALLOC_FREE(element);
-	}
-	count=0;array_size=0;element=NULL;
-}
-
-template <class Type> void Array<Type>::allocate(NxI32 s)
-{
-	assert(s>0);
-	assert(s>=count);
-	Type *old = element;
-	array_size =s;
-	element = (Type *) MEMALLOC_MALLOC( sizeof(Type)*array_size );
-	assert(element);
-	for(NxI32 i=0;i<count;i++)
-	{
-		element[i]=old[i];
-	}
-	if(old)
-	{
-		MEMALLOC_FREE(old);
-	}
-}
-
-template <class Type> void Array<Type>::SetSize(NxI32 s)
-{
-	if(s==0)
-	{
-		if(element)
-		{
-			MEMALLOC_FREE(element);
-			element = NULL;
-		}
- 	  array_size = s;
-	}
-	else
-	{
-		allocate(s);
-	}
-	count=s;
-}
-
-template <class Type> void Array<Type>::Pack()
-{
-	allocate(count);
-}
-
-template <class Type> Type& Array<Type>::Add(Type t)
-{
-	assert(count<=array_size);
-	if(count==array_size)
-	{
-		allocate((array_size)?array_size *2:16);
-	}
-	element[count++] = t;
-	return element[count-1];
-}
-
-template <class Type> NxI32 Array<Type>::Contains(Type t)
-{
-	NxI32 i;
-	NxI32 found=0;
-	for(i=0;i<count;i++)
-	{
-		if(element[i] == t) found++;
-	}
-	return found;
-}
-
-template <class Type> void Array<Type>::AddUnique(Type t)
-{
-	if(!Contains(t)) Add(t);
-}
-
-
-template <class Type> void Array<Type>::DelIndex(NxI32 i)
-{
-	assert(i<count);
-	count--;
-	while(i<count)
-	{
-		element[i] = element[i+1];
-		i++;
-	}
-}
-
-template <class Type> void Array<Type>::Remove(Type t)
-{
-	NxI32 i;
-	for(i=0;i<count;i++)
-	{
-		if(element[i] == t)
-		{
-			break;
-		}
-	}
-	assert(i<count); // assert object t is in the array.
-	DelIndex(i);
-	for(i=0;i<count;i++)
-	{
-		assert(element[i] != t);
-	}
-}
-
-template <class Type> void Array<Type>::Insert(Type t,NxI32 k)
-{
-	NxI32 i=count;
-	Add(t); // to allocate space
-	while(i>k)
-	{
-		element[i]=element[i-1];
-		i--;
-	}
-	assert(i==k);
-	element[k]=t;
-}
-
-
-template <class Type> NxI32 Array<Type>::IndexOf(Type t)
-{
-	NxI32 i;
-	for(i=0;i<count;i++)
-	{
-		if(element[i] == t)
-		{
-			return i;
-		}
-	}
-	assert(0);
-	return -1;
-}
-
-//****************************************************
-//** VECMATH.H
-//****************************************************
-#define PI (3.1415926535897932384626433832795f)
-
-#define DEG2RAD (PI / 180.0f)
-#define RAD2DEG (180.0f / PI)
-#define SQRT_OF_2 (1.4142135f)
-#define OFFSET(Class,Member)  (((char*) (&(((Class*)NULL)-> Member )))- ((char*)NULL))
-
-
-
-NxI32    argmin(NxF32 a[],NxI32 n);
-NxF32  sqr(NxF32 a);
-NxF32  clampf(NxF32 a) ;
-NxF32  Round(NxF32 a,NxF32 precision);
-NxF32  Interpolate(const NxF32 &f0,const NxF32 &f1,NxF32 alpha) ;
-
-template <class T>
-void Swap(T &a,T &b)
-{
-	T tmp = a;
-	a=b;
-	b=tmp;
-}
-
-
-
-template <class T>
-T Max(const T &a,const T &b)
-{
-	return (a>b)?a:b;
-}
-
-template <class T>
-T Min(const T &a,const T &b)
-{
-	return (a<b)?a:b;
-}
-
-//----------------------------------
-
-class int3  : public Memalloc
-{
-public:
-	NxI32 x,y,z;
-	int3(){};
-	int3(NxI32 _x,NxI32 _y, NxI32 _z){x=_x;y=_y;z=_z;}
-	const NxI32& operator[](NxI32 i) const {return (&x)[i];}
-	NxI32& operator[](NxI32 i) {return (&x)[i];}
-};
-
-
-//-------- 2D --------
-
-class float2  : public Memalloc
-{
-public:
-	NxF32 x,y;
-	float2(){x=0;y=0;};
-	float2(NxF32 _x,NxF32 _y){x=_x;y=_y;}
-	NxF32& operator[](NxI32 i) {assert(i>=0&&i<2);return ((NxF32*)this)[i];}
-	const NxF32& operator[](NxI32 i) const {assert(i>=0&&i<2);return ((NxF32*)this)[i];}
-};
-inline float2 operator-( const float2& a, const float2& b ){return float2(a.x-b.x,a.y-b.y);}
-inline float2 operator+( const float2& a, const float2& b ){return float2(a.x+b.x,a.y+b.y);}
-
-//--------- 3D ---------
-
-class float3  : public Memalloc // 3D
-{
-	public:
-	NxF32 x,y,z;
-	float3(){x=0;y=0;z=0;};
-	float3(NxF32 _x,NxF32 _y,NxF32 _z){x=_x;y=_y;z=_z;};
-	//operator NxF32 *() { return &x;};
-	NxF32& operator[](NxI32 i) {assert(i>=0&&i<3);return ((NxF32*)this)[i];}
-	const NxF32& operator[](NxI32 i) const {assert(i>=0&&i<3);return ((NxF32*)this)[i];}
-};
-
-
-float3& operator+=( float3 &a, const float3& b );
-float3& operator-=( float3 &a ,const float3& b );
-float3& operator*=( float3 &v ,const NxF32 s );
-float3& operator/=( float3 &v, const NxF32 s );
-
-NxF32  magnitude( const float3& v );
-float3 normalize( const float3& v );
-float3 safenormalize(const float3 &v);
-float3 vabs(const float3 &v);
-float3 operator+( const float3& a, const float3& b );
-float3 operator-( const float3& a, const float3& b );
-float3 operator-( const float3& v );
-float3 operator*( const float3& v, const NxF32 s );
-float3 operator*( const NxF32 s, const float3& v );
-float3 operator/( const float3& v, const NxF32 s );
-inline NxI32 operator==( const float3 &a, const float3 &b ) { return (a.x==b.x && a.y==b.y && a.z==b.z); }
-inline NxI32 operator!=( const float3 &a, const float3 &b ) { return (a.x!=b.x || a.y!=b.y || a.z!=b.z); }
-// due to ambiguity and inconsistent standards ther are no overloaded operators for mult such as va*vb.
-NxF32  dot( const float3& a, const float3& b );
-float3 cmul( const float3 &a, const float3 &b);
-float3 cross( const float3& a, const float3& b );
-float3 Interpolate(const float3 &v0,const float3 &v1,NxF32 alpha);
-float3 Round(const float3& a,NxF32 precision);
-float3	VectorMax(const float3 &a, const float3 &b);
-float3	VectorMin(const float3 &a, const float3 &b);
-
-
-
-class float3x3  : public Memalloc
-{
-	public:
-	float3 x,y,z;  // the 3 rows of the Matrix
-	float3x3(){}
-	float3x3(NxF32 xx,NxF32 xy,NxF32 xz,NxF32 yx,NxF32 yy,NxF32 yz,NxF32 zx,NxF32 zy,NxF32 zz):x(xx,xy,xz),y(yx,yy,yz),z(zx,zy,zz){}
-	float3x3(float3 _x,float3 _y,float3 _z):x(_x),y(_y),z(_z){}
-	float3&       operator[](NxI32 i)       {assert(i>=0&&i<3);return (&x)[i];}
-	const float3& operator[](NxI32 i) const {assert(i>=0&&i<3);return (&x)[i];}
-	NxF32&        operator()(NxI32 r, NxI32 c)       {assert(r>=0&&r<3&&c>=0&&c<3);return ((&x)[r])[c];}
-	const NxF32&  operator()(NxI32 r, NxI32 c) const {assert(r>=0&&r<3&&c>=0&&c<3);return ((&x)[r])[c];}
-};
-float3x3 Transpose( const float3x3& m );
-float3   operator*( const float3& v  , const float3x3& m  );
-float3   operator*( const float3x3& m , const float3& v   );
-float3x3 operator*( const float3x3& m , const NxF32& s   );
-float3x3 operator*( const float3x3& ma, const float3x3& mb );
-float3x3 operator/( const float3x3& a, const NxF32& s ) ;
-float3x3 operator+( const float3x3& a, const float3x3& b );
-float3x3 operator-( const float3x3& a, const float3x3& b );
-float3x3 &operator+=( float3x3& a, const float3x3& b );
-float3x3 &operator-=( float3x3& a, const float3x3& b );
-float3x3 &operator*=( float3x3& a, const NxF32& s );
-NxF32    Determinant(const float3x3& m );
-float3x3 Inverse(const float3x3& a);  // its just 3x3 so we simply do that cofactor method
-
-
-//-------- 4D Math --------
-
-class float4  : public Memalloc
-{
-public:
-	NxF32 x,y,z,w;
-	float4(){x=0;y=0;z=0;w=0;};
-	float4(NxF32 _x,NxF32 _y,NxF32 _z,NxF32 _w){x=_x;y=_y;z=_z;w=_w;}
-	float4(const float3 &v,NxF32 _w){x=v.x;y=v.y;z=v.z;w=_w;}
-	//operator NxF32 *() { return &x;};
-	NxF32& operator[](NxI32 i) {assert(i>=0&&i<4);return ((NxF32*)this)[i];}
-	const NxF32& operator[](NxI32 i) const {assert(i>=0&&i<4);return ((NxF32*)this)[i];}
-	const float3& xyz() const { return *((float3*)this);}
-	float3&       xyz()       { return *((float3*)this);}
-};
-
-
-struct D3DXMATRIX;
-
-class float4x4  : public Memalloc
-{
-	public:
-	float4 x,y,z,w;  // the 4 rows
-	float4x4(){}
-	float4x4(const float4 &_x, const float4 &_y, const float4 &_z, const float4 &_w):x(_x),y(_y),z(_z),w(_w){}
-	float4x4(NxF32 m00, NxF32 m01, NxF32 m02, NxF32 m03,
-						NxF32 m10, NxF32 m11, NxF32 m12, NxF32 m13,
-				NxF32 m20, NxF32 m21, NxF32 m22, NxF32 m23,
-				NxF32 m30, NxF32 m31, NxF32 m32, NxF32 m33 )
-			:x(m00,m01,m02,m03),y(m10,m11,m12,m13),z(m20,m21,m22,m23),w(m30,m31,m32,m33){}
-	NxF32&       operator()(NxI32 r, NxI32 c)       {assert(r>=0&&r<4&&c>=0&&c<4);return ((&x)[r])[c];}
-	const NxF32& operator()(NxI32 r, NxI32 c) const {assert(r>=0&&r<4&&c>=0&&c<4);return ((&x)[r])[c];}
-		operator       NxF32* ()       {return &x.x;}
-		operator const NxF32* () const {return &x.x;}
-	operator       struct D3DXMATRIX* ()       { return (struct D3DXMATRIX*) this;}
-	operator const struct D3DXMATRIX* () const { return (struct D3DXMATRIX*) this;}
-};
-
-
-NxI32     operator==( const float4 &a, const float4 &b );
-float4 Homogenize(const float3 &v3,const NxF32 &w=1.0f); // Turns a 3D float3 4D vector4 by appending w
-float4 cmul( const float4 &a, const float4 &b);
-float4 operator*( const float4 &v, NxF32 s);
-float4 operator*( NxF32 s, const float4 &v);
-float4 operator+( const float4 &a, const float4 &b);
-float4 operator-( const float4 &a, const float4 &b);
-float4x4 operator*( const float4x4& a, const float4x4& b );
-float4 operator*( const float4& v, const float4x4& m );
-float4x4 Inverse(const float4x4 &m);
-float4x4 MatrixRigidInverse(const float4x4 &m);
-float4x4 MatrixTranspose(const float4x4 &m);
-float4x4 MatrixPerspectiveFov(NxF32 fovy, NxF32 Aspect, NxF32 zn, NxF32 zf );
-float4x4 MatrixTranslation(const float3 &t);
-float4x4 MatrixRotationZ(const NxF32 angle_radians);
-float4x4 MatrixLookAt(const float3& eye, const float3& at, const float3& up);
-NxI32     operator==( const float4x4 &a, const float4x4 &b );
-
-
-//-------- Quaternion ------------
-
-class Quaternion :public float4
-{
- public:
-	Quaternion() { x = y = z = 0.0f; w = 1.0f; }
-	Quaternion( float3 v, NxF32 t ) { v = normalize(v); w = cosf(t/2.0f); v = v*sinf(t/2.0f); x = v.x; y = v.y; z = v.z; }
-	Quaternion(NxF32 _x, NxF32 _y, NxF32 _z, NxF32 _w){x=_x;y=_y;z=_z;w=_w;}
-	NxF32 angle() const { return acosf(w)*2.0f; }
-	float3 axis() const { float3 a(x,y,z); if(fabsf(angle())<0.0000001f) return float3(1,0,0); return a*(1/sinf(angle()/2.0f)); }
-	float3 xdir() const { return float3( 1-2*(y*y+z*z),  2*(x*y+w*z),  2*(x*z-w*y) ); }
-	float3 ydir() const { return float3(   2*(x*y-w*z),1-2*(x*x+z*z),  2*(y*z+w*x) ); }
-	float3 zdir() const { return float3(   2*(x*z+w*y),  2*(y*z-w*x),1-2*(x*x+y*y) ); }
-	float3x3 getmatrix() const { return float3x3( xdir(), ydir(), zdir() ); }
-	operator float3x3() { return getmatrix(); }
-	void Normalize();
-};
-
-Quaternion& operator*=(Quaternion& a, NxF32 s );
-Quaternion	operator*( const Quaternion& a, NxF32 s );
-Quaternion	operator*( const Quaternion& a, const Quaternion& b);
-Quaternion	operator+( const Quaternion& a, const Quaternion& b );
-Quaternion	normalize( Quaternion a );
-NxF32		dot( const Quaternion &a, const Quaternion &b );
-float3		operator*( const Quaternion& q, const float3& v );
-float3		operator*( const float3& v, const Quaternion& q );
-Quaternion	slerp( Quaternion a, const Quaternion& b, NxF32 interp );
-Quaternion  Interpolate(const Quaternion &q0,const Quaternion &q1,NxF32 alpha);
-Quaternion  RotationArc(float3 v0, float3 v1 );  // returns quat q where q*v0=v1
-Quaternion  Inverse(const Quaternion &q);
-float4x4     MatrixFromQuatVec(const Quaternion &q, const float3 &v);
-
-
-//------ Euler Angle -----
-
-Quaternion YawPitchRoll( NxF32 yaw, NxF32 pitch, NxF32 roll );
-NxF32 Yaw( const Quaternion& q );
-NxF32 Pitch( const Quaternion& q );
-NxF32 Roll( Quaternion q );
-NxF32 Yaw( const float3& v );
-NxF32 Pitch( const float3& v );
-
-
-//------- Plane ----------
-
-class Plane
-{
-	public:
-	float3	normal;
-	NxF32	dist;   // distance below origin - the D from plane equasion Ax+By+Cz+D=0
-			Plane(const float3 &n,NxF32 d):normal(n),dist(d){}
-			Plane():normal(),dist(0){}
-	void	Transform(const float3 &position, const Quaternion &orientation);
-};
-
-inline Plane PlaneFlip(const Plane &plane){return Plane(-plane.normal,-plane.dist);}
-inline NxI32 operator==( const Plane &a, const Plane &b ) { return (a.normal==b.normal && a.dist==b.dist); }
-inline NxI32 coplanar( const Plane &a, const Plane &b ) { return (a==b || a==PlaneFlip(b)); }
-
-
-//--------- Utility Functions ------
-
-float3  PlaneLineIntersection(const Plane &plane, const float3 &p0, const float3 &p1);
-float3  PlaneProject(const Plane &plane, const float3 &point);
-float3  LineProject(const float3 &p0, const float3 &p1, const float3 &a);  // projects a onto infinite line p0p1
-NxF32   LineProjectTime(const float3 &p0, const float3 &p1, const float3 &a);
-float3  ThreePlaneIntersection(const Plane &p0,const Plane &p1, const Plane &p2);
-NxI32     PolyHit(const float3 *vert,const NxI32 n,const float3 &v0, const float3 &v1, float3 *impact=NULL, float3 *normal=NULL);
-NxI32     BoxInside(const float3 &p,const float3 &bmin, const float3 &bmax) ;
-NxI32     BoxIntersect(const float3 &v0, const float3 &v1, const float3 &bmin, const float3 &bmax, float3 *impact);
-NxF32   DistanceBetweenLines(const float3 &ustart, const float3 &udir, const float3 &vstart, const float3 &vdir, float3 *upoint=NULL, float3 *vpoint=NULL);
-float3  TriNormal(const float3 &v0, const float3 &v1, const float3 &v2);
-float3  NormalOf(const float3 *vert, const NxI32 n);
-Quaternion VirtualTrackBall(const float3 &cop, const float3 &cor, const float3 &dir0, const float3 &dir1);
-
-
-
-
-//*****************************************************
-// ** VECMATH.CPP
-//*****************************************************
-
-
-NxF32   sqr(NxF32 a) {return a*a;}
-NxF32   clampf(NxF32 a) {return Min(1.0f,Max(0.0f,a));}
-
-
-NxF32 Round(NxF32 a,NxF32 precision)
-{
-	return floorf(0.5f+a/precision)*precision;
-}
-
-
-NxF32 Interpolate(const NxF32 &f0,const NxF32 &f1,NxF32 alpha)
-{
-	return f0*(1-alpha) + f1*alpha;
-}
-
-
-NxI32     argmin(NxF32 a[],NxI32 n)
-{
-	NxI32 r=0;
-	for(NxI32 i=1;i<n;i++)
-		{
-		if(a[i]<a[r])
-				{
-			r = i;
-		}
-	}
-	return r;
-}
-
-
-
-//------------ float3 (3D) --------------
-
-
-
-float3 operator+( const float3& a, const float3& b )
-{
-	return float3(a.x+b.x, a.y+b.y, a.z+b.z);
-}
-
-
-float3 operator-( const float3& a, const float3& b )
-{
-	return float3( a.x-b.x, a.y-b.y, a.z-b.z );
-}
-
-
-float3 operator-( const float3& v )
-{
-	return float3( -v.x, -v.y, -v.z );
-}
-
-
-float3 operator*( const float3& v, NxF32 s )
-{
-	return float3( v.x*s, v.y*s, v.z*s );
-}
-
-
-float3 operator*( NxF32 s, const float3& v )
-{
-	return float3( v.x*s, v.y*s, v.z*s );
-}
-
-
-float3 operator/( const float3& v, NxF32 s )
-{
-	return v*(1.0f/s);
-}
-
-NxF32  dot( const float3& a, const float3& b )
-{
-	return a.x*b.x + a.y*b.y + a.z*b.z;
-}
-
-float3 cmul( const float3 &v1, const float3 &v2)
-{
-	return float3(v1.x*v2.x, v1.y*v2.y, v1.z*v2.z);
-}
-
-
-float3 cross( const float3& a, const float3& b )
-{
-		return float3( a.y*b.z - a.z*b.y,
-									 a.z*b.x - a.x*b.z,
-									 a.x*b.y - a.y*b.x );
-}
-
-
-
-
-float3& operator+=( float3& a , const float3& b )
-{
-		a.x += b.x;
-		a.y += b.y;
-		a.z += b.z;
-		return a;
-}
-
-
-float3& operator-=( float3& a , const float3& b )
-{
-		a.x -= b.x;
-		a.y -= b.y;
-		a.z -= b.z;
-		return a;
-}
-
-
-float3& operator*=(float3& v , NxF32 s )
-{
-		v.x *= s;
-		v.y *= s;
-		v.z *= s;
-		return v;
-}
-
-
-float3& operator/=(float3& v , NxF32 s )
-{
-		NxF32 sinv = 1.0f / s;
-		v.x *= sinv;
-		v.y *= sinv;
-		v.z *= sinv;
-		return v;
-}
-
-float3 vabs(const float3 &v)
-{
-	return float3(fabsf(v.x),fabsf(v.y),fabsf(v.z));
-}
-
-
-NxF32 magnitude( const float3& v )
-{
-		return sqrtf(sqr(v.x) + sqr( v.y)+ sqr(v.z));
-}
-
-
-
-float3 normalize( const float3 &v )
-{
-	// this routine, normalize, is ok, provided magnitude works!!
-		NxF32 d=magnitude(v);
-		if (d==0)
-		{
-		printf("Cant normalize ZERO vector\n");
-		assert(0);// yes this could go here
-		d=0.1f;
-	}
-	d = 1/d;
-	return float3(v.x*d,v.y*d,v.z*d);
-}
-
-float3 safenormalize(const float3 &v)
-{
-	if(magnitude(v)<=0.0f)
-	{
-		return float3(1,0,0);
-	}
-	return normalize(v);
-}
-
-float3 Round(const float3 &a,NxF32 precision)
-{
-	return float3(Round(a.x,precision),Round(a.y,precision),Round(a.z,precision));
-}
-
-
-float3 Interpolate(const float3 &v0,const float3 &v1,NxF32 alpha)
-{
-	return v0*(1-alpha) + v1*alpha;
-}
-
-float3 VectorMin(const float3 &a,const float3 &b)
-{
-	return float3(Min(a.x,b.x),Min(a.y,b.y),Min(a.z,b.z));
-}
-float3 VectorMax(const float3 &a,const float3 &b)
-{
-	return float3(Max(a.x,b.x),Max(a.y,b.y),Max(a.z,b.z));
-}
-
-// the statement v1*v2 is ambiguous since there are 3 types
-// of vector multiplication
-//  - componantwise (for example combining colors)
-//  - dot product
-//  - cross product
-// Therefore we never declare/implement this function.
-// So we will never see:  float3 operator*(float3 a,float3 b)
-
-
-
-
-//------------ float3x3 ---------------
-NxF32 Determinant(const float3x3 &m)
-{
-	return  m.x.x*m.y.y*m.z.z + m.y.x*m.z.y*m.x.z + m.z.x*m.x.y*m.y.z
-			 -m.x.x*m.z.y*m.y.z - m.y.x*m.x.y*m.z.z - m.z.x*m.y.y*m.x.z ;
-}
-
-float3x3 Inverse(const float3x3 &a)
-{
-	float3x3 b;
-	NxF32 d=Determinant(a);
-	assert(d!=0);
-	for(NxI32 i=0;i<3;i++)
-		{
-		for(NxI32 j=0;j<3;j++)
-				{
-			NxI32 i1=(i+1)%3;
-			NxI32 i2=(i+2)%3;
-			NxI32 j1=(j+1)%3;
-			NxI32 j2=(j+2)%3;
-			// reverse indexs i&j to take transpose
-			b[j][i] = (a[i1][j1]*a[i2][j2]-a[i1][j2]*a[i2][j1])/d;
-		}
-	}
-	// Matrix check=a*b; // Matrix 'check' should be the identity (or close to it)
-	return b;
-}
-
-
-float3x3 Transpose( const float3x3& m )
-{
-	return float3x3( float3(m.x.x,m.y.x,m.z.x),
-					float3(m.x.y,m.y.y,m.z.y),
-					float3(m.x.z,m.y.z,m.z.z));
-}
-
-
-float3 operator*(const float3& v , const float3x3 &m ) {
-	return float3((m.x.x*v.x + m.y.x*v.y + m.z.x*v.z),
-					(m.x.y*v.x + m.y.y*v.y + m.z.y*v.z),
-					(m.x.z*v.x + m.y.z*v.y + m.z.z*v.z));
-}
-float3 operator*(const float3x3 &m,const float3& v  ) {
-	return float3(dot(m.x,v),dot(m.y,v),dot(m.z,v));
-}
-
-
-float3x3 operator*( const float3x3& a, const float3x3& b )
-{
-	return float3x3(a.x*b,a.y*b,a.z*b);
-}
-
-float3x3 operator*( const float3x3& a, const NxF32& s )
-{
-	return float3x3(a.x*s, a.y*s ,a.z*s);
-}
-float3x3 operator/( const float3x3& a, const NxF32& s )
-{
-	NxF32 t=1/s;
-	return float3x3(a.x*t, a.y*t ,a.z*t);
-}
-float3x3 operator+( const float3x3& a, const float3x3& b )
-{
-	return float3x3(a.x+b.x, a.y+b.y, a.z+b.z);
-}
-float3x3 operator-( const float3x3& a, const float3x3& b )
-{
-	return float3x3(a.x-b.x, a.y-b.y, a.z-b.z);
-}
-float3x3 &operator+=( float3x3& a, const float3x3& b )
-{
-	a.x+=b.x;
-	a.y+=b.y;
-	a.z+=b.z;
-	return a;
-}
-float3x3 &operator-=( float3x3& a, const float3x3& b )
-{
-	a.x-=b.x;
-	a.y-=b.y;
-	a.z-=b.z;
-	return a;
-}
-float3x3 &operator*=( float3x3& a, const NxF32& s )
-{
-	a.x*=s;
-	a.y*=s;
-	a.z*=s;
-	return a;
-}
-
-
-
-float3 ThreePlaneIntersection(const Plane &p0,const Plane &p1, const Plane &p2){
-	float3x3 mp =Transpose(float3x3(p0.normal,p1.normal,p2.normal));
-	float3x3 mi = Inverse(mp);
-	float3 b(p0.dist,p1.dist,p2.dist);
-	return -b * mi;
-}
-
-
-//--------------- 4D ----------------
-
-float4   operator*( const float4&   v, const float4x4& m )
-{
-	return v.x*m.x + v.y*m.y + v.z*m.z + v.w*m.w; // yes this actually works
-}
-
-NxI32 operator==( const float4 &a, const float4 &b )
-{
-	return (a.x==b.x && a.y==b.y && a.z==b.z && a.w==b.w);
-}
-
-
-//  Dont implement m*v for now, since that might confuse us
-//  All our transforms are based on multiplying the "row" vector on the left
-//float4   operator*(const float4x4& m , const float4&   v )
-//{
-//	return float4(dot(v,m.x),dot(v,m.y),dot(v,m.z),dot(v,m.w));
-//}
-
-
-
-float4 cmul( const float4 &a, const float4 &b)
-{
-	return float4(a.x*b.x,a.y*b.y,a.z*b.z,a.w*b.w);
-}
-
-
-float4 operator*( const float4 &v, NxF32 s)
-{
-	return float4(v.x*s,v.y*s,v.z*s,v.w*s);
-}
-
-
-float4 operator*( NxF32 s, const float4 &v)
-{
-	return float4(v.x*s,v.y*s,v.z*s,v.w*s);
-}
-
-
-float4 operator+( const float4 &a, const float4 &b)
-{
-	return float4(a.x+b.x,a.y+b.y,a.z+b.z,a.w+b.w);
-}
-
-
-
-float4 operator-( const float4 &a, const float4 &b)
-{
-	return float4(a.x-b.x,a.y-b.y,a.z-b.z,a.w-b.w);
-}
-
-
-float4 Homogenize(const float3 &v3,const NxF32 &w)
-{
-	return float4(v3.x,v3.y,v3.z,w);
-}
-
-
-
-float4x4 operator*( const float4x4& a, const float4x4& b )
-{
-	return float4x4(a.x*b,a.y*b,a.z*b,a.w*b);
-}
-
-float4x4 MatrixTranspose(const float4x4 &m)
-{
-	return float4x4(
-		m.x.x, m.y.x, m.z.x, m.w.x,
-		m.x.y, m.y.y, m.z.y, m.w.y,
-		m.x.z, m.y.z, m.z.z, m.w.z,
-		m.x.w, m.y.w, m.z.w, m.w.w );
-}
-
-float4x4 MatrixRigidInverse(const float4x4 &m)
-{
-	float4x4 trans_inverse = MatrixTranslation(-m.w.xyz());
-	float4x4 rot   = m;
-	rot.w = float4(0,0,0,1);
-	return trans_inverse * MatrixTranspose(rot);
-}
-
-
-float4x4 MatrixPerspectiveFov(NxF32 fovy, NxF32 aspect, NxF32 zn, NxF32 zf )
-{
-	NxF32 h = 1.0f/tanf(fovy/2.0f); // view space height
-	NxF32 w = h / aspect ;  // view space width
-	return float4x4(
-		w, 0, 0             ,   0,
-		0, h, 0             ,   0,
-		0, 0, zf/(zn-zf)    ,  -1,
-		0, 0, zn*zf/(zn-zf) ,   0 );
-}
-
-
-
-float4x4 MatrixLookAt(const float3& eye, const float3& at, const float3& up)
-{
-	float4x4 m;
-	m.w.w = 1.0f;
-	m.w.xyz() = eye;
-	m.z.xyz() = normalize(eye-at);
-	m.x.xyz() = normalize(cross(up,m.z.xyz()));
-	m.y.xyz() = cross(m.z.xyz(),m.x.xyz());
-	return MatrixRigidInverse(m);
-}
-
-
-float4x4 MatrixTranslation(const float3 &t)
-{
-	return float4x4(
-		1,  0,  0,  0,
-		0,  1,  0,  0,
-		0,  0,  1,  0,
-		t.x,t.y,t.z,1 );
-}
-
-
-float4x4 MatrixRotationZ(const NxF32 angle_radians)
-{
-	NxF32 s =  sinf(angle_radians);
-	NxF32 c =  cosf(angle_radians);
-	return float4x4(
-		c,  s,  0,  0,
-		-s, c,  0,  0,
-		0,  0,  1,  0,
-		0,  0,  0,  1 );
-}
-
-
-
-NxI32 operator==( const float4x4 &a, const float4x4 &b )
-{
-	return (a.x==b.x && a.y==b.y && a.z==b.z && a.w==b.w);
-}
-
-
-float4x4 Inverse(const float4x4 &m)
-{
-	float4x4 d;
-	NxF32 *dst = &d.x.x;
-	NxF32 tmp[12]; /* temp array for pairs */
-	NxF32 src[16]; /* array of transpose source matrix */
-	NxF32 det; /* determinant */
-	/* transpose matrix */
-	for ( NxI32 i = 0; i < 4; i++) {
-		src[i] = m(i,0) ;
-		src[i + 4] = m(i,1);
-		src[i + 8] = m(i,2);
-		src[i + 12] = m(i,3);
-	}
-	/* calculate pairs for first 8 elements (cofactors) */
-	tmp[0]  = src[10] * src[15];
-	tmp[1]  = src[11] * src[14];
-	tmp[2]  = src[9] * src[15];
-	tmp[3]  = src[11] * src[13];
-	tmp[4]  = src[9] * src[14];
-	tmp[5]  = src[10] * src[13];
-	tmp[6]  = src[8] * src[15];
-	tmp[7]  = src[11] * src[12];
-	tmp[8]  = src[8] * src[14];
-	tmp[9]  = src[10] * src[12];
-	tmp[10] = src[8] * src[13];
-	tmp[11] = src[9] * src[12];
-	/* calculate first 8 elements (cofactors) */
-	dst[0]  = tmp[0]*src[5] + tmp[3]*src[6] + tmp[4]*src[7];
-	dst[0] -= tmp[1]*src[5] + tmp[2]*src[6] + tmp[5]*src[7];
-	dst[1]  = tmp[1]*src[4] + tmp[6]*src[6] + tmp[9]*src[7];
-	dst[1] -= tmp[0]*src[4] + tmp[7]*src[6] + tmp[8]*src[7];
-	dst[2]  = tmp[2]*src[4] + tmp[7]*src[5] + tmp[10]*src[7];
-	dst[2] -= tmp[3]*src[4] + tmp[6]*src[5] + tmp[11]*src[7];
-	dst[3]  = tmp[5]*src[4] + tmp[8]*src[5] + tmp[11]*src[6];
-	dst[3] -= tmp[4]*src[4] + tmp[9]*src[5] + tmp[10]*src[6];
-	dst[4]  = tmp[1]*src[1] + tmp[2]*src[2] + tmp[5]*src[3];
-	dst[4] -= tmp[0]*src[1] + tmp[3]*src[2] + tmp[4]*src[3];
-	dst[5]  = tmp[0]*src[0] + tmp[7]*src[2] + tmp[8]*src[3];
-	dst[5] -= tmp[1]*src[0] + tmp[6]*src[2] + tmp[9]*src[3];
-	dst[6]  = tmp[3]*src[0] + tmp[6]*src[1] + tmp[11]*src[3];
-	dst[6] -= tmp[2]*src[0] + tmp[7]*src[1] + tmp[10]*src[3];
-	dst[7]  = tmp[4]*src[0] + tmp[9]*src[1] + tmp[10]*src[2];
-	dst[7] -= tmp[5]*src[0] + tmp[8]*src[1] + tmp[11]*src[2];
-	/* calculate pairs for second 8 elements (cofactors) */
-	tmp[0]  = src[2]*src[7];
-	tmp[1]  = src[3]*src[6];
-	tmp[2]  = src[1]*src[7];
-	tmp[3]  = src[3]*src[5];
-	tmp[4]  = src[1]*src[6];
-	tmp[5]  = src[2]*src[5];
-	tmp[6]  = src[0]*src[7];
-	tmp[7]  = src[3]*src[4];
-	tmp[8]  = src[0]*src[6];
-	tmp[9]  = src[2]*src[4];
-	tmp[10] = src[0]*src[5];
-	tmp[11] = src[1]*src[4];
-	/* calculate second 8 elements (cofactors) */
-	dst[8]  = tmp[0]*src[13] + tmp[3]*src[14] + tmp[4]*src[15];
-	dst[8] -= tmp[1]*src[13] + tmp[2]*src[14] + tmp[5]*src[15];
-	dst[9]  = tmp[1]*src[12] + tmp[6]*src[14] + tmp[9]*src[15];
-	dst[9] -= tmp[0]*src[12] + tmp[7]*src[14] + tmp[8]*src[15];
-	dst[10] = tmp[2]*src[12] + tmp[7]*src[13] + tmp[10]*src[15];
-	dst[10]-= tmp[3]*src[12] + tmp[6]*src[13] + tmp[11]*src[15];
-	dst[11] = tmp[5]*src[12] + tmp[8]*src[13] + tmp[11]*src[14];
-	dst[11]-= tmp[4]*src[12] + tmp[9]*src[13] + tmp[10]*src[14];
-	dst[12] = tmp[2]*src[10] + tmp[5]*src[11] + tmp[1]*src[9];
-	dst[12]-= tmp[4]*src[11] + tmp[0]*src[9] + tmp[3]*src[10];
-	dst[13] = tmp[8]*src[11] + tmp[0]*src[8] + tmp[7]*src[10];
-	dst[13]-= tmp[6]*src[10] + tmp[9]*src[11] + tmp[1]*src[8];
-	dst[14] = tmp[6]*src[9] + tmp[11]*src[11] + tmp[3]*src[8];
-	dst[14]-= tmp[10]*src[11] + tmp[2]*src[8] + tmp[7]*src[9];
-	dst[15] = tmp[10]*src[10] + tmp[4]*src[8] + tmp[9]*src[9];
-	dst[15]-= tmp[8]*src[9] + tmp[11]*src[10] + tmp[5]*src[8];
-	/* calculate determinant */
-	det=src[0]*dst[0]+src[1]*dst[1]+src[2]*dst[2]+src[3]*dst[3];
-	/* calculate matrix inverse */
-	det = 1/det;
-	for ( NxI32 j = 0; j < 16; j++)
-	dst[j] *= det;
-	return d;
-}
-
-
-//--------- Quaternion --------------
-
-Quaternion operator*( const Quaternion& a, const Quaternion& b )
-{
-	Quaternion c;
-	c.w = a.w*b.w - a.x*b.x - a.y*b.y - a.z*b.z;
-	c.x = a.w*b.x + a.x*b.w + a.y*b.z - a.z*b.y;
-	c.y = a.w*b.y - a.x*b.z + a.y*b.w + a.z*b.x;
-	c.z = a.w*b.z + a.x*b.y - a.y*b.x + a.z*b.w;
-	return c;
-}
-
-
-Quaternion operator*( const Quaternion& a, NxF32 b )
-{
-	return Quaternion(a.x*b, a.y*b, a.z*b ,a.w*b);
-}
-
-Quaternion  Inverse(const Quaternion &q)
-{
-	return Quaternion(-q.x,-q.y,-q.z,q.w);
-}
-
-Quaternion& operator*=( Quaternion& q, const NxF32 s )
-{
-		q.x *= s;
-		q.y *= s;
-		q.z *= s;
-		q.w *= s;
-		return q;
-}
-void Quaternion::Normalize()
-{
-	NxF32 m = sqrtf(sqr(w)+sqr(x)+sqr(y)+sqr(z));
-	if(m<0.000000001f) {
-		w=1.0f;
-		x=y=z=0.0f;
-		return;
-	}
-	(*this) *= (1.0f/m);
-}
-
-float3 operator*( const Quaternion& q, const float3& v )
-{
-	// The following is equivalent to:
-	//return (q.getmatrix() * v);
-	NxF32 qx2 = q.x*q.x;
-	NxF32 qy2 = q.y*q.y;
-	NxF32 qz2 = q.z*q.z;
-
-	NxF32 qxqy = q.x*q.y;
-	NxF32 qxqz = q.x*q.z;
-	NxF32 qxqw = q.x*q.w;
-	NxF32 qyqz = q.y*q.z;
-	NxF32 qyqw = q.y*q.w;
-	NxF32 qzqw = q.z*q.w;
-	return float3(
-		(1-2*(qy2+qz2))*v.x + (2*(qxqy-qzqw))*v.y + (2*(qxqz+qyqw))*v.z ,
-		(2*(qxqy+qzqw))*v.x + (1-2*(qx2+qz2))*v.y + (2*(qyqz-qxqw))*v.z ,
-		(2*(qxqz-qyqw))*v.x + (2*(qyqz+qxqw))*v.y + (1-2*(qx2+qy2))*v.z  );
-}
-
-Quaternion operator+( const Quaternion& a, const Quaternion& b )
-{
-	return Quaternion(a.x+b.x, a.y+b.y, a.z+b.z, a.w+b.w);
-}
-
-NxF32 dot( const Quaternion &a,const Quaternion &b )
-{
-	return  (a.w*b.w + a.x*b.x + a.y*b.y + a.z*b.z);
-}
-
-Quaternion normalize( Quaternion a )
-{
-	NxF32 m = sqrtf(sqr(a.w)+sqr(a.x)+sqr(a.y)+sqr(a.z));
-	if(m<0.000000001)
-		{
-		a.w=1;
-		a.x=a.y=a.z=0;
-		return a;
-	}
-	return a * (1/m);
-}
-
-Quaternion slerp( Quaternion a, const Quaternion& b, NxF32 interp )
-{
-	if(dot(a,b) <0.0)
-		{
-		a.w=-a.w;
-		a.x=-a.x;
-		a.y=-a.y;
-		a.z=-a.z;
-	}
-	NxF32 d = dot(a,b);
-	if(d>=1.0) {
-		return a;
-	}
-	NxF32 theta = acosf(d);
-	if(theta==0.0f) { return(a);}
-	return a*(sinf(theta-interp*theta)/sinf(theta)) + b*(sinf(interp*theta)/sinf(theta));
-}
-
-
-Quaternion Interpolate(const Quaternion &q0,const Quaternion &q1,NxF32 alpha) {
-	return slerp(q0,q1,alpha);
-}
-
-
-Quaternion YawPitchRoll( NxF32 yaw, NxF32 pitch, NxF32 roll )
-{
-	roll  *= DEG2RAD;
-	yaw   *= DEG2RAD;
-	pitch *= DEG2RAD;
-	return Quaternion(float3(0.0f,0.0f,1.0f),yaw)*Quaternion(float3(1.0f,0.0f,0.0f),pitch)*Quaternion(float3(0.0f,1.0f,0.0f),roll);
-}
-
-NxF32 Yaw( const Quaternion& q )
-{
-	static float3 v;
-	v=q.ydir();
-	return (v.y==0.0&&v.x==0.0) ? 0.0f: atan2f(-v.x,v.y)*RAD2DEG;
-}
-
-NxF32 Pitch( const Quaternion& q )
-{
-	static float3 v;
-	v=q.ydir();
-	return atan2f(v.z,sqrtf(sqr(v.x)+sqr(v.y)))*RAD2DEG;
-}
-
-NxF32 Roll( Quaternion q )
-{
-	q = Quaternion(float3(0.0f,0.0f,1.0f),-Yaw(q)*DEG2RAD)  *q;
-	q = Quaternion(float3(1.0f,0.0f,0.0f),-Pitch(q)*DEG2RAD)  *q;
-	return atan2f(-q.xdir().z,q.xdir().x)*RAD2DEG;
-}
-
-NxF32 Yaw( const float3& v )
-{
-	return (v.y==0.0&&v.x==0.0) ? 0.0f: atan2f(-v.x,v.y)*RAD2DEG;
-}
-
-NxF32 Pitch( const float3& v )
-{
-	return atan2f(v.z,sqrtf(sqr(v.x)+sqr(v.y)))*RAD2DEG;
-}
-
-
-//------------- Plane --------------
-
-
-void Plane::Transform(const float3 &position, const Quaternion &orientation) {
-	//   Transforms the plane to the space defined by the
-	//   given position/orientation.
-	static float3 newnormal;
-	static float3 origin;
-
-	newnormal = Inverse(orientation)*normal;
-	origin = Inverse(orientation)*(-normal*dist - position);
-
-	normal = newnormal;
-	dist = -dot(newnormal, origin);
-}
-
-
-
-
-//--------- utility functions -------------
-
-//        RotationArc()
-// Given two vectors v0 and v1 this function
-// returns quaternion q where q*v0==v1.
-// Routine taken from game programming gems.
-Quaternion RotationArc(float3 v0,float3 v1){
-	static Quaternion q;
-	v0 = normalize(v0);  // Comment these two lines out if you know its not needed.
-	v1 = normalize(v1);  // If vector is already unit length then why do it again?
-	float3  c = cross(v0,v1);
-	NxF32   d = dot(v0,v1);
-	if(d<=-1.0f) { return Quaternion(1,0,0,0);} // 180 about x axis
-	NxF32   s = sqrtf((1+d)*2);
-	q.x = c.x / s;
-	q.y = c.y / s;
-	q.z = c.z / s;
-	q.w = s /2.0f;
-	return q;
-}
-
-
-float4x4 MatrixFromQuatVec(const Quaternion &q, const float3 &v)
-{
-	// builds a 4x4 transformation matrix based on orientation q and translation v
-	NxF32 qx2 = q.x*q.x;
-	NxF32 qy2 = q.y*q.y;
-	NxF32 qz2 = q.z*q.z;
-
-	NxF32 qxqy = q.x*q.y;
-	NxF32 qxqz = q.x*q.z;
-	NxF32 qxqw = q.x*q.w;
-	NxF32 qyqz = q.y*q.z;
-	NxF32 qyqw = q.y*q.w;
-	NxF32 qzqw = q.z*q.w;
-
-	return float4x4(
-		1-2*(qy2+qz2),
-		2*(qxqy+qzqw),
-		2*(qxqz-qyqw),
-		0            ,
-		2*(qxqy-qzqw),
-		1-2*(qx2+qz2),
-		2*(qyqz+qxqw),
-		0            ,
-		2*(qxqz+qyqw),
-		2*(qyqz-qxqw),
-		1-2*(qx2+qy2),
-		0    ,
-		 v.x ,
-		 v.y ,
-		 v.z ,
-		 1.0f );
-}
-
-
-float3 PlaneLineIntersection(const Plane &plane, const float3 &p0, const float3 &p1)
-{
-	// returns the point where the line p0-p1 intersects the plane n&d
-				static float3 dif;
-		dif = p1-p0;
-				NxF32 dn= dot(plane.normal,dif);
-				NxF32 t = -(plane.dist+dot(plane.normal,p0) )/dn;
-				return p0 + (dif*t);
-}
-
-float3 PlaneProject(const Plane &plane, const float3 &point)
-{
-	return point - plane.normal * (dot(point,plane.normal)+plane.dist);
-}
-
-float3 LineProject(const float3 &p0, const float3 &p1, const float3 &a)
-{
-	float3 w;
-	w = p1-p0;
-	NxF32 t= dot(w,(a-p0)) / (sqr(w.x)+sqr(w.y)+sqr(w.z));
-	return p0+ w*t;
-}
-
-
-NxF32 LineProjectTime(const float3 &p0, const float3 &p1, const float3 &a)
-{
-	float3 w;
-	w = p1-p0;
-	NxF32 t= dot(w,(a-p0)) / (sqr(w.x)+sqr(w.y)+sqr(w.z));
-	return t;
-}
-
-
-
-float3 TriNormal(const float3 &v0, const float3 &v1, const float3 &v2)
-{
-	// return the normal of the triangle
-	// inscribed by v0, v1, and v2
-	float3 cp=cross(v1-v0,v2-v1);
-	NxF32 m=magnitude(cp);
-	if(m==0) return float3(1,0,0);
-	return cp*(1.0f/m);
-}
-
-
-
-NxI32 BoxInside(const float3 &p, const float3 &bmin, const float3 &bmax)
-{
-	return (p.x >= bmin.x && p.x <=bmax.x &&
-			p.y >= bmin.y && p.y <=bmax.y &&
-			p.z >= bmin.z && p.z <=bmax.z );
-}
-
-
-NxI32 BoxIntersect(const float3 &v0, const float3 &v1, const float3 &bmin, const float3 &bmax,float3 *impact)
-{
-	if(BoxInside(v0,bmin,bmax))
-		{
-				*impact=v0;
-				return 1;
-		}
-	if(v0.x<=bmin.x && v1.x>=bmin.x)
-		{
-		NxF32 a = (bmin.x-v0.x)/(v1.x-v0.x);
-		//v.x = bmin.x;
-		NxF32 vy =  (1-a) *v0.y + a*v1.y;
-		NxF32 vz =  (1-a) *v0.z + a*v1.z;
-		if(vy>=bmin.y && vy<=bmax.y && vz>=bmin.z && vz<=bmax.z)
-				{
-			impact->x = bmin.x;
-			impact->y = vy;
-			impact->z = vz;
-			return 1;
-		}
-	}
-	else if(v0.x >= bmax.x  &&  v1.x <= bmax.x)
-		{
-		NxF32 a = (bmax.x-v0.x)/(v1.x-v0.x);
-		//v.x = bmax.x;
-		NxF32 vy =  (1-a) *v0.y + a*v1.y;
-		NxF32 vz =  (1-a) *v0.z + a*v1.z;
-		if(vy>=bmin.y && vy<=bmax.y && vz>=bmin.z && vz<=bmax.z)
-				{
-			impact->x = bmax.x;
-			impact->y = vy;
-			impact->z = vz;
-			return 1;
-		}
-	}
-	if(v0.y<=bmin.y && v1.y>=bmin.y)
-		{
-		NxF32 a = (bmin.y-v0.y)/(v1.y-v0.y);
-		NxF32 vx =  (1-a) *v0.x + a*v1.x;
-		//v.y = bmin.y;
-		NxF32 vz =  (1-a) *v0.z + a*v1.z;
-		if(vx>=bmin.x && vx<=bmax.x && vz>=bmin.z && vz<=bmax.z)
-				{
-			impact->x = vx;
-			impact->y = bmin.y;
-			impact->z = vz;
-			return 1;
-		}
-	}
-	else if(v0.y >= bmax.y  &&  v1.y <= bmax.y)
-		{
-		NxF32 a = (bmax.y-v0.y)/(v1.y-v0.y);
-		NxF32 vx =  (1-a) *v0.x + a*v1.x;
-		// vy = bmax.y;
-		NxF32 vz =  (1-a) *v0.z + a*v1.z;
-		if(vx>=bmin.x && vx<=bmax.x && vz>=bmin.z && vz<=bmax.z)
-				{
-			impact->x = vx;
-			impact->y = bmax.y;
-			impact->z = vz;
-			return 1;
-		}
-	}
-	if(v0.z<=bmin.z && v1.z>=bmin.z)
-		{
-		NxF32 a = (bmin.z-v0.z)/(v1.z-v0.z);
-		NxF32 vx =  (1-a) *v0.x + a*v1.x;
-		NxF32 vy =  (1-a) *v0.y + a*v1.y;
-		// v.z = bmin.z;
-		if(vy>=bmin.y && vy<=bmax.y && vx>=bmin.x && vx<=bmax.x)
-				{
-			impact->x = vx;
-			impact->y = vy;
-			impact->z = bmin.z;
-			return 1;
-		}
-	}
-	else if(v0.z >= bmax.z  &&  v1.z <= bmax.z)
-		{
-		NxF32 a = (bmax.z-v0.z)/(v1.z-v0.z);
-		NxF32 vx =  (1-a) *v0.x + a*v1.x;
-		NxF32 vy =  (1-a) *v0.y + a*v1.y;
-		// v.z = bmax.z;
-		if(vy>=bmin.y && vy<=bmax.y && vx>=bmin.x && vx<=bmax.x)
-				{
-			impact->x = vx;
-			impact->y = vy;
-			impact->z = bmax.z;
-			return 1;
-		}
-	}
-	return 0;
-}
-
-
-NxF32 DistanceBetweenLines(const float3 &ustart, const float3 &udir, const float3 &vstart, const float3 &vdir, float3 *upoint, float3 *vpoint)
-{
-	static float3 cp;
-	cp = normalize(cross(udir,vdir));
-
-	NxF32 distu = -dot(cp,ustart);
-	NxF32 distv = -dot(cp,vstart);
-	NxF32 dist = (NxF32)fabs(distu-distv);
-	if(upoint)
-		{
-		Plane plane;
-		plane.normal = normalize(cross(vdir,cp));
-		plane.dist = -dot(plane.normal,vstart);
-		*upoint = PlaneLineIntersection(plane,ustart,ustart+udir);
-	}
-	if(vpoint)
-		{
-		Plane plane;
-		plane.normal = normalize(cross(udir,cp));
-		plane.dist = -dot(plane.normal,ustart);
-		*vpoint = PlaneLineIntersection(plane,vstart,vstart+vdir);
-	}
-	return dist;
-}
-
-
-Quaternion VirtualTrackBall(const float3 &cop, const float3 &cor, const float3 &dir1, const float3 &dir2)
-{
-	// routine taken from game programming gems.
-	// Implement track ball functionality to spin stuf on the screen
-	//  cop   center of projection
-	//  cor   center of rotation
-	//  dir1  old mouse direction
-	//  dir2  new mouse direction
-	// pretend there is a sphere around cor.  Then find the points
-	// where dir1 and dir2 intersect that sphere.  Find the
-	// rotation that takes the first point to the second.
-	NxF32 m;
-	// compute plane
-	float3 nrml = cor - cop;
-	NxF32 fudgefactor = 1.0f/(magnitude(nrml) * 0.25f); // since trackball proportional to distance from cop
-	nrml = normalize(nrml);
-	NxF32 dist = -dot(nrml,cor);
-	float3 u= PlaneLineIntersection(Plane(nrml,dist),cop,cop+dir1);
-	u=u-cor;
-	u=u*fudgefactor;
-	m= magnitude(u);
-	if(m>1)
-		{
-				u/=m;
-		}
-	else
-		{
-		u=u - (nrml * sqrtf(1-m*m));
-	}
-	float3 v= PlaneLineIntersection(Plane(nrml,dist),cop,cop+dir2);
-	v=v-cor;
-	v=v*fudgefactor;
-	m= magnitude(v);
-	if(m>1)
-		{
-				v/=m;
-		}
-	else
-		{
-		v=v - (nrml * sqrtf(1-m*m));
-	}
-	return RotationArc(u,v);
-}
-
-
-NxI32 countpolyhit=0;
-NxI32 PolyHit(const float3 *vert, const NxI32 n, const float3 &v0, const float3 &v1, float3 *impact, float3 *normal)
-{
-	countpolyhit++;
-	NxI32 i;
-	float3 nrml(0,0,0);
-	for(i=0;i<n;i++)
-		{
-		NxI32 i1=(i+1)%n;
-		NxI32 i2=(i+2)%n;
-		nrml = nrml + cross(vert[i1]-vert[i],vert[i2]-vert[i1]);
-	}
-
-	NxF32 m = magnitude(nrml);
-	if(m==0.0)
-		{
-				return 0;
-		}
-	nrml = nrml * (1.0f/m);
-	NxF32 dist = -dot(nrml,vert[0]);
-	NxF32 d0,d1;
-	if((d0=dot(v0,nrml)+dist) <0  ||  (d1=dot(v1,nrml)+dist) >0)
-		{
-				return 0;
-		}
-
-	static float3 the_point;
-	// By using the cached plane distances d0 and d1
-	// we can optimize the following:
-	//     the_point = planelineintersection(nrml,dist,v0,v1);
-	NxF32 a = d0/(d0-d1);
-	the_point = v0*(1-a) + v1*a;
-
-
-	NxI32 inside=1;
-	for(NxI32 j=0;inside && j<n;j++)
-		{
-			// let inside = 0 if outside
-			float3 pp1,pp2,side;
-			pp1 = vert[j] ;
-			pp2 = vert[(j+1)%n];
-			side = cross((pp2-pp1),(the_point-pp1));
-			inside = (dot(nrml,side) >= 0.0);
-	}
-	if(inside)
-		{
-		if(normal){*normal=nrml;}
-		if(impact){*impact=the_point;}
-	}
-	return inside;
-}
-
-//****************************************************
-// HULL.H source code goes here
-//****************************************************
-class PHullResult
-{
-public:
-
-	PHullResult(void)
-	{
-		mVcount = 0;
-		mIndexCount = 0;
-		mFaceCount = 0;
-		mVertices = 0;
-		mIndices  = 0;
-	}
-
-	NxU32 mVcount;
-	NxU32 mIndexCount;
-	NxU32 mFaceCount;
-	NxF32       *mVertices;
-	NxU32 *mIndices;
-};
-
-bool ComputeHull(NxU32 vcount,const NxF32 *vertices,PHullResult &result,NxU32 maxverts,NxF32 inflate);
-void ReleaseHull(PHullResult &result);
-
-//*****************************************************
-// HULL.cpp source code goes here
-//*****************************************************
-
-
-#define REAL3 float3
-#define REAL  NxF32
-
-#define COPLANAR   (0)
-#define UNDER      (1)
-#define OVER       (2)
-#define SPLIT      (OVER|UNDER)
-#define PAPERWIDTH (0.001f)
-#define VOLUME_EPSILON (1e-20f)
-
-NxF32 planetestepsilon = PAPERWIDTH;
-
-class ConvexH : public Memalloc
-{
-  public:
-	class HalfEdge
-	{
-	  public:
-		short ea;         // the other half of the edge (index into edges list)
-		NxU8 v;  // the vertex at the start of this edge (index into vertices list)
-		NxU8 p;  // the facet on which this edge lies (index into facets list)
-		HalfEdge(){}
-		HalfEdge(short _ea,NxU8 _v, NxU8 _p):ea(_ea),v(_v),p(_p){}
-	};
-	Array<REAL3> vertices;
-	Array<HalfEdge> edges;
-	Array<Plane>  facets;
-	ConvexH(NxI32 vertices_size,NxI32 edges_size,NxI32 facets_size);
-};
-
-typedef ConvexH::HalfEdge HalfEdge;
-
-ConvexH::ConvexH(NxI32 vertices_size,NxI32 edges_size,NxI32 facets_size)
-	:vertices(vertices_size)
-	,edges(edges_size)
-	,facets(facets_size)
-{
-	vertices.count=vertices_size;
-	edges.count   = edges_size;
-	facets.count  = facets_size;
-}
-
-ConvexH *ConvexHDup(ConvexH *src)
-{
-	ConvexH *dst = MEMALLOC_NEW(ConvexH)(src->vertices.count,src->edges.count,src->facets.count);
-
-	memcpy(dst->vertices.element,src->vertices.element,sizeof(float3)*src->vertices.count);
-	memcpy(dst->edges.element,src->edges.element,sizeof(HalfEdge)*src->edges.count);
-	memcpy(dst->facets.element,src->facets.element,sizeof(Plane)*src->facets.count);
-	return dst;
-}
-
-
-NxI32 PlaneTest(const Plane &p, const REAL3 &v) {
-	REAL a  = dot(v,p.normal)+p.dist;
-	NxI32   flag = (a>planetestepsilon)?OVER:((a<-planetestepsilon)?UNDER:COPLANAR);
-	return flag;
-}
-
-NxI32 SplitTest(ConvexH &convex,const Plane &plane) {
-	NxI32 flag=0;
-	for(NxI32 i=0;i<convex.vertices.count;i++) {
-		flag |= PlaneTest(plane,convex.vertices[i]);
-	}
-	return flag;
-}
-
-class VertFlag
-{
-public:
-	NxU8 planetest;
-	NxU8 junk;
-	NxU8 undermap;
-	NxU8 overmap;
-};
-class EdgeFlag
-{
-public:
-	NxU8 planetest;
-	NxU8 fixes;
-	short undermap;
-	short overmap;
-};
-class PlaneFlag
-{
-public:
-	NxU8 undermap;
-	NxU8 overmap;
-};
-class Coplanar{
-public:
-	unsigned short ea;
-	NxU8 v0;
-	NxU8 v1;
-};
-
-NxI32 AssertIntact(ConvexH &convex) {
-	NxI32 i;
-	NxI32 estart=0;
-	for(i=0;i<convex.edges.count;i++) {
-		if(convex.edges[estart].p!= convex.edges[i].p) {
-			estart=i;
-		}
-		NxI32 inext = i+1;
-		if(inext>= convex.edges.count || convex.edges[inext].p != convex.edges[i].p) {
-			inext = estart;
-		}
-		assert(convex.edges[inext].p == convex.edges[i].p);
-		NxI32 nb = convex.edges[i].ea;
-		assert(nb!=255);
-		if(nb==255 || nb==-1) return 0;
-		assert(nb!=-1);
-		assert(i== convex.edges[nb].ea);
-	}
-	for(i=0;i<convex.edges.count;i++) {
-		assert(COPLANAR==PlaneTest(convex.facets[convex.edges[i].p],convex.vertices[convex.edges[i].v]));
-		if(COPLANAR!=PlaneTest(convex.facets[convex.edges[i].p],convex.vertices[convex.edges[i].v])) return 0;
-		if(convex.edges[estart].p!= convex.edges[i].p) {
-			estart=i;
-		}
-		NxI32 i1 = i+1;
-		if(i1>= convex.edges.count || convex.edges[i1].p != convex.edges[i].p) {
-			i1 = estart;
-		}
-		NxI32 i2 = i1+1;
-		if(i2>= convex.edges.count || convex.edges[i2].p != convex.edges[i].p) {
-			i2 = estart;
-		}
-		if(i==i2) continue; // i sliced tangent to an edge and created 2 meaningless edges
-		REAL3 localnormal = TriNormal(convex.vertices[convex.edges[i ].v],
-			                           convex.vertices[convex.edges[i1].v],
-			                           convex.vertices[convex.edges[i2].v]);
-		//assert(dot(localnormal,convex.facets[convex.edges[i].p].normal)>0);//Commented out on Stan Melax' advice
-		if(dot(localnormal,convex.facets[convex.edges[i].p].normal)<=0)return 0;
-	}
-	return 1;
-}
-
-ConvexH *ConvexHCrop(ConvexH &convex,const Plane &slice)
-{
-	NxI32 i;
-	NxI32 vertcountunder=0;
-	NxI32 vertcountover =0;
-	static Array<NxI32> vertscoplanar;  // existing vertex members of convex that are coplanar
-	vertscoplanar.count=0;
-	static Array<NxI32> edgesplit;  // existing edges that members of convex that cross the splitplane
-	edgesplit.count=0;
-
-	assert(convex.edges.count<480);
-
-	EdgeFlag  edgeflag[512];
-	VertFlag  vertflag[256];
-	PlaneFlag planeflag[128];
-	HalfEdge  tmpunderedges[512];
-	Plane	  tmpunderplanes[128];
-	Coplanar coplanaredges[512];
-	NxI32 coplanaredges_num=0;
-
-	Array<REAL3> createdverts;
-	// do the side-of-plane tests
-	for(i=0;i<convex.vertices.count;i++) {
-		vertflag[i].planetest = (NxU8)PlaneTest(slice,convex.vertices[i]);
-		if(vertflag[i].planetest == COPLANAR) {
-			// ? vertscoplanar.Add(i);
-			vertflag[i].undermap = (NxU8)vertcountunder++;
-			vertflag[i].overmap  = (NxU8)vertcountover++;
-		}
-		else if(vertflag[i].planetest == UNDER)	{
-			vertflag[i].undermap = (NxU8)vertcountunder++;
-		}
-		else {
-			assert(vertflag[i].planetest == OVER);
-			vertflag[i].overmap  = (NxU8)vertcountover++;
-			vertflag[i].undermap = (NxU8)-1; // for debugging purposes
-		}
-	}
-
-	NxI32 under_edge_count =0;
-	NxI32 underplanescount=0;
-	NxI32 e0=0;
-
-	for(NxI32 currentplane=0; currentplane<convex.facets.count; currentplane++) {
-		NxI32 estart =e0;
-		NxI32 enextface=0;
-		NxI32 planeside = 0;
-		NxI32 e1 = e0+1;
-		NxI32 vout=-1;
-		NxI32 vin =-1;
-		NxI32 coplanaredge = -1;
-		do{
-
-			if(e1 >= convex.edges.count || convex.edges[e1].p!=currentplane) {
-				enextface = e1;
-				e1=estart;
-			}
-			HalfEdge &edge0 = convex.edges[e0];
-			HalfEdge &edge1 = convex.edges[e1];
-			HalfEdge &edgea = convex.edges[edge0.ea];
-
-
-			planeside |= vertflag[edge0.v].planetest;
-			//if((vertflag[edge0.v].planetest & vertflag[edge1.v].planetest)  == COPLANAR) {
-			//	assert(ecop==-1);
-			//	ecop=e;
-			//}
-
-
-			if(vertflag[edge0.v].planetest == OVER && vertflag[edge1.v].planetest == OVER){
-				// both endpoints over plane
-				edgeflag[e0].undermap  = -1;
-			}
-			else if((vertflag[edge0.v].planetest | vertflag[edge1.v].planetest)  == UNDER) {
-				// at least one endpoint under, the other coplanar or under
-
-				edgeflag[e0].undermap = (short)under_edge_count;
-				tmpunderedges[under_edge_count].v = (NxU8)vertflag[edge0.v].undermap;
-				tmpunderedges[under_edge_count].p = (NxU8)underplanescount;
-				if(edge0.ea < e0) {
-					// connect the neighbors
-					assert(edgeflag[edge0.ea].undermap !=-1);
-					tmpunderedges[under_edge_count].ea = edgeflag[edge0.ea].undermap;
-					tmpunderedges[edgeflag[edge0.ea].undermap].ea = (short)under_edge_count;
-				}
-				under_edge_count++;
-			}
-			else if((vertflag[edge0.v].planetest | vertflag[edge1.v].planetest)  == COPLANAR) {
-				// both endpoints coplanar
-				// must check a 3rd point to see if UNDER
-				NxI32 e2 = e1+1;
-				if(e2>=convex.edges.count || convex.edges[e2].p!=currentplane) {
-					e2 = estart;
-				}
-				assert(convex.edges[e2].p==currentplane);
-				HalfEdge &edge2 = convex.edges[e2];
-				if(vertflag[edge2.v].planetest==UNDER) {
-
-					edgeflag[e0].undermap = (short)under_edge_count;
-					tmpunderedges[under_edge_count].v = (NxU8)vertflag[edge0.v].undermap;
-					tmpunderedges[under_edge_count].p = (NxU8)underplanescount;
-					tmpunderedges[under_edge_count].ea = -1;
-					// make sure this edge is added to the "coplanar" list
-					coplanaredge = under_edge_count;
-					vout = vertflag[edge0.v].undermap;
-					vin  = vertflag[edge1.v].undermap;
-					under_edge_count++;
-				}
-				else {
-					edgeflag[e0].undermap = -1;
-				}
-			}
-			else if(vertflag[edge0.v].planetest == UNDER && vertflag[edge1.v].planetest == OVER) {
-				// first is under 2nd is over
-
-				edgeflag[e0].undermap = (short) under_edge_count;
-				tmpunderedges[under_edge_count].v = (NxU8)vertflag[edge0.v].undermap;
-				tmpunderedges[under_edge_count].p = (NxU8)underplanescount;
-				if(edge0.ea < e0) {
-					assert(edgeflag[edge0.ea].undermap !=-1);
-					// connect the neighbors
-					tmpunderedges[under_edge_count].ea = edgeflag[edge0.ea].undermap;
-					tmpunderedges[edgeflag[edge0.ea].undermap].ea = (short)under_edge_count;
-					vout = tmpunderedges[edgeflag[edge0.ea].undermap].v;
-				}
-				else {
-					Plane &p0 = convex.facets[edge0.p];
-					Plane &pa = convex.facets[edgea.p];
-					createdverts.Add(ThreePlaneIntersection(p0,pa,slice));
-					//createdverts.Add(PlaneProject(slice,PlaneLineIntersection(slice,convex.vertices[edge0.v],convex.vertices[edgea.v])));
-					//createdverts.Add(PlaneLineIntersection(slice,convex.vertices[edge0.v],convex.vertices[edgea.v]));
-					vout = vertcountunder++;
-				}
-				under_edge_count++;
-				/// hmmm something to think about: i might be able to output this edge regarless of
-				// wheter or not we know v-in yet.  ok i;ll try this now:
-				tmpunderedges[under_edge_count].v = (NxU8)vout;
-				tmpunderedges[under_edge_count].p = (NxU8)underplanescount;
-				tmpunderedges[under_edge_count].ea = -1;
-				coplanaredge = under_edge_count;
-				under_edge_count++;
-
-				if(vin!=-1) {
-					// we previously processed an edge  where we came under
-					// now we know about vout as well
-
-					// ADD THIS EDGE TO THE LIST OF EDGES THAT NEED NEIGHBOR ON PARTITION PLANE!!
-				}
-
-			}
-			else if(vertflag[edge0.v].planetest == COPLANAR && vertflag[edge1.v].planetest == OVER) {
-				// first is coplanar 2nd is over
-
-				edgeflag[e0].undermap = -1;
-				vout = vertflag[edge0.v].undermap;
-				// I hate this but i have to make sure part of this face is UNDER before ouputting this vert
-				NxI32 k=estart;
-				assert(edge0.p == currentplane);
-				while(!(planeside&UNDER) && k<convex.edges.count && convex.edges[k].p==edge0.p) {
-					planeside |= vertflag[convex.edges[k].v].planetest;
-					k++;
-				}
-				if(planeside&UNDER){
-					tmpunderedges[under_edge_count].v = (NxU8)vout;
-					tmpunderedges[under_edge_count].p = (NxU8)underplanescount;
-					tmpunderedges[under_edge_count].ea = -1;
-					coplanaredge = under_edge_count; // hmmm should make a note of the edge # for later on
-					under_edge_count++;
-
-				}
-			}
-			else if(vertflag[edge0.v].planetest == OVER && vertflag[edge1.v].planetest == UNDER) {
-				// first is over next is under
-				// new vertex!!!
-				if (vin!=-1) return NULL;
-				if(e0<edge0.ea) {
-					Plane &p0 = convex.facets[edge0.p];
-					Plane &pa = convex.facets[edgea.p];
-					createdverts.Add(ThreePlaneIntersection(p0,pa,slice));
-					//createdverts.Add(PlaneLineIntersection(slice,convex.vertices[edge0.v],convex.vertices[edgea.v]));
-					//createdverts.Add(PlaneProject(slice,PlaneLineIntersection(slice,convex.vertices[edge0.v],convex.vertices[edgea.v])));
-					vin = vertcountunder++;
-				}
-				else {
-					// find the new vertex that was created by edge[edge0.ea]
-					NxI32 nea = edgeflag[edge0.ea].undermap;
-					assert(tmpunderedges[nea].p==tmpunderedges[nea+1].p);
-					vin = tmpunderedges[nea+1].v;
-					assert(vin < vertcountunder);
-				}
-				if(vout!=-1) {
-					// we previously processed an edge  where we went over
-					// now we know vin too
-					// ADD THIS EDGE TO THE LIST OF EDGES THAT NEED NEIGHBOR ON PARTITION PLANE!!
-				}
-				// output edge
-				tmpunderedges[under_edge_count].v = (NxU8)vin;
-				tmpunderedges[under_edge_count].p = (NxU8)underplanescount;
-				edgeflag[e0].undermap = (short)under_edge_count;
-				if(e0>edge0.ea) {
-					assert(edgeflag[edge0.ea].undermap !=-1);
-					// connect the neighbors
-					tmpunderedges[under_edge_count].ea = edgeflag[edge0.ea].undermap;
-					tmpunderedges[edgeflag[edge0.ea].undermap].ea = (short)under_edge_count;
-				}
-				assert(edgeflag[e0].undermap == under_edge_count);
-				under_edge_count++;
-			}
-			else if(vertflag[edge0.v].planetest == OVER && vertflag[edge1.v].planetest == COPLANAR) {
-				// first is over next is coplanar
-
-				edgeflag[e0].undermap = -1;
-				vin = vertflag[edge1.v].undermap;
-				if (vin==-1) return NULL;
-				if(vout!=-1) {
-					// we previously processed an edge  where we came under
-					// now we know both endpoints
-					// ADD THIS EDGE TO THE LIST OF EDGES THAT NEED NEIGHBOR ON PARTITION PLANE!!
-				}
-
-			}
-			else {
-				assert(0);
-			}
-
-
-			e0=e1;
-			e1++; // do the modulo at the beginning of the loop
-
-		} while(e0!=estart) ;
-		e0 = enextface;
-		if(planeside&UNDER) {
-			planeflag[currentplane].undermap = (NxU8)underplanescount;
-			tmpunderplanes[underplanescount] = convex.facets[currentplane];
-			underplanescount++;
-		}
-		else {
-			planeflag[currentplane].undermap = 0;
-		}
-		if(vout>=0 && (planeside&UNDER)) {
-			assert(vin>=0);
-			assert(coplanaredge>=0);
-			assert(coplanaredge!=511);
-			coplanaredges[coplanaredges_num].ea = (short)coplanaredge;
-			coplanaredges[coplanaredges_num].v0 = (NxU8)vin;
-			coplanaredges[coplanaredges_num].v1 = (NxU8)vout;
-			coplanaredges_num++;
-		}
-	}
-
-	// add the new plane to the mix:
-	if(coplanaredges_num>0) {
-		tmpunderplanes[underplanescount++]=slice;
-	}
-	for(i=0;i<coplanaredges_num-1;i++) {
-		if(coplanaredges[i].v1 != coplanaredges[i+1].v0) {
-			NxI32 j = 0;
-			for(j=i+2;j<coplanaredges_num;j++) {
-				if(coplanaredges[i].v1 == coplanaredges[j].v0) {
-					Coplanar tmp = coplanaredges[i+1];
-					coplanaredges[i+1] = coplanaredges[j];
-					coplanaredges[j] = tmp;
-					break;
-				}
-			}
-			if(j>=coplanaredges_num)
-			{
-				// assert(j<coplanaredges_num);
-				return NULL;
-			}
-		}
-	}
-	ConvexH *punder = MEMALLOC_NEW(ConvexH)(vertcountunder,under_edge_count+coplanaredges_num,underplanescount);
-
-	ConvexH &under = *punder;
-	NxI32 k=0;
-	for(i=0;i<convex.vertices.count;i++) {
-		if(vertflag[i].planetest != OVER){
-			under.vertices[k++] = convex.vertices[i];
-		}
-	}
-	i=0;
-	while(k<vertcountunder) {
-		under.vertices[k++] = createdverts[i++];
-	}
-	assert(i==createdverts.count);
-
-	for(i=0;i<coplanaredges_num;i++) {
-		under.edges[under_edge_count+i].p  = (NxU8)(underplanescount-1);
-		under.edges[under_edge_count+i].ea = coplanaredges[i].ea;
-		tmpunderedges[coplanaredges[i].ea].ea = (short)(under_edge_count+i);
-		under.edges[under_edge_count+i].v  = coplanaredges[i].v0;
-	}
-
-	memcpy(under.edges.element,tmpunderedges,sizeof(HalfEdge)*under_edge_count);
-	memcpy(under.facets.element,tmpunderplanes,sizeof(Plane)*underplanescount);
-	return punder;
-}
-
-
-NxF32 minadjangle = 3.0f;  // in degrees  - result wont have two adjacent facets within this angle of each other.
-static NxI32 candidateplane(Plane *planes,NxI32 planes_count,ConvexH *convex,NxF32 epsilon)
-{
-	NxI32 p =-1;
-	REAL md=0;
-	NxI32 i,j;
-	NxF32 maxdot_minang = cosf(DEG2RAD*minadjangle);
-	for(i=0;i<planes_count;i++)
-	{
-		NxF32 d=0;
-		NxF32 dmax=0;
-		NxF32 dmin=0;
-		for(j=0;j<convex->vertices.count;j++)
-		{
-			dmax = Max(dmax,dot(convex->vertices[j],planes[i].normal)+planes[i].dist);
-			dmin = Min(dmin,dot(convex->vertices[j],planes[i].normal)+planes[i].dist);
-		}
-		NxF32 dr = dmax-dmin;
-		if(dr<planetestepsilon) dr=1.0f; // shouldn't happen.
-		d = dmax /dr;
-		if(d<=md) continue;
-		for(j=0;j<convex->facets.count;j++)
-		{
-			if(planes[i]==convex->facets[j])
-			{
-				d=0;continue;
-			}
-			if(dot(planes[i].normal,convex->facets[j].normal)>maxdot_minang)
-			{
-				for(NxI32 k=0;k<convex->edges.count;k++)
-				{
-					if(convex->edges[k].p!=j) continue;
-					if(dot(convex->vertices[convex->edges[k].v],planes[i].normal)+planes[i].dist<0)
-					{
-						d=0; // so this plane wont get selected.
-						break;
-					}
-				}
-			}
-		}
-		if(d>md)
-		{
-			p=i;
-			md=d;
-		}
-	}
-	return (md>epsilon)?p:-1;
-}
-
-
-
-template<class T>
-inline NxI32 maxdir(const T *p,NxI32 count,const T &dir)
-{
-	assert(count);
-	NxI32 m=0;
-	for(NxI32 i=1;i<count;i++)
-	{
-		if(dot(p[i],dir)>dot(p[m],dir)) m=i;
-	}
-	return m;
-}
-
-
-template<class T>
-NxI32 maxdirfiltered(const T *p,NxI32 count,const T &dir,Array<NxI32> &allow)
-{
-	assert(count);
-	NxI32 m=-1;
-	for(NxI32 i=0;i<count;i++) if(allow[i])
-	{
-		if(m==-1 || dot(p[i],dir)>dot(p[m],dir)) m=i;
-	}
-	assert(m!=-1);
-	return m;
-}
-
-float3 orth(const float3 &v)
-{
-	float3 a=cross(v,float3(0,0,1));
-	float3 b=cross(v,float3(0,1,0));
-	return normalize((magnitude(a)>magnitude(b))?a:b);
-}
-
-
-template<class T>
-NxI32 maxdirsterid(const T *p,NxI32 count,const T &dir,Array<NxI32> &allow)
-{
-	NxI32 m=-1;
-	while(m==-1)
-	{
-		m = maxdirfiltered(p,count,dir,allow);
-		if(allow[m]==3) return m;
-		T u = orth(dir);
-		T v = cross(u,dir);
-		NxI32 ma=-1;
-		for(NxF32 x = 0.0f ; x<= 360.0f ; x+= 45.0f)
-		{
-			NxF32 s = sinf(DEG2RAD*(x));
-			NxF32 c = cosf(DEG2RAD*(x));
-			NxI32 mb = maxdirfiltered(p,count,dir+(u*s+v*c)*0.025f,allow);
-			if(ma==m && mb==m)
-			{
-				allow[m]=3;
-				return m;
-			}
-			if(ma!=-1 && ma!=mb)  // Yuck - this is really ugly
-			{
-				NxI32 mc = ma;
-				for(NxF32 xx = x-40.0f ; xx <= x ; xx+= 5.0f)
-				{
-					NxF32 s = sinf(DEG2RAD*(xx));
-					NxF32 c = cosf(DEG2RAD*(xx));
-					NxI32 md = maxdirfiltered(p,count,dir+(u*s+v*c)*0.025f,allow);
-					if(mc==m && md==m)
-					{
-						allow[m]=3;
-						return m;
-					}
-					mc=md;
-				}
-			}
-			ma=mb;
-		}
-		allow[m]=0;
-		m=-1;
-	}
-	assert(0);
-	return m;
-}
-
-
-
-
-NxI32 operator ==(const int3 &a,const int3 &b)
-{
-	for(NxI32 i=0;i<3;i++)
-	{
-		if(a[i]!=b[i]) return 0;
-	}
-	return 1;
-}
-
-int3 roll3(int3 a)
-{
-	NxI32 tmp=a[0];
-	a[0]=a[1];
-	a[1]=a[2];
-	a[2]=tmp;
-	return a;
-}
-NxI32 isa(const int3 &a,const int3 &b)
-{
-	return ( a==b || roll3(a)==b || a==roll3(b) );
-}
-NxI32 b2b(const int3 &a,const int3 &b)
-{
-	return isa(a,int3(b[2],b[1],b[0]));
-}
-NxI32 above(float3* vertices,const int3& t, const float3 &p, NxF32 epsilon)
-{
-	float3 n=TriNormal(vertices[t[0]],vertices[t[1]],vertices[t[2]]);
-	return (dot(n,p-vertices[t[0]]) > epsilon); // EPSILON???
-}
-NxI32 hasedge(const int3 &t, NxI32 a,NxI32 b)
-{
-	for(NxI32 i=0;i<3;i++)
-	{
-		NxI32 i1= (i+1)%3;
-		if(t[i]==a && t[i1]==b) return 1;
-	}
-	return 0;
-}
-NxI32 hasvert(const int3 &t, NxI32 v)
-{
-	return (t[0]==v || t[1]==v || t[2]==v) ;
-}
-NxI32 shareedge(const int3 &a,const int3 &b)
-{
-	NxI32 i;
-	for(i=0;i<3;i++)
-	{
-		NxI32 i1= (i+1)%3;
-		if(hasedge(a,b[i1],b[i])) return 1;
-	}
-	return 0;
-}
-
-class Tri;
-
-static Array<Tri*> tris; // djs: For heaven's sake!!!!
-
-class Tri : public int3
-{
-public:
-	int3 n;
-	NxI32 id;
-	NxI32 vmax;
-	NxF32 rise;
-	Tri(NxI32 a,NxI32 b,NxI32 c):int3(a,b,c),n(-1,-1,-1)
-	{
-		id = tris.count;
-		tris.Add(this);
-		vmax=-1;
-		rise = 0.0f;
-	}
-	~Tri()
-	{
-		assert(tris[id]==this);
-		tris[id]=NULL;
-	}
-	NxI32 &neib(NxI32 a,NxI32 b);
-};
-
-
-NxI32 &Tri::neib(NxI32 a,NxI32 b)
-{
-	static NxI32 er=-1;
-	NxI32 i;
-	for(i=0;i<3;i++)
-	{
-		NxI32 i1=(i+1)%3;
-		NxI32 i2=(i+2)%3;
-		if((*this)[i]==a && (*this)[i1]==b) return n[i2];
-		if((*this)[i]==b && (*this)[i1]==a) return n[i2];
-	}
-	assert(0);
-	return er;
-}
-void b2bfix(Tri* s,Tri*t)
-{
-	NxI32 i;
-	for(i=0;i<3;i++)
-	{
-		NxI32 i1=(i+1)%3;
-		NxI32 i2=(i+2)%3;
-		NxI32 a = (*s)[i1];
-		NxI32 b = (*s)[i2];
-		assert(tris[s->neib(a,b)]->neib(b,a) == s->id);
-		assert(tris[t->neib(a,b)]->neib(b,a) == t->id);
-		tris[s->neib(a,b)]->neib(b,a) = t->neib(b,a);
-		tris[t->neib(b,a)]->neib(a,b) = s->neib(a,b);
-	}
-}
-
-void removeb2b(Tri* s,Tri*t)
-{
-	b2bfix(s,t);
-	delete s;
-	delete t;
-}
-
-void extrude(Tri *t0,NxI32 v)
-{
-	int3 t= *t0;
-	NxI32 n = tris.count;
-	Tri* ta = MEMALLOC_NEW(Tri)(v,t[1],t[2]);
-	ta->n = int3(t0->n[0],n+1,n+2);
-	tris[t0->n[0]]->neib(t[1],t[2]) = n+0;
-	Tri* tb = MEMALLOC_NEW(Tri)(v,t[2],t[0]);
-	tb->n = int3(t0->n[1],n+2,n+0);
-	tris[t0->n[1]]->neib(t[2],t[0]) = n+1;
-	Tri* tc = MEMALLOC_NEW(Tri)(v,t[0],t[1]);
-	tc->n = int3(t0->n[2],n+0,n+1);
-	tris[t0->n[2]]->neib(t[0],t[1]) = n+2;
-	if(hasvert(*tris[ta->n[0]],v)) removeb2b(ta,tris[ta->n[0]]);
-	if(hasvert(*tris[tb->n[0]],v)) removeb2b(tb,tris[tb->n[0]]);
-	if(hasvert(*tris[tc->n[0]],v)) removeb2b(tc,tris[tc->n[0]]);
-	delete t0;
-
-}
-
-Tri *extrudable(NxF32 epsilon)
-{
-	NxI32 i;
-	Tri *t=NULL;
-	for(i=0;i<tris.count;i++)
-	{
-		if(!t || (tris[i] && t->rise<tris[i]->rise))
-		{
-			t = tris[i];
-		}
-	}
-	return (t->rise >epsilon)?t:NULL ;
-}
-
-class int4
-{
-public:
-	NxI32 x,y,z,w;
-	int4(){};
-	int4(NxI32 _x,NxI32 _y, NxI32 _z,NxI32 _w){x=_x;y=_y;z=_z;w=_w;}
-	const NxI32& operator[](NxI32 i) const {return (&x)[i];}
-	NxI32& operator[](NxI32 i) {return (&x)[i];}
-};
-
-
-
-bool hasVolume(float3 *verts, NxI32 p0, NxI32 p1, NxI32 p2, NxI32 p3)
-{
-	float3 result3 = cross(verts[p1]-verts[p0], verts[p2]-verts[p0]);
-	if (magnitude(result3) < VOLUME_EPSILON && magnitude(result3) > -VOLUME_EPSILON) // Almost collinear or otherwise very close to each other
-		return false;
-	NxF32 result = dot(normalize(result3), verts[p3]-verts[p0]);
-	return (result > VOLUME_EPSILON || result < -VOLUME_EPSILON); // Returns true iff volume is significantly non-zero
-}
-
-int4 FindSimplex(float3 *verts,NxI32 verts_count,Array<NxI32> &allow)
-{
-	float3 basis[3];
-	basis[0] = float3( 0.01f, 0.02f, 1.0f );
-	NxI32 p0 = maxdirsterid(verts,verts_count, basis[0],allow);
-	NxI32	p1 = maxdirsterid(verts,verts_count,-basis[0],allow);
-	basis[0] = verts[p0]-verts[p1];
-	if(p0==p1 || basis[0]==float3(0,0,0))
-		return int4(-1,-1,-1,-1);
-	basis[1] = cross(float3(     1, 0.02f, 0),basis[0]);
-	basis[2] = cross(float3(-0.02f,     1, 0),basis[0]);
-	basis[1] = normalize( (magnitude(basis[1])>magnitude(basis[2])) ? basis[1]:basis[2]);
-	NxI32 p2 = maxdirsterid(verts,verts_count,basis[1],allow);
-	if(p2 == p0 || p2 == p1)
-	{
-		p2 = maxdirsterid(verts,verts_count,-basis[1],allow);
-	}
-	if(p2 == p0 || p2 == p1)
-		return int4(-1,-1,-1,-1);
-	basis[1] = verts[p2] - verts[p0];
-	basis[2] = normalize(cross(basis[1],basis[0]));
-	NxI32 p3 = maxdirsterid(verts,verts_count,basis[2],allow);
-	if(p3==p0||p3==p1||p3==p2||!hasVolume(verts, p0, p1, p2, p3)) p3 = maxdirsterid(verts,verts_count,-basis[2],allow);
-	if(p3==p0||p3==p1||p3==p2)
-		return int4(-1,-1,-1,-1);
-	assert(!(p0==p1||p0==p2||p0==p3||p1==p2||p1==p3||p2==p3));
-	if(dot(verts[p3]-verts[p0],cross(verts[p1]-verts[p0],verts[p2]-verts[p0])) <0) {Swap(p2,p3);}
-	return int4(p0,p1,p2,p3);
-}
-#pragma warning(push)
-#pragma warning(disable:4706)
-NxI32 calchullgen(float3 *verts,NxI32 verts_count, NxI32 vlimit)
-{
-	if(verts_count <4) return 0;
-	if(vlimit==0) vlimit=1000000000;
-	NxI32 j;
-	float3 bmin(*verts),bmax(*verts);
-	Array<NxI32> isextreme(verts_count);
-	Array<NxI32> allow(verts_count);
-	for(j=0;j<verts_count;j++)
-	{
-		allow.Add(1);
-		isextreme.Add(0);
-		bmin = VectorMin(bmin,verts[j]);
-		bmax = VectorMax(bmax,verts[j]);
-	}
-	NxF32 epsilon = magnitude(bmax-bmin) * 0.001f;
-
-
-	int4 p = FindSimplex(verts,verts_count,allow);
-	if(p.x==-1) return 0; // simplex failed
-
-
-
-	float3 center = (verts[p[0]]+verts[p[1]]+verts[p[2]]+verts[p[3]]) /4.0f;  // a valid interior point
-	Tri *t0 = MEMALLOC_NEW(Tri)(p[2],p[3],p[1]); t0->n=int3(2,3,1);
-	Tri *t1 = MEMALLOC_NEW(Tri)(p[3],p[2],p[0]); t1->n=int3(3,2,0);
-	Tri *t2 = MEMALLOC_NEW(Tri)(p[0],p[1],p[3]); t2->n=int3(0,1,3);
-	Tri *t3 = MEMALLOC_NEW(Tri)(p[1],p[0],p[2]); t3->n=int3(1,0,2);
-	isextreme[p[0]]=isextreme[p[1]]=isextreme[p[2]]=isextreme[p[3]]=1;
-
-	for(j=0;j<tris.count;j++)
-	{
-		Tri *t=tris[j];
-		assert(t);
-		assert(t->vmax<0);
-		float3 n=TriNormal(verts[(*t)[0]],verts[(*t)[1]],verts[(*t)[2]]);
-		t->vmax = maxdirsterid(verts,verts_count,n,allow);
-		t->rise = dot(n,verts[t->vmax]-verts[(*t)[0]]);
-	}
-	Tri *te;
-	vlimit-=4;
-	while(vlimit >0 && (te=extrudable(epsilon)))
-	{
-		int3 ti=*te;
-		NxI32 v=te->vmax;
-		assert(!isextreme[v]);  // wtf we've already done this vertex
-		isextreme[v]=1;
-		//if(v==p0 || v==p1 || v==p2 || v==p3) continue; // done these already
-		j=tris.count;
-		while(j--) {
-			if(!tris[j]) continue;
-			int3 t=*tris[j];
-			if(above(verts,t,verts[v],0.01f*epsilon))
-			{
-				extrude(tris[j],v);
-			}
-		}
-		// now check for those degenerate cases where we have a flipped triangle or a really skinny triangle
-		j=tris.count;
-		while(j--)
-		{
-			if(!tris[j]) continue;
-			if(!hasvert(*tris[j],v)) break;
-			int3 nt=*tris[j];
-			if(above(verts,nt,center,0.01f*epsilon)  || magnitude(cross(verts[nt[1]]-verts[nt[0]],verts[nt[2]]-verts[nt[1]]))< epsilon*epsilon*0.1f )
-			{
-				Tri *nb = tris[tris[j]->n[0]];
-				assert(nb);assert(!hasvert(*nb,v));assert(nb->id<j);
-				extrude(nb,v);
-				j=tris.count;
-			}
-		}
-		j=tris.count;
-		while(j--)
-		{
-			Tri *t=tris[j];
-			if(!t) continue;
-			if(t->vmax>=0) break;
-			float3 n=TriNormal(verts[(*t)[0]],verts[(*t)[1]],verts[(*t)[2]]);
-			t->vmax = maxdirsterid(verts,verts_count,n,allow);
-			if(isextreme[t->vmax])
-			{
-				t->vmax=-1; // already done that vertex - algorithm needs to be able to terminate.
-			}
-			else
-			{
-				t->rise = dot(n,verts[t->vmax]-verts[(*t)[0]]);
-			}
-		}
-		vlimit --;
-	}
-	return 1;
-}
-#pragma warning(pop)
-
-NxI32 calchull(float3 *verts,NxI32 verts_count, NxI32 *&tris_out, NxI32 &tris_count,NxI32 vlimit)
-{
-	NxI32 rc=calchullgen(verts,verts_count,  vlimit) ;
-	if(!rc) return 0;
-	Array<NxI32> ts;
-	for(NxI32 i=0;i<tris.count;i++)if(tris[i])
-	{
-		for(NxI32 j=0;j<3;j++)ts.Add((*tris[i])[j]);
-		delete tris[i];
-	}
-	tris_count = ts.count/3;
-	tris_out   = ts.element;
-	ts.element=NULL; ts.count=ts.array_size=0;
-	// please reset here, otherwise, we get a nice virtual function call (R6025) error with NxCooking library
-	tris.SetSize( 0 );
-	return 1;
-}
-
-static NxF32 area2(const float3 &v0,const float3 &v1,const float3 &v2)
-{
-	float3 cp = cross(v0-v1,v2-v0);
-	return dot(cp,cp);
-}
-NxI32 calchullpbev(float3 *verts,NxI32 verts_count,NxI32 vlimit, Array<Plane> &planes,NxF32 bevangle)
-{
-	NxI32 i,j;
-	Array<Plane> bplanes;
-	planes.count=0;
-	NxI32 rc = calchullgen(verts,verts_count,vlimit);
-	if(!rc) return 0;
-	extern NxF32 minadjangle; // default is 3.0f;  // in degrees  - result wont have two adjacent facets within this angle of each other.
-	NxF32 maxdot_minang = cosf(DEG2RAD*minadjangle);
-	for(i=0;i<tris.count;i++)if(tris[i])
-	{
-		Plane p;
-		Tri *t = tris[i];
-		p.normal = TriNormal(verts[(*t)[0]],verts[(*t)[1]],verts[(*t)[2]]);
-		p.dist   = -dot(p.normal, verts[(*t)[0]]);
-		for(j=0;j<3;j++)
-		{
-			if(t->n[j]<t->id) continue;
-			Tri *s = tris[t->n[j]];
-			REAL3 snormal = TriNormal(verts[(*s)[0]],verts[(*s)[1]],verts[(*s)[2]]);
-			if(dot(snormal,p.normal)>=cos(bevangle*DEG2RAD)) continue;
-			REAL3 e = verts[(*t)[(j+2)%3]] - verts[(*t)[(j+1)%3]];
-			REAL3 n = (e!=REAL3(0,0,0))? cross(snormal,e)+cross(e,p.normal) : snormal+p.normal;
-			assert(n!=REAL3(0,0,0));
-			if(n==REAL3(0,0,0)) return 0;
-			n=normalize(n);
-			bplanes.Add(Plane(n,-dot(n,verts[maxdir(verts,verts_count,n)])));
-		}
-	}
-	for(i=0;i<tris.count;i++)if(tris[i])for(j=i+1;j<tris.count;j++)if(tris[i] && tris[j])
-	{
-		Tri *ti = tris[i];
-		Tri *tj = tris[j];
-		REAL3 ni = TriNormal(verts[(*ti)[0]],verts[(*ti)[1]],verts[(*ti)[2]]);
-		REAL3 nj = TriNormal(verts[(*tj)[0]],verts[(*tj)[1]],verts[(*tj)[2]]);
-		if(dot(ni,nj)>maxdot_minang)
-		{
-			// somebody has to die, keep the biggest triangle
-			if( area2(verts[(*ti)[0]],verts[(*ti)[1]],verts[(*ti)[2]]) < area2(verts[(*tj)[0]],verts[(*tj)[1]],verts[(*tj)[2]]))
-			{
-				delete tris[i];
-			}
-			else
-			{
-				delete tris[j];
-			}
-		}
-	}
-	for(i=0;i<tris.count;i++)if(tris[i])
-	{
-		Plane p;
-		Tri *t = tris[i];
-		p.normal = TriNormal(verts[(*t)[0]],verts[(*t)[1]],verts[(*t)[2]]);
-		p.dist   = -dot(p.normal, verts[(*t)[0]]);
-		planes.Add(p);
-	}
-	for(i=0;i<bplanes.count;i++)
-	{
-		for(j=0;j<planes.count;j++)
-		{
-			if(dot(bplanes[i].normal,planes[j].normal)>maxdot_minang) break;
-		}
-		if(j==planes.count)
-		{
-			planes.Add(bplanes[i]);
-		}
-	}
-	for(i=0;i<tris.count;i++)if(tris[i])
-	{
-		delete tris[i];
-	}
-	tris.count = 0; //bad place to do the tris.SetSize(0) fix, this line is executed many times, and will result in a whole lot of allocations if the array is totally cleared here
-	return 1;
-}
-
-ConvexH *test_cube()
-{
-	ConvexH *convex = MEMALLOC_NEW(ConvexH)(8,24,6);
-	convex->vertices[0] = REAL3(0,0,0);
-	convex->vertices[1] = REAL3(0,0,1);
-	convex->vertices[2] = REAL3(0,1,0);
-	convex->vertices[3] = REAL3(0,1,1);
-	convex->vertices[4] = REAL3(1,0,0);
-	convex->vertices[5] = REAL3(1,0,1);
-	convex->vertices[6] = REAL3(1,1,0);
-	convex->vertices[7] = REAL3(1,1,1);
-
-	convex->facets[0] = Plane(REAL3(-1,0,0),0);
-	convex->facets[1] = Plane(REAL3(1,0,0),-1);
-	convex->facets[2] = Plane(REAL3(0,-1,0),0);
-	convex->facets[3] = Plane(REAL3(0,1,0),-1);
-	convex->facets[4] = Plane(REAL3(0,0,-1),0);
-	convex->facets[5] = Plane(REAL3(0,0,1),-1);
-
-	convex->edges[0 ] = HalfEdge(11,0,0);
-	convex->edges[1 ] = HalfEdge(23,1,0);
-	convex->edges[2 ] = HalfEdge(15,3,0);
-	convex->edges[3 ] = HalfEdge(16,2,0);
-
-	convex->edges[4 ] = HalfEdge(13,6,1);
-	convex->edges[5 ] = HalfEdge(21,7,1);
-	convex->edges[6 ] = HalfEdge( 9,5,1);
-	convex->edges[7 ] = HalfEdge(18,4,1);
-
-	convex->edges[8 ] = HalfEdge(19,0,2);
-	convex->edges[9 ] = HalfEdge( 6,4,2);
-	convex->edges[10] = HalfEdge(20,5,2);
-	convex->edges[11] = HalfEdge( 0,1,2);
-
-	convex->edges[12] = HalfEdge(22,3,3);
-	convex->edges[13] = HalfEdge( 4,7,3);
-	convex->edges[14] = HalfEdge(17,6,3);
-	convex->edges[15] = HalfEdge( 2,2,3);
-
-	convex->edges[16] = HalfEdge( 3,0,4);
-	convex->edges[17] = HalfEdge(14,2,4);
-	convex->edges[18] = HalfEdge( 7,6,4);
-	convex->edges[19] = HalfEdge( 8,4,4);
-
-	convex->edges[20] = HalfEdge(10,1,5);
-	convex->edges[21] = HalfEdge( 5,5,5);
-	convex->edges[22] = HalfEdge(12,7,5);
-	convex->edges[23] = HalfEdge( 1,3,5);
-
-
-	return convex;
-}
-
-ConvexH *ConvexHMakeCube(const REAL3 &bmin, const REAL3 &bmax)
-{
-	ConvexH *convex = test_cube();
-	convex->vertices[0] = REAL3(bmin.x,bmin.y,bmin.z);
-	convex->vertices[1] = REAL3(bmin.x,bmin.y,bmax.z);
-	convex->vertices[2] = REAL3(bmin.x,bmax.y,bmin.z);
-	convex->vertices[3] = REAL3(bmin.x,bmax.y,bmax.z);
-	convex->vertices[4] = REAL3(bmax.x,bmin.y,bmin.z);
-	convex->vertices[5] = REAL3(bmax.x,bmin.y,bmax.z);
-	convex->vertices[6] = REAL3(bmax.x,bmax.y,bmin.z);
-	convex->vertices[7] = REAL3(bmax.x,bmax.y,bmax.z);
-
-	convex->facets[0] = Plane(REAL3(-1,0,0), bmin.x);
-	convex->facets[1] = Plane(REAL3(1,0,0), -bmax.x);
-	convex->facets[2] = Plane(REAL3(0,-1,0), bmin.y);
-	convex->facets[3] = Plane(REAL3(0,1,0), -bmax.y);
-	convex->facets[4] = Plane(REAL3(0,0,-1), bmin.z);
-	convex->facets[5] = Plane(REAL3(0,0,1), -bmax.z);
-	return convex;
-}
-
-
-static NxI32 overhull(Plane *planes,NxI32 planes_count,float3 *verts, NxI32 verts_count,NxI32 maxplanes,
-			 float3 *&verts_out, NxI32 &verts_count_out,  NxI32 *&faces_out, NxI32 &faces_count_out ,NxF32 inflate)
-{
-	NxI32 i,j;
-   if (verts_count < 4) return 0;
-	maxplanes = Min(maxplanes,planes_count);
-	float3 bmin(verts[0]),bmax(verts[0]);
-	for(i=0;i<verts_count;i++)
-	{
-		bmin = VectorMin(bmin,verts[i]);
-		bmax = VectorMax(bmax,verts[i]);
-	}
-	NxF32 diameter = magnitude(bmax-bmin);
-//	inflate *=diameter;   // RELATIVE INFLATION
-	bmin -= float3(inflate*2.5f,inflate*2.5f,inflate*2.5f);
-	bmax += float3(inflate*2.5f,inflate*2.5f,inflate*2.5f);
-	// 2 is from the formula:
-	// D = d*|n1+n2|/(1-n1 dot n2), where d is "inflate" and
-	// n1 and n2 are the normals of two planes at bevelAngle to each other
-	// for 120 degrees, D is 2d
-
-	//bmin -= float3(inflate,inflate,inflate);
-	//bmax += float3(inflate,inflate,inflate);
-	for(i=0;i<planes_count;i++)
-	{
-		planes[i].dist -= inflate;
-	}
-	float3 emin = bmin; // VectorMin(bmin,float3(0,0,0));
-	float3 emax = bmax; // VectorMax(bmax,float3(0,0,0));
-	NxF32 epsilon  = 0.01f; // size of object is taken into account within candidate plane function.  Used to multiply here by magnitude(emax-emin)
-	planetestepsilon = magnitude(emax-emin) * PAPERWIDTH;
-	// todo: add bounding cube planes to force bevel. or try instead not adding the diameter expansion ??? must think.
-	// ConvexH *convex = ConvexHMakeCube(bmin - float3(diameter,diameter,diameter),bmax+float3(diameter,diameter,diameter));
-	NxF32 maxdot_minang = cosf(DEG2RAD*minadjangle);
-	for(j=0;j<6;j++)
-	{
-		float3 n(0,0,0);
-		n[j/2] = (j%2)? 1.0f : -1.0f;
-		for(i=0;i<planes_count;i++)
-		{
-			if(dot(n,planes[i].normal)> maxdot_minang)
-			{
-				(*((j%2)?&bmax:&bmin)) += n * (diameter*0.5f);
-				break;
-			}
-		}
-	}
-	ConvexH *c = ConvexHMakeCube(REAL3(bmin),REAL3(bmax));
-	NxI32 k;
-	while(maxplanes-- && (k=candidateplane(planes,planes_count,c,epsilon))>=0)
-	{
-		ConvexH *tmp = c;
-		c = ConvexHCrop(*tmp,planes[k]);
-		if(c==NULL) {c=tmp; break;} // might want to debug this case better!!!
-		if(!AssertIntact(*c)) {c=tmp; break;} // might want to debug this case better too!!!
-		delete tmp;
-	}
-
-	assert(AssertIntact(*c));
-	//return c;
-	faces_out = (NxI32*)MEMALLOC_MALLOC(sizeof(NxI32)*(1+c->facets.count+c->edges.count));     // new NxI32[1+c->facets.count+c->edges.count];
-	faces_count_out=0;
-	i=0;
-	faces_out[faces_count_out++]=-1;
-	k=0;
-	while(i<c->edges.count)
-	{
-		j=1;
-		while(j+i<c->edges.count && c->edges[i].p==c->edges[i+j].p) { j++; }
-		faces_out[faces_count_out++]=j;
-		while(j--)
-		{
-			faces_out[faces_count_out++] = c->edges[i].v;
-			i++;
-		}
-		k++;
-	}
-	faces_out[0]=k; // number of faces.
-	assert(k==c->facets.count);
-	assert(faces_count_out == 1+c->facets.count+c->edges.count);
-	verts_out = c->vertices.element; // new float3[c->vertices.count];
-	verts_count_out = c->vertices.count;
-	for(i=0;i<c->vertices.count;i++)
-	{
-		verts_out[i] = float3(c->vertices[i]);
-	}
-	c->vertices.count=c->vertices.array_size=0;	c->vertices.element=NULL;
-	delete c;
-	return 1;
-}
-
-static NxI32 overhullv(float3 *verts, NxI32 verts_count,NxI32 maxplanes,
-			 float3 *&verts_out, NxI32 &verts_count_out,  NxI32 *&faces_out, NxI32 &faces_count_out ,NxF32 inflate,NxF32 bevangle,NxI32 vlimit)
-{
-	if(!verts_count) return 0;
-	extern NxI32 calchullpbev(float3 *verts,NxI32 verts_count,NxI32 vlimit, Array<Plane> &planes,NxF32 bevangle) ;
-	Array<Plane> planes;
-	NxI32 rc=calchullpbev(verts,verts_count,vlimit,planes,bevangle) ;
-	if(!rc) return 0;
-	return overhull(planes.element,planes.count,verts,verts_count,maxplanes,verts_out,verts_count_out,faces_out,faces_count_out,inflate);
-}
-
-
-//*****************************************************
-//*****************************************************
-
-
-bool ComputeHull(NxU32 vcount,const NxF32 *vertices,PHullResult &result,NxU32 vlimit,NxF32 inflate)
-{
-
-	NxI32 index_count;
-	NxI32 *faces;
-	float3 *verts_out;
-	NxI32     verts_count_out;
-
-	if(inflate==0.0f)
-	{
-		NxI32  *tris_out;
-		NxI32    tris_count;
-		NxI32 ret = calchull( (float3 *) vertices, (NxI32) vcount, tris_out, tris_count, vlimit );
-		if(!ret) return false;
-		result.mIndexCount = (NxU32) (tris_count*3);
-		result.mFaceCount  = (NxU32) tris_count;
-		result.mVertices   = (NxF32*) vertices;
-		result.mVcount     = (NxU32) vcount;
-		result.mIndices    = (NxU32 *) tris_out;
-		return true;
-	}
-
-	NxI32 ret = overhullv((float3*)vertices,vcount,35,verts_out,verts_count_out,faces,index_count,inflate,120.0f,vlimit);
-	if(!ret) {
-		tris.SetSize(0); //have to set the size to 0 in order to protect from a "pure virtual function call" problem
-		return false;
-	}
-
-	Array<int3> tris;
-	NxI32 n=faces[0];
-	NxI32 k=1;
-	for(NxI32 i=0;i<n;i++)
-	{
-		NxI32 pn = faces[k++];
-		for(NxI32 j=2;j<pn;j++) tris.Add(int3(faces[k],faces[k+j-1],faces[k+j]));
-		k+=pn;
-	}
-	assert(tris.count == index_count-1-(n*3));
-	MEMALLOC_FREE(faces);	// PT: I added that. Is it ok ?
-
-	result.mIndexCount = (NxU32) (tris.count*3);
-	result.mFaceCount  = (NxU32) tris.count;
-	result.mVertices   = (NxF32*) verts_out;
-	result.mVcount     = (NxU32) verts_count_out;
-	result.mIndices    = (NxU32 *) tris.element;
-	tris.element=NULL; tris.count = tris.array_size=0;
-	CONVEX_DECOMPOSITION::tris.SetSize(0); //have to set the size to 0 in order to protect from a "pure virtual function call" problem
-
-	return true;
-}
-
-
-void ReleaseHull(PHullResult &result)
-{
-  MEMALLOC_FREE(result.mIndices);	// PT: I added that. Is it ok ?
-  MEMALLOC_FREE(result.mVertices);	// PT: I added that. Is it ok ?
-	result.mVcount = 0;
-	result.mIndexCount = 0;
-	result.mIndices = 0;
-	result.mVertices = 0;
-	result.mIndices  = 0;
-}
-
-
-
-//****** HULLLIB source code
-
-
-HullError HullLibrary::CreateConvexHull(const HullDesc       &desc,           // describes the input request
-																				HullResult           &result)         // contains the resulst
-{
-	HullError ret = QE_FAIL;
-
-
-	PHullResult hr;
-
-	NxU32 vcount = desc.mVcount;
-	if ( vcount < 8 ) vcount = 8;
-
-	NxF32 *vsource  = (NxF32 *) MEMALLOC_MALLOC( sizeof(NxF32)*vcount*3 );
-
-
-	NxF32 scale[3];
-
-	NxU32 ovcount;
-
-	bool ok = CleanupVertices(desc.mVcount,desc.mVertices, desc.mVertexStride, ovcount, vsource, desc.mNormalEpsilon, scale ); // normalize point cloud, remove duplicates!
-
-	if ( ok )
-	{
-
-
-		{
-			for (NxU32 i=0; i<ovcount; i++)
-			{
-				NxF32 *v = &vsource[i*3];
-				v[0]*=scale[0];
-				v[1]*=scale[1];
-				v[2]*=scale[2];
-			}
-		}
-
-		NxF32 skinwidth = 0;
-		if ( desc.HasHullFlag(QF_SKIN_WIDTH) )
-			skinwidth = desc.mSkinWidth;
-
-		ok = ComputeHull(ovcount,vsource,hr,desc.mMaxVertices,skinwidth);
-
-		if ( ok )
-		{
-
-			// re-index triangle mesh so it refers to only used vertices, rebuild a new vertex table.
-			NxF32 *vscratch = (NxF32 *) MEMALLOC_MALLOC( sizeof(NxF32)*hr.mVcount*3);
-			BringOutYourDead(hr.mVertices,hr.mVcount, vscratch, ovcount, hr.mIndices, hr.mIndexCount );
-
-			ret = QE_OK;
-
-			if ( desc.HasHullFlag(QF_TRIANGLES) ) // if he wants the results as triangle!
-			{
-				result.mPolygons          = false;
-				result.mNumOutputVertices = ovcount;
-				result.mOutputVertices    = (NxF32 *)MEMALLOC_MALLOC( sizeof(NxF32)*ovcount*3);
-				result.mNumFaces          = hr.mFaceCount;
-				result.mNumIndices        = hr.mIndexCount;
-
-				result.mIndices           = (NxU32 *) MEMALLOC_MALLOC( sizeof(NxU32)*hr.mIndexCount);
-
-				memcpy(result.mOutputVertices, vscratch, sizeof(NxF32)*3*ovcount );
-
-  			if ( desc.HasHullFlag(QF_REVERSE_ORDER) )
-				{
-
-					const NxU32 *source = hr.mIndices;
-								NxU32 *dest   = result.mIndices;
-
-					for (NxU32 i=0; i<hr.mFaceCount; i++)
-					{
-						dest[0] = source[2];
-						dest[1] = source[1];
-						dest[2] = source[0];
-						dest+=3;
-						source+=3;
-					}
-
-				}
-				else
-				{
-					memcpy(result.mIndices, hr.mIndices, sizeof(NxU32)*hr.mIndexCount);
-				}
-			}
-			else
-			{
-				result.mPolygons          = true;
-				result.mNumOutputVertices = ovcount;
-				result.mOutputVertices    = (NxF32 *)MEMALLOC_MALLOC( sizeof(NxF32)*ovcount*3);
-				result.mNumFaces          = hr.mFaceCount;
-				result.mNumIndices        = hr.mIndexCount+hr.mFaceCount;
-				result.mIndices           = (NxU32 *) MEMALLOC_MALLOC( sizeof(NxU32)*result.mNumIndices);
-				memcpy(result.mOutputVertices, vscratch, sizeof(NxF32)*3*ovcount );
-
-				{
-					const NxU32 *source = hr.mIndices;
-								NxU32 *dest   = result.mIndices;
-					for (NxU32 i=0; i<hr.mFaceCount; i++)
-					{
-						dest[0] = 3;
-						if ( desc.HasHullFlag(QF_REVERSE_ORDER) )
-						{
-							dest[1] = source[2];
-							dest[2] = source[1];
-							dest[3] = source[0];
-						}
-						else
-						{
-							dest[1] = source[0];
-							dest[2] = source[1];
-							dest[3] = source[2];
-						}
-
-						dest+=4;
-						source+=3;
-					}
-				}
-			}
-			// ReleaseHull frees memory for hr.mVertices, which can be the
-			// same pointer as vsource, so be sure to set it to NULL if necessary
-			if ( hr.mVertices == vsource) vsource = NULL;
-
-			ReleaseHull(hr);
-
-			if ( vscratch )
-			{
-				MEMALLOC_FREE(vscratch);
-			}
-		}
-	}
-
-	// this pointer is usually freed in ReleaseHull()
-	if ( vsource )
-	{
-		MEMALLOC_FREE(vsource);
-	}
-
-
-	return ret;
-}
-
-
-
-HullError HullLibrary::ReleaseResult(HullResult &result) // release memory allocated for this result, we are done with it.
-{
-	if ( result.mOutputVertices )
-	{
-		MEMALLOC_FREE(result.mOutputVertices);
-		result.mOutputVertices = 0;
-	}
-	if ( result.mIndices )
-	{
-		MEMALLOC_FREE(result.mIndices);
-		result.mIndices = 0;
-	}
-	return QE_OK;
-}
-
-
-static void AddPoint(NxU32 &vcount,NxF32 *p,NxF32 x,NxF32 y,NxF32 z)
-{
-	NxF32 *dest = &p[vcount*3];
-	dest[0] = x;
-	dest[1] = y;
-	dest[2] = z;
-	vcount++;
-}
-
-
-NxF32 GetDist(NxF32 px,NxF32 py,NxF32 pz,const NxF32 *p2)
-{
-
-	NxF32 dx = px - p2[0];
-	NxF32 dy = py - p2[1];
-	NxF32 dz = pz - p2[2];
-
-	return dx*dx+dy*dy+dz*dz;
-}
-
-
-
-bool  HullLibrary::CleanupVertices(NxU32 svcount,
-																const NxF32 *svertices,
-																NxU32 stride,
-																NxU32 &vcount,       // output number of vertices
-																NxF32 *vertices,                 // location to store the results.
-																NxF32  normalepsilon,
-																NxF32 *scale)
-{
-	if ( svcount == 0 ) return false;
-
-
-	#define EPSILON 0.000001f // close enough to consider two floating point numbers to be 'the same'.
-
-	vcount = 0;
-
-	NxF32 recip[3];
-
-	if ( scale )
-	{
-		scale[0] = 1;
-		scale[1] = 1;
-		scale[2] = 1;
-	}
-
-	NxF32 bmin[3] = {  FLT_MAX,  FLT_MAX,  FLT_MAX };
-	NxF32 bmax[3] = { -FLT_MAX, -FLT_MAX, -FLT_MAX };
-
-	const char *vtx = (const char *) svertices;
-
-	{
-		for (NxU32 i=0; i<svcount; i++)
-		{
-			const NxF32 *p = (const NxF32 *) vtx;
-
-			vtx+=stride;
-
-			for (NxI32 j=0; j<3; j++)
-			{
-				if ( p[j] < bmin[j] ) bmin[j] = p[j];
-				if ( p[j] > bmax[j] ) bmax[j] = p[j];
-			}
-		}
-	}
-
-	NxF32 dx = bmax[0] - bmin[0];
-	NxF32 dy = bmax[1] - bmin[1];
-	NxF32 dz = bmax[2] - bmin[2];
-
-	NxF32 center[3];
-
-	center[0] = dx*0.5f + bmin[0];
-	center[1] = dy*0.5f + bmin[1];
-	center[2] = dz*0.5f + bmin[2];
-
-	if ( dx < EPSILON || dy < EPSILON || dz < EPSILON || svcount < 3 )
-	{
-
-		NxF32 len = FLT_MAX;
-
-		if ( dx > EPSILON && dx < len ) len = dx;
-		if ( dy > EPSILON && dy < len ) len = dy;
-		if ( dz > EPSILON && dz < len ) len = dz;
-
-		if ( len == FLT_MAX )
-		{
-			dx = dy = dz = 0.01f; // one centimeter
-		}
-		else
-		{
-			if ( dx < EPSILON ) dx = len * 0.05f; // 1/5th the shortest non-zero edge.
-			if ( dy < EPSILON ) dy = len * 0.05f;
-			if ( dz < EPSILON ) dz = len * 0.05f;
-		}
-
-		NxF32 x1 = center[0] - dx;
-		NxF32 x2 = center[0] + dx;
-
-		NxF32 y1 = center[1] - dy;
-		NxF32 y2 = center[1] + dy;
-
-		NxF32 z1 = center[2] - dz;
-		NxF32 z2 = center[2] + dz;
-
-		AddPoint(vcount,vertices,x1,y1,z1);
-		AddPoint(vcount,vertices,x2,y1,z1);
-		AddPoint(vcount,vertices,x2,y2,z1);
-		AddPoint(vcount,vertices,x1,y2,z1);
-		AddPoint(vcount,vertices,x1,y1,z2);
-		AddPoint(vcount,vertices,x2,y1,z2);
-		AddPoint(vcount,vertices,x2,y2,z2);
-		AddPoint(vcount,vertices,x1,y2,z2);
-
-		return true; // return cube
-
-
-	}
-	else
-	{
-		if ( scale )
-		{
-			scale[0] = dx;
-			scale[1] = dy;
-			scale[2] = dz;
-
-			recip[0] = 1 / dx;
-			recip[1] = 1 / dy;
-			recip[2] = 1 / dz;
-
-			center[0]*=recip[0];
-			center[1]*=recip[1];
-			center[2]*=recip[2];
-
-		}
-
-	}
-
-
-
-	vtx = (const char *) svertices;
-
-	for (NxU32 i=0; i<svcount; i++)
-	{
-
-		const NxF32 *p = (const NxF32 *)vtx;
-		vtx+=stride;
-
-		NxF32 px = p[0];
-		NxF32 py = p[1];
-		NxF32 pz = p[2];
-
-		if ( scale )
-		{
-			px = px*recip[0]; // normalize
-			py = py*recip[1]; // normalize
-			pz = pz*recip[2]; // normalize
-		}
-
-		{
-			NxU32 j;
-
-			for (j=0; j<vcount; j++)
-			{
-				NxF32 *v = &vertices[j*3];
-
-				NxF32 x = v[0];
-				NxF32 y = v[1];
-				NxF32 z = v[2];
-
-				NxF32 dx = fabsf(x - px );
-				NxF32 dy = fabsf(y - py );
-				NxF32 dz = fabsf(z - pz );
-
-				if ( dx < normalepsilon && dy < normalepsilon && dz < normalepsilon )
-				{
-					// ok, it is close enough to the old one
-					// now let us see if it is further from the center of the point cloud than the one we already recorded.
-					// in which case we keep this one instead.
-
-					NxF32 dist1 = GetDist(px,py,pz,center);
-					NxF32 dist2 = GetDist(v[0],v[1],v[2],center);
-
-					if ( dist1 > dist2 )
-					{
-						v[0] = px;
-						v[1] = py;
-						v[2] = pz;
-					}
-
-					break;
-				}
-			}
-
-			if ( j == vcount )
-			{
-				NxF32 *dest = &vertices[vcount*3];
-				dest[0] = px;
-				dest[1] = py;
-				dest[2] = pz;
-				vcount++;
-			}
-		}
-	}
-
-	// ok..now make sure we didn't prune so many vertices it is now invalid.
-	{
-		NxF32 bmin[3] = {  FLT_MAX,  FLT_MAX,  FLT_MAX };
-		NxF32 bmax[3] = { -FLT_MAX, -FLT_MAX, -FLT_MAX };
-
-		for (NxU32 i=0; i<vcount; i++)
-		{
-			const NxF32 *p = &vertices[i*3];
-			for (NxI32 j=0; j<3; j++)
-			{
-				if ( p[j] < bmin[j] ) bmin[j] = p[j];
-				if ( p[j] > bmax[j] ) bmax[j] = p[j];
-			}
-		}
-
-		NxF32 dx = bmax[0] - bmin[0];
-		NxF32 dy = bmax[1] - bmin[1];
-		NxF32 dz = bmax[2] - bmin[2];
-
-		if ( dx < EPSILON || dy < EPSILON || dz < EPSILON || vcount < 3)
-		{
-			NxF32 cx = dx*0.5f + bmin[0];
-			NxF32 cy = dy*0.5f + bmin[1];
-			NxF32 cz = dz*0.5f + bmin[2];
-
-			NxF32 len = FLT_MAX;
-
-			if ( dx >= EPSILON && dx < len ) len = dx;
-			if ( dy >= EPSILON && dy < len ) len = dy;
-			if ( dz >= EPSILON && dz < len ) len = dz;
-
-			if ( len == FLT_MAX )
-			{
-				dx = dy = dz = 0.01f; // one centimeter
-			}
-			else
-			{
-				if ( dx < EPSILON ) dx = len * 0.05f; // 1/5th the shortest non-zero edge.
-				if ( dy < EPSILON ) dy = len * 0.05f;
-				if ( dz < EPSILON ) dz = len * 0.05f;
-			}
-
-			NxF32 x1 = cx - dx;
-			NxF32 x2 = cx + dx;
-
-			NxF32 y1 = cy - dy;
-			NxF32 y2 = cy + dy;
-
-			NxF32 z1 = cz - dz;
-			NxF32 z2 = cz + dz;
-
-			vcount = 0; // add box
-
-			AddPoint(vcount,vertices,x1,y1,z1);
-			AddPoint(vcount,vertices,x2,y1,z1);
-			AddPoint(vcount,vertices,x2,y2,z1);
-			AddPoint(vcount,vertices,x1,y2,z1);
-			AddPoint(vcount,vertices,x1,y1,z2);
-			AddPoint(vcount,vertices,x2,y1,z2);
-			AddPoint(vcount,vertices,x2,y2,z2);
-			AddPoint(vcount,vertices,x1,y2,z2);
-
-			return true;
-		}
-	}
-
-	return true;
-}
-
-void HullLibrary::BringOutYourDead(const NxF32 *verts,NxU32 vcount, NxF32 *overts,NxU32 &ocount,NxU32 *indices,NxU32 indexcount)
-{
-	NxU32 *used = (NxU32 *)MEMALLOC_MALLOC(sizeof(NxU32)*vcount);
-	memset(used,0,sizeof(NxU32)*vcount);
-
-	ocount = 0;
-
-	for (NxU32 i=0; i<indexcount; i++)
-	{
-		NxU32 v = indices[i]; // original array index
-
-		assert( v < vcount );
-
-		if ( used[v] ) // if already remapped
-		{
-			indices[i] = used[v]-1; // index to new array
-		}
-		else
-		{
-
-			indices[i] = ocount;      // new index mapping
-
-			overts[ocount*3+0] = verts[v*3+0]; // copy old vert to new vert array
-			overts[ocount*3+1] = verts[v*3+1];
-			overts[ocount*3+2] = verts[v*3+2];
-
-			ocount++; // increment output vert count
-
-			assert( ocount <= vcount );
-
-			used[v] = ocount; // assign new index remapping
-		}
-	}
-
-	MEMALLOC_FREE(used);
-}
-
-//==================================================================================
-HullError HullLibrary::CreateTriangleMesh(HullResult &answer,ConvexHullTriangleInterface *iface)
-{
-	HullError ret = QE_FAIL;
-
-
-	const NxF32 *p            = answer.mOutputVertices;
-	const NxU32   *idx = answer.mIndices;
-	NxU32 fcount       = answer.mNumFaces;
-
-	if ( p && idx && fcount )
-	{
-		ret = QE_OK;
-
-		for (NxU32 i=0; i<fcount; i++)
-		{
-			NxU32 pcount = *idx++;
-
-			NxU32 i1 = *idx++;
-			NxU32 i2 = *idx++;
-			NxU32 i3 = *idx++;
-
-			const NxF32 *p1 = &p[i1*3];
-			const NxF32 *p2 = &p[i2*3];
-			const NxF32 *p3 = &p[i3*3];
-
-			AddConvexTriangle(iface,p1,p2,p3);
-
-			pcount-=3;
-			while ( pcount )
-			{
-				i3 = *idx++;
-				p2 = p3;
-				p3 = &p[i3*3];
-
-				AddConvexTriangle(iface,p1,p2,p3);
-				pcount--;
-			}
-
-		}
-	}
-
-	return ret;
-}
-
-//==================================================================================
-void HullLibrary::AddConvexTriangle(ConvexHullTriangleInterface *callback,const NxF32 *p1,const NxF32 *p2,const NxF32 *p3)
-{
-	ConvexHullVertex v1,v2,v3;
-
-	#define TSCALE1 (1.0f/4.0f)
-
-	v1.mPos[0] = p1[0];
-	v1.mPos[1] = p1[1];
-	v1.mPos[2] = p1[2];
-
-	v2.mPos[0] = p2[0];
-	v2.mPos[1] = p2[1];
-	v2.mPos[2] = p2[2];
-
-	v3.mPos[0] = p3[0];
-	v3.mPos[1] = p3[1];
-	v3.mPos[2] = p3[2];
-
-	NxF32 n[3];
-	ComputeNormal(n,p1,p2,p3);
-
-	v1.mNormal[0] = n[0];
-	v1.mNormal[1] = n[1];
-	v1.mNormal[2] = n[2];
-
-	v2.mNormal[0] = n[0];
-	v2.mNormal[1] = n[1];
-	v2.mNormal[2] = n[2];
-
-	v3.mNormal[0] = n[0];
-	v3.mNormal[1] = n[1];
-	v3.mNormal[2] = n[2];
-
-	const NxF32 *tp1 = p1;
-	const NxF32 *tp2 = p2;
-	const NxF32 *tp3 = p3;
-
-	NxI32 i1 = 0;
-	NxI32 i2 = 0;
-
-	NxF32 nx = fabsf(n[0]);
-	NxF32 ny = fabsf(n[1]);
-	NxF32 nz = fabsf(n[2]);
-
-	if ( nx <= ny && nx <= nz )
-		i1 = 0;
-	if ( ny <= nx && ny <= nz )
-		i1 = 1;
-	if ( nz <= nx && nz <= ny )
-		i1 = 2;
-
-	switch ( i1 )
-	{
-		case 0:
-			if ( ny < nz )
-				i2 = 1;
-			else
-				i2 = 2;
-			break;
-		case 1:
-			if ( nx < nz )
-				i2 = 0;
-			else
-				i2 = 2;
-			break;
-		case 2:
-			if ( nx < ny )
-				i2 = 0;
-			else
-				i2 = 1;
-			break;
-	}
-
-	v1.mTexel[0] = tp1[i1]*TSCALE1;
-	v1.mTexel[1] = tp1[i2]*TSCALE1;
-
-	v2.mTexel[0] = tp2[i1]*TSCALE1;
-	v2.mTexel[1] = tp2[i2]*TSCALE1;
-
-	v3.mTexel[0] = tp3[i1]*TSCALE1;
-	v3.mTexel[1] = tp3[i2]*TSCALE1;
-
-	callback->ConvexHullTriangle(v3,v2,v1);
-}
-
-//==================================================================================
-NxF32 HullLibrary::ComputeNormal(NxF32 *n,const NxF32 *A,const NxF32 *B,const NxF32 *C)
-{
-	NxF32 vx,vy,vz,wx,wy,wz,vw_x,vw_y,vw_z,mag;
-
-	vx = (B[0] - C[0]);
-	vy = (B[1] - C[1]);
-	vz = (B[2] - C[2]);
-
-	wx = (A[0] - B[0]);
-	wy = (A[1] - B[1]);
-	wz = (A[2] - B[2]);
-
-	vw_x = vy * wz - vz * wy;
-	vw_y = vz * wx - vx * wz;
-	vw_z = vx * wy - vy * wx;
-
-	mag = sqrtf((vw_x * vw_x) + (vw_y * vw_y) + (vw_z * vw_z));
-
-	if ( mag < 0.000001f )
-	{
-		mag = 0;
-	}
-	else
-	{
-		mag = 1.0f/mag;
-	}
-
-	n[0] = vw_x * mag;
-	n[1] = vw_y * mag;
-	n[2] = vw_z * mag;
-
-	return mag;
-}
-
-}; // End of namespace

+ 0 - 201
Engine/lib/convexDecomp/NvStanHull.h

@@ -1,201 +0,0 @@
-#ifndef NV_STAN_HULL_H
-
-#define NV_STAN_HULL_H
-
-/*
-
-NvStanHull.h : A convex hull generator written by Stan Melax
-
-*/
-
-
-/*!
-**
-** Copyright (c) 2009 by John W. Ratcliff mailto:[email protected]
-**
-** Portions of this source has been released with the PhysXViewer application, as well as
-** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
-**
-** If you find this code useful or you are feeling particularily generous I would
-** ask that you please go to http://www.amillionpixels.us and make a donation
-** to Troy DeMolay.
-**
-** DeMolay is a youth group for young men between the ages of 12 and 21.
-** It teaches strong moral principles, as well as leadership skills and
-** public speaking.  The donations page uses the 'pay for pixels' paradigm
-** where, in this case, a pixel is only a single penny.  Donations can be
-** made for as small as $4 or as high as a $100 block.  Each person who donates
-** will get a link to their own site as well as acknowledgement on the
-** donations blog located here http://www.amillionpixels.blogspot.com/
-**
-** If you wish to contact me you can use the following methods:
-**
-** Skype ID: jratcliff63367
-** Yahoo: jratcliff63367
-** AOL: jratcliff1961
-** email: [email protected]
-**
-**
-** The MIT license:
-**
-** Permission is hereby granted, free of charge, to any person obtaining a copy
-** of this software and associated documentation files (the "Software"), to deal
-** in the Software without restriction, including without limitation the rights
-** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-** copies of the Software, and to permit persons to whom the Software is furnished
-** to do so, subject to the following conditions:
-**
-** The above copyright notice and this permission notice shall be included in all
-** copies or substantial portions of the Software.
-
-** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#include "NvUserMemAlloc.h"
-
-namespace CONVEX_DECOMPOSITION
-{
-
-class HullResult
-{
-public:
-	HullResult(void)
-	{
-		mPolygons = true;
-		mNumOutputVertices = 0;
-		mOutputVertices = 0;
-		mNumFaces = 0;
-		mNumIndices = 0;
-		mIndices = 0;
-	}
-	bool             mPolygons;                  // true if indices represents polygons, false indices are triangles
-	NxU32            mNumOutputVertices;         // number of vertices in the output hull
-	NxF32           *mOutputVertices;            // array of vertices, 3 floats each x,y,z
-	NxU32            mNumFaces;                  // the number of faces produced
-	NxU32            mNumIndices;                // the total number of indices
-	NxU32           *mIndices;                   // pointer to indices.
-
-// If triangles, then indices are array indexes into the vertex list.
-// If polygons, indices are in the form (number of points in face) (p1, p2, p3, ..) etc..
-};
-
-enum HullFlag
-{
-	QF_TRIANGLES         = (1<<0),             // report results as triangles, not polygons.
-	QF_REVERSE_ORDER     = (1<<1),             // reverse order of the triangle indices.
-	QF_SKIN_WIDTH        = (1<<2),             // extrude hull based on this skin width
-	QF_DEFAULT           = (QF_TRIANGLES | QF_SKIN_WIDTH)
-};
-
-
-class HullDesc
-{
-public:
-	HullDesc(void)
-	{
-		mFlags          = QF_DEFAULT;
-		mVcount         = 0;
-		mVertices       = 0;
-		mVertexStride   = 0;
-		mNormalEpsilon  = 0.001f;
-		mMaxVertices = 4096; // maximum number of points to be considered for a convex hull.
-		mSkinWidth = 0.01f; // default is one centimeter
-	};
-
-	HullDesc(HullFlag flag,
-						 NxU32 vcount,
-						 const NxF32 *vertices,
-						 NxU32 stride)
-	{
-		mFlags          = flag;
-		mVcount         = vcount;
-		mVertices       = vertices;
-		mVertexStride   = stride;
-		mNormalEpsilon  = 0.001f;
-		mMaxVertices    = 4096;
-		mSkinWidth = 0.01f; // default is one centimeter
-	}
-
-	bool HasHullFlag(HullFlag flag) const
-	{
-		if ( mFlags & flag ) return true;
-		return false;
-	}
-
-	void SetHullFlag(HullFlag flag)
-	{
-		mFlags|=flag;
-	}
-
-	void ClearHullFlag(HullFlag flag)
-	{
-		mFlags&=~flag;
-	}
-
-	NxU32      mFlags;           // flags to use when generating the convex hull.
-	NxU32      mVcount;          // number of vertices in the input point cloud
-	const NxF32      *mVertices;        // the array of vertices.
-	NxU32      mVertexStride;    // the stride of each vertex, in bytes.
-	NxF32             mNormalEpsilon;   // the epsilon for removing duplicates.  This is a normalized value, if normalized bit is on.
-	NxF32             mSkinWidth;
-	NxU32      mMaxVertices;               // maximum number of vertices to be considered for the hull!
-};
-
-enum HullError
-{
-	QE_OK,            // success!
-	QE_FAIL,           // failed.
-	QE_NOT_READY,
-};
-
-// This class is used when converting a convex hull into a triangle mesh.
-class ConvexHullVertex
-{
-public:
-	NxF32         mPos[3];
-	NxF32         mNormal[3];
-	NxF32         mTexel[2];
-};
-
-// A virtual interface to receive the triangles from the convex hull.
-class ConvexHullTriangleInterface
-{
-public:
-	virtual void ConvexHullTriangle(const ConvexHullVertex &v1,const ConvexHullVertex &v2,const ConvexHullVertex &v3) = 0;
-};
-
-
-class HullLibrary
-{
-public:
-
-	HullError CreateConvexHull(const HullDesc       &desc,           // describes the input request
-															HullResult           &result);        // contains the resulst
-
-	HullError ReleaseResult(HullResult &result); // release memory allocated for this result, we are done with it.
-
-	HullError CreateTriangleMesh(HullResult &answer,ConvexHullTriangleInterface *iface);
-private:
-	NxF32 ComputeNormal(NxF32 *n,const NxF32 *A,const NxF32 *B,const NxF32 *C);
-	void AddConvexTriangle(ConvexHullTriangleInterface *callback,const NxF32 *p1,const NxF32 *p2,const NxF32 *p3);
-
-	void BringOutYourDead(const NxF32 *verts,NxU32 vcount, NxF32 *overts,NxU32 &ocount,NxU32 *indices,NxU32 indexcount);
-
-	bool    CleanupVertices(NxU32 svcount,
-													const NxF32 *svertices,
-													NxU32 stride,
-													NxU32 &vcount,       // output number of vertices
-													NxF32 *vertices,                 // location to store the results.
-													NxF32  normalepsilon,
-													NxF32 *scale);
-};
-
-}; // end of namespace
-
-#endif

+ 0 - 511
Engine/lib/convexDecomp/NvThreadConfig.cpp

@@ -1,511 +0,0 @@
-/*
-
-NvThreadConfig.cpp : A simple wrapper class to define threading and mutex locks.
-
-*/
-/*!
-**
-** Copyright (c) 2009 by John W. Ratcliff mailto:[email protected]
-**
-** Portions of this source has been released with the PhysXViewer application, as well as
-** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
-**
-** If you find this code useful or you are feeling particularily generous I would
-** ask that you please go to http://www.amillionpixels.us and make a donation
-** to Troy DeMolay.
-**
-** DeMolay is a youth group for young men between the ages of 12 and 21.
-** It teaches strong moral principles, as well as leadership skills and
-** public speaking.  The donations page uses the 'pay for pixels' paradigm
-** where, in this case, a pixel is only a single penny.  Donations can be
-** made for as small as $4 or as high as a $100 block.  Each person who donates
-** will get a link to their own site as well as acknowledgement on the
-** donations blog located here http://www.amillionpixels.blogspot.com/
-**
-** If you wish to contact me you can use the following methods:
-**
-** Skype ID: jratcliff63367
-** Yahoo: jratcliff63367
-** AOL: jratcliff1961
-** email: [email protected]
-**
-**
-** The MIT license:
-**
-** Permission is hereby granted, free of charge, to any person obtaining a copy
-** of this software and associated documentation files (the "Software"), to deal
-** in the Software without restriction, including without limitation the rights
-** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-** copies of the Software, and to permit persons to whom the Software is furnished
-** to do so, subject to the following conditions:
-**
-** The above copyright notice and this permission notice shall be included in all
-** copies or substantial portions of the Software.
-
-** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-#include <cassert>
-#include "NvThreadConfig.h"
-
-#if defined(WIN32)
-
-#define _WIN32_WINNT 0x400
-#include <windows.h>
-
-#pragma comment(lib,"winmm.lib")
-
-//	#ifndef _WIN32_WINNT
-
-//	#endif
-//	#include <windows.h>
-//#include <winbase.h>
-#endif
-
-#if defined(_XBOX)
-	#include <xtl.h>
-#endif
-
-#if defined(__linux__) || defined( __APPLE__ ) || defined( __FreeBSD__)
-	//#include <sys/time.h>
-	#include <time.h>
-	#include <unistd.h>
-	#include <errno.h>
-	#define __stdcall
-#endif
-
-#if defined( __APPLE__ ) || defined( __FreeBSD__)
-
-   #include <sys/time.h>
-#endif
-
-#if defined(__APPLE__) || defined(__linux__) || defined( __FreeBSD__)
-
-	#include <pthread.h>
-#endif
-
-#if defined( __APPLE__ ) || defined( __FreeBSD__)
-
-   #define PTHREAD_MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE
-#endif
-
-
-#ifdef	NDEBUG
-#define VERIFY( x ) (x)
-#else
-#define VERIFY( x ) assert((x))
-#endif
-
-namespace CONVEX_DECOMPOSITION
-{
-
-NxU32 tc_timeGetTime(void)
-{
-   #if defined(__linux__)
-      struct timespec ts;
-      clock_gettime(CLOCK_REALTIME, &ts);
-      return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
-    #elif defined( __APPLE__ ) || defined( __FreeBSD__)
-
-      struct timeval tp;
-      gettimeofday(&tp, (struct timezone *)0);
-      return tp.tv_sec * 1000 + tp.tv_usec / 1000;
-   #elif defined( _XBOX )
-      return GetTickCount();
-   #else
-      return timeGetTime();
-   #endif
-}
-
-void   tc_sleep(NxU32 ms)
-{
-   #if defined(__linux__) || defined( __APPLE__ ) ||  defined( __FreeBSD__)
-      usleep(ms * 1000);
-   #else
-      Sleep(ms);
-   #endif
-}
-
-void tc_spinloop()
-{
-
-   #if defined( _XBOX )
-      // Pause would do nothing on the Xbox. Threads are not scheduled.
-   #elif defined( _WIN64 )
-      YieldProcessor( );
-   #elif defined( __APPLE__ )
-      pthread_yield_np();
-   #elif defined(__linux__)  || defined(__FreeBSD__)
-      #if defined(_POSIX_PRIORITY_SCHEDULING)
-         sched_yield();
-      #else
-         asm("pause");
-      #endif
-   #elif
-      __asm { pause };
-   #endif
-}
-
-void tc_interlockedExchange(void *dest, const int64_t exchange)
-{
-   #if defined( __linux__ ) || defined( __APPLE__ ) ||  defined( __FreeBSD__)
-
-	  // not working
-	  assert(false);
-	  //__sync_lock_test_and_set((int64_t*)dest, exchange);
-#elif defined( _XBOX ) || defined( _WIN64 )
-   InterlockedExchange((volatile LONG *)dest, exchange);
-   #else
-      __asm
-      {
-         mov      ebx, dword ptr [exchange]
-         mov      ecx, dword ptr [exchange + 4]
-         mov      edi, dest
-         mov      eax, dword ptr [edi]
-         mov      edx, dword ptr [edi + 4]
-         jmp      start
-      retry:
-         pause
-      start:
-         lock cmpxchg8b [edi]
-         jnz      retry
-      };
-   #endif
-}
-
-NxI32 tc_interlockedCompareExchange(void *dest, NxI32 exchange, NxI32 compare)
-{
-   #if defined( __linux__ ) || defined( __APPLE__ ) ||  defined( __FreeBSD__)
-
-	  // not working
-	  assert(false);
-	  return 0;
-	  //return __sync_val_compare_and_swap((uintptr_t*)dest, exchange, compare);
-	  //return __sync_bool_compare_and_swap((uintptr_t*)dest, exchange, compare);
-   #elif defined( _XBOX ) || defined( _WIN64 )
-     return InterlockedCompareExchange((volatile LONG *)dest, exchange, compare);
-   #else
-      char _ret;
-      //
-      __asm
-      {
-         mov      edx, [dest]
-         mov      eax, [compare]
-         mov      ecx, [exchange]
-
-         lock cmpxchg [edx], ecx
-
-         setz    al
-         mov     byte ptr [_ret], al
-      }
-      //
-      return _ret;
-   #endif
-}
-
-NxI32 tc_interlockedCompareExchange(void *dest, const NxI32 exchange1, const NxI32 exchange2, const NxI32 compare1, const NxI32 compare2)
-{
-   #if defined( __linux__ ) || defined( __APPLE__ ) || defined( __FreeBSD__)
-	  // not working
-      assert(false);
-	  return 0;
-	  //uint64_t exchange = ((uint64_t)exchange1 << 32) | (uint64_t)exchange2;
-	  //uint64_t compare = ((uint64_t)compare1 << 32) | (uint64_t)compare2;
-	  //return __sync_bool_compare_and_swap((int64_t*)dest, exchange, compare);
-   #elif defined( _XBOX ) || defined( _WIN64 )
-     assert(false);
-     return 0;
-   #else
-      char _ret;
-      //
-      __asm
-      {
-         mov     ebx, [exchange1]
-         mov     ecx, [exchange2]
-         mov     edi, [dest]
-         mov     eax, [compare1]
-         mov     edx, [compare2]
-         lock cmpxchg8b [edi]
-         setz    al
-         mov     byte ptr [_ret], al
-      }
-      //
-      return _ret;
-   #endif
-}
-
-class MyThreadMutex : public ThreadMutex
-{
-public:
-  MyThreadMutex(void)
-  {
-    #if defined(WIN32) || defined(_XBOX)
-  	InitializeCriticalSection(&m_Mutex);
-    #elif defined(__APPLE__) || defined(__linux__) ||  defined( __FreeBSD__)
-
-  	pthread_mutexattr_t mutexAttr;  // Mutex Attribute
-  	VERIFY( pthread_mutexattr_init(&mutexAttr) == 0 );
-  	VERIFY( pthread_mutexattr_settype(&mutexAttr, PTHREAD_MUTEX_RECURSIVE_NP) == 0 );
-  	VERIFY( pthread_mutex_init(&m_Mutex, &mutexAttr) == 0 );
-  	VERIFY( pthread_mutexattr_destroy(&mutexAttr) == 0 );
-    #endif
-  }
-
-  ~MyThreadMutex(void)
-  {
-    #if defined(WIN32) || defined(_XBOX)
-  	DeleteCriticalSection(&m_Mutex);
-    #elif defined(__APPLE__) || defined(__linux__) ||  defined( __FreeBSD__)
-
-  	VERIFY( pthread_mutex_destroy(&m_Mutex) == 0 );
-    #endif
-  }
-
-  void lock(void)
-  {
-    #if defined(WIN32) || defined(_XBOX)
-  	EnterCriticalSection(&m_Mutex);
-    #elif defined(__APPLE__) || defined(__linux__) ||  defined( __FreeBSD__)
-
-  	VERIFY( pthread_mutex_lock(&m_Mutex) == 0 );
-    #endif
-  }
-
-  bool tryLock(void)
-  {
-    #if defined(WIN32) || defined(_XBOX)
-  	bool bRet = false;
-  	//assert(("TryEnterCriticalSection seems to not work on XP???", 0));
-  	bRet = TryEnterCriticalSection(&m_Mutex) ? true : false;
-  	return bRet;
-    #elif defined(__APPLE__) || defined(__linux__) ||  defined( __FreeBSD__)
-
-  	NxI32 result = pthread_mutex_trylock(&m_Mutex);
-  	return (result == 0);
-    #endif
-  }
-
-  void unlock(void)
-  {
-    #if defined(WIN32) || defined(_XBOX)
-  	LeaveCriticalSection(&m_Mutex);
-    #elif defined(__APPLE__) || defined(__linux__) ||  defined( __FreeBSD__)
-
-  	VERIFY( pthread_mutex_unlock(&m_Mutex) == 0 );
-    #endif
-  }
-
-private:
-  #if defined(WIN32) || defined(_XBOX)
-	CRITICAL_SECTION m_Mutex;
-	#elif defined(__APPLE__) || defined(__linux__) ||  defined( __FreeBSD__)
-
-	pthread_mutex_t  m_Mutex;
-	#endif
-};
-
-ThreadMutex * tc_createThreadMutex(void)
-{
-  MyThreadMutex *m = new MyThreadMutex;
-  return static_cast< ThreadMutex *>(m);
-}
-
-void          tc_releaseThreadMutex(ThreadMutex *tm)
-{
-  MyThreadMutex *m = static_cast< MyThreadMutex *>(tm);
-  delete m;
-}
-
-#if defined(WIN32) || defined(_XBOX)
-static unsigned long __stdcall _ThreadWorkerFunc(LPVOID arg);
-#elif defined(__APPLE__) || defined(__linux__) ||  defined( __FreeBSD__)
-
-static void* _ThreadWorkerFunc(void* arg);
-#endif
-
-class MyThread : public Thread
-{
-public:
-  MyThread(ThreadInterface *iface)
-  {
-    mInterface = iface;
-	#if defined(WIN32) || defined(_XBOX)
-   	  mThread     = CreateThread(0, 0, _ThreadWorkerFunc, this, 0, 0);
-    #elif defined(__APPLE__) || defined(__linux__) ||  defined( __FreeBSD__)
-
-	  VERIFY( pthread_create(&mThread, NULL, _ThreadWorkerFunc, this) == 0 );
-	#endif
-  }
-
-  ~MyThread(void)
-  {
-	#if defined(WIN32) || defined(_XBOX)
-      if ( mThread )
-      {
-        CloseHandle(mThread);
-        mThread = 0;
-      }
-	#endif
-  }
-
-  void onJobExecute(void)
-  {
-    mInterface->threadMain();
-  }
-
-private:
-  ThreadInterface *mInterface;
-  #if defined(WIN32) || defined(_XBOX)
-    HANDLE           mThread;
-  #elif defined(__APPLE__) || defined(__linux__) ||  defined( __FreeBSD__)
-
-    pthread_t mThread;
-  #endif
-};
-
-
-Thread      * tc_createThread(ThreadInterface *tinterface)
-{
-  MyThread *m = new MyThread(tinterface);
-  return static_cast< Thread *>(m);
-}
-
-void          tc_releaseThread(Thread *t)
-{
-  MyThread *m = static_cast<MyThread *>(t);
-  delete m;
-}
-
-#if defined(WIN32) || defined(_XBOX)
-static unsigned long __stdcall _ThreadWorkerFunc(LPVOID arg)
-#elif defined(__APPLE__) || defined(__linux__) ||  defined( __FreeBSD__)
-
-static void* _ThreadWorkerFunc(void* arg)
-#endif
-{
-  MyThread *worker = (MyThread *) arg;
-	worker->onJobExecute();
-  return 0;
-}
-
-
-class MyThreadEvent : public ThreadEvent
-{
-public:
-  MyThreadEvent(void)
-  {
-	#if defined(WIN32) || defined(_XBOX)
-      mEvent = ::CreateEventA(NULL,TRUE,TRUE,"ThreadEvent");
-	#elif defined(__APPLE__) || defined(__linux__) ||  defined( __FreeBSD__)
-
-	  pthread_mutexattr_t mutexAttr;  // Mutex Attribute
-	  VERIFY( pthread_mutexattr_init(&mutexAttr) == 0 );
-	  VERIFY( pthread_mutexattr_settype(&mutexAttr, PTHREAD_MUTEX_RECURSIVE_NP) == 0 );
-	  VERIFY( pthread_mutex_init(&mEventMutex, &mutexAttr) == 0 );
-	  VERIFY( pthread_mutexattr_destroy(&mutexAttr) == 0 );
-	  VERIFY( pthread_cond_init(&mEvent, NULL) == 0 );
-	#endif
-  }
-
-  ~MyThreadEvent(void)
-  {
-	#if defined(WIN32) || defined(_XBOX)
-    if ( mEvent )
-    {
-      ::CloseHandle(mEvent);
-    }
-	#elif defined(__APPLE__) || defined(__linux__) ||  defined( __FreeBSD__)
-
-	  VERIFY( pthread_cond_destroy(&mEvent) == 0 );
-	  VERIFY( pthread_mutex_destroy(&mEventMutex) == 0 );
-	#endif
-  }
-
-  virtual void setEvent(void)  // signal the event
-  {
-	#if defined(WIN32) || defined(_XBOX)
-    if ( mEvent )
-    {
-      ::SetEvent(mEvent);
-    }
-	#elif defined(__APPLE__) || defined(__linux__) ||  defined( __FreeBSD__)
-
-	  VERIFY( pthread_mutex_lock(&mEventMutex) == 0 );
-	  VERIFY( pthread_cond_signal(&mEvent) == 0 );
-	  VERIFY( pthread_mutex_unlock(&mEventMutex) == 0 );
-	#endif
-  }
-
-  void resetEvent(void)
-  {
-	#if defined(WIN32) || defined(_XBOX)
-    if ( mEvent )
-    {
-      ::ResetEvent(mEvent);
-    }
-	#endif
-  }
-
-  virtual void waitForSingleObject(NxU32 ms)
-  {
-	#if defined(WIN32) || defined(_XBOX)
-    if ( mEvent )
-    {
-      ::WaitForSingleObject(mEvent,ms);
-    }
-	#elif defined(__APPLE__) || defined(__linux__) ||  defined( __FreeBSD__)
-
-      VERIFY( pthread_mutex_lock(&mEventMutex) == 0 );
-	  if (ms == 0xffffffff)
-	  {
-		  VERIFY( pthread_cond_wait(&mEvent, &mEventMutex) == 0 );
-	  }
-	  else
-	  {
-	     struct timespec ts;
-        #ifdef __APPLE__
-        struct timeval tp;
-        gettimeofday(&tp, (struct timezone *)0);
-        ts.tv_nsec = tp.tv_usec * 1000;
-        ts.tv_sec = tp.tv_sec;
-        #else
-	     clock_gettime(CLOCK_REALTIME, &ts);
-        #endif
-	     ts.tv_nsec += ms * 1000000;
-	     ts.tv_sec += ts.tv_nsec / 1000000000;
-	     ts.tv_nsec %= 1000000000;
-		  NxI32 result = pthread_cond_timedwait(&mEvent, &mEventMutex, &ts);
-		  assert(result == 0 || result == ETIMEDOUT);
-	  }
-	  VERIFY( pthread_mutex_unlock(&mEventMutex) == 0 );
-	#endif
-  }
-
-private:
-  #if defined(WIN32) || defined(_XBOX)
-    HANDLE mEvent;
-  #elif defined(__APPLE__) || defined(__linux__) ||  defined( __FreeBSD__)
-
-    pthread_mutex_t mEventMutex;
-    pthread_cond_t mEvent;
-  #endif
-};
-
-ThreadEvent * tc_createThreadEvent(void)
-{
-  MyThreadEvent *m = new MyThreadEvent;
-  return static_cast<ThreadEvent *>(m);
-}
-
-void  tc_releaseThreadEvent(ThreadEvent *t)
-{
-  MyThreadEvent *m = static_cast< MyThreadEvent *>(t);
-  delete m;
-}
-
-}; // end of namespace

+ 0 - 119
Engine/lib/convexDecomp/NvThreadConfig.h

@@ -1,119 +0,0 @@
-#ifndef NV_THREAD_CONFIG_H
-
-#define NV_THREAD_CONFIG_H
-
-#include "NvUserMemAlloc.h"
-
-/*
-
-NvThreadConfig.h : A simple wrapper class to define threading and mutex locks.
-
-*/
-
-
-/*!
-**
-** Copyright (c) 2009 by John W. Ratcliff mailto:[email protected]
-**
-** Portions of this source has been released with the PhysXViewer application, as well as
-** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
-**
-** If you find this code useful or you are feeling particularily generous I would
-** ask that you please go to http://www.amillionpixels.us and make a donation
-** to Troy DeMolay.
-**
-** DeMolay is a youth group for young men between the ages of 12 and 21.
-** It teaches strong moral principles, as well as leadership skills and
-** public speaking.  The donations page uses the 'pay for pixels' paradigm
-** where, in this case, a pixel is only a single penny.  Donations can be
-** made for as small as $4 or as high as a $100 block.  Each person who donates
-** will get a link to their own site as well as acknowledgement on the
-** donations blog located here http://www.amillionpixels.blogspot.com/
-**
-** If you wish to contact me you can use the following methods:
-**
-** Skype ID: jratcliff63367
-** Yahoo: jratcliff63367
-** AOL: jratcliff1961
-** email: [email protected]
-**
-**
-** The MIT license:
-**
-** Permission is hereby granted, free of charge, to any person obtaining a copy
-** of this software and associated documentation files (the "Software"), to deal
-** in the Software without restriction, including without limitation the rights
-** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-** copies of the Software, and to permit persons to whom the Software is furnished
-** to do so, subject to the following conditions:
-**
-** The above copyright notice and this permission notice shall be included in all
-** copies or substantial portions of the Software.
-
-** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#ifdef _MSC_VER
-typedef __int64 int64_t;
-#else
-#include <stdint.h>
-#endif
-
-namespace CONVEX_DECOMPOSITION
-{
-
-NxU32 tc_timeGetTime(void);
-void     tc_sleep(NxU32 ms);
-
-void     tc_spinloop();
-void     tc_interlockedExchange(void *dest, const int64_t exchange);
-NxI32      tc_interlockedCompareExchange(void *dest, NxI32 exchange, NxI32 compare);
-NxI32      tc_interlockedCompareExchange(void *dest, const NxI32 exchange1, const NxI32 exchange2, const NxI32 compare1, const NxI32 compare2);
-
-class ThreadMutex
-{
-public:
-  virtual void lock(void) = 0;
-  virtual void unlock(void) = 0;
-  virtual bool tryLock(void) = 0;
-};
-
-
-ThreadMutex * tc_createThreadMutex(void);
-void          tc_releaseThreadMutex(ThreadMutex *tm);
-
-class ThreadInterface
-{
-public:
-  virtual void threadMain(void) = 0;
-};
-
-class Thread
-{
-public:
-};
-
-Thread      * tc_createThread(ThreadInterface *tinterface);
-void          tc_releaseThread(Thread *t);
-
-class ThreadEvent
-{
-public:
-  virtual void setEvent(void) = 0; // signal the event
-  virtual void resetEvent(void) = 0;
-  virtual void waitForSingleObject(NxU32 ms) = 0;
-};
-
-ThreadEvent * tc_createThreadEvent(void);
-void          tc_releaseThreadEvent(ThreadEvent *t);
-
-}; // end of namespace
-
-
-#endif

+ 0 - 81
Engine/lib/convexDecomp/NvUserMemAlloc.h

@@ -1,81 +0,0 @@
-#ifndef NV_USER_MEMALLOC_H
-
-#define NV_USER_MEMALLOC_H
-
-#include "NvSimpleTypes.h"
-
-/*
-
-NvUserMemAlloc.h : Modify these macros to change the default memory allocation behavior of the convex decomposition code.
-
-*/
-
-
-/*!
-**
-** Copyright (c) 2009 by John W. Ratcliff mailto:[email protected]
-**
-** Portions of this source has been released with the PhysXViewer application, as well as
-** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
-**
-** If you find this code useful or you are feeling particularily generous I would
-** ask that you please go to http://www.amillionpixels.us and make a donation
-** to Troy DeMolay.
-**
-** DeMolay is a youth group for young men between the ages of 12 and 21.
-** It teaches strong moral principles, as well as leadership skills and
-** public speaking.  The donations page uses the 'pay for pixels' paradigm
-** where, in this case, a pixel is only a single penny.  Donations can be
-** made for as small as $4 or as high as a $100 block.  Each person who donates
-** will get a link to their own site as well as acknowledgement on the
-** donations blog located here http://www.amillionpixels.blogspot.com/
-**
-** If you wish to contact me you can use the following methods:
-**
-** Skype ID: jratcliff63367
-** Yahoo: jratcliff63367
-** AOL: jratcliff1961
-** email: [email protected]
-**
-**
-** The MIT license:
-**
-** Permission is hereby granted, free of charge, to any person obtaining a copy
-** of this software and associated documentation files (the "Software"), to deal
-** in the Software without restriction, including without limitation the rights
-** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-** copies of the Software, and to permit persons to whom the Software is furnished
-** to do so, subject to the following conditions:
-**
-** The above copyright notice and this permission notice shall be included in all
-** copies or substantial portions of the Software.
-
-** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#ifndef MEMALLOC_NEW
-#define MEMALLOC_NEW(x) new x
-#define MEMALLOC_MALLOC(x) ::malloc(x)
-#define MEMALLOC_FREE(x) ::free(x)
-#define MEMALLOC_REALLOC(x,y) ::realloc(x,y)
-#endif
-
-namespace CONVEX_DECOMPOSITION
-{
-
-class Memalloc
-{
-public:
-};
-
-}; // end of namespace
-
-
-
-#endif

+ 0 - 38
Engine/lib/convexDecomp/readme.txt

@@ -1,38 +0,0 @@
-The ConvexDecomposition library was written by John W. Ratcliff mailto:[email protected]
-
-What is Convex Decomposition?
-
-Convex Decomposition is when you take an arbitrarily complex triangle mesh and sub-divide it into
-a collection of discrete compound pieces (each represented as a convex hull) to approximate
-the original shape of the objet.
-
-This is required since few physics engines can treat aribtrary triangle mesh objects as dynamic
-objects.  Even those engines which can handle this use case incurr a huge performance and memory
-penalty to do so.
-
-By breaking a complex triangle mesh up into a discrete number of convex components you can greatly
-improve performance for dynamic simulations.
-
---------------------------------------------------------------------------------
-
-This code is released under the MIT license.
-
-The code is functional but could use the following improvements:
-
-(1) The convex hull generator, originally written by Stan Melax, could use some major code cleanup.
-
-(2) The code to remove T-junctions appears to have a bug in it.  This code was working fine before,
-	but I haven't had time to debug why it stopped working.
-
-(3) Island generation once the mesh has been split is currently disabled due to the fact that the
-	Remove Tjunctions functionality has a bug in it.
-
-(4) The code to perform a raycast against a triangle mesh does not currently use any acceleration
-	data structures.
-
-(5) When a split is performed, the surface that got split is not 'capped'.  This causes a problem
-	if you use a high recursion depth on your convex decomposition.  It will cause the object to
-	be modelled as if it had a hollow interior.  A lot of work was done to solve this problem, but
-	it hasn't been integrated into this code drop yet.
-
-

+ 0 - 852
Engine/lib/convexDecomp/wavefront.cpp

@@ -1,852 +0,0 @@
-/*
-
-wavefront.cpp : A very small code snippet to read a Wavefront OBJ file into memory.
-
-*/
-
-
-/*!
-**
-** Copyright (c) 2009 by John W. Ratcliff mailto:[email protected]
-**
-** Portions of this source has been released with the PhysXViewer application, as well as
-** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
-**
-** If you find this code useful or you are feeling particularily generous I would
-** ask that you please go to http://www.amillionpixels.us and make a donation
-** to Troy DeMolay.
-**
-** DeMolay is a youth group for young men between the ages of 12 and 21.
-** It teaches strong moral principles, as well as leadership skills and
-** public speaking.  The donations page uses the 'pay for pixels' paradigm
-** where, in this case, a pixel is only a single penny.  Donations can be
-** made for as small as $4 or as high as a $100 block.  Each person who donates
-** will get a link to their own site as well as acknowledgement on the
-** donations blog located here http://www.amillionpixels.blogspot.com/
-**
-** If you wish to contact me you can use the following methods:
-**
-** Skype ID: jratcliff63367
-** Yahoo: jratcliff63367
-** AOL: jratcliff1961
-** email: [email protected]
-**
-**
-** The MIT license:
-**
-** Permission is hereby granted, free of charge, to any person obtaining a copy
-** of this software and associated documentation files (the "Software"), to deal
-** in the Software without restriction, including without limitation the rights
-** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-** copies of the Software, and to permit persons to whom the Software is furnished
-** to do so, subject to the following conditions:
-**
-** The above copyright notice and this permission notice shall be included in all
-** copies or substantial portions of the Software.
-
-** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-#ifndef __PPCGEKKO__
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <ctype.h>
-
-#include "wavefront.h"
-
-#include <vector>
-
-typedef std::vector< NxI32 > IntVector;
-typedef std::vector< NxF32 > FloatVector;
-
-#pragma warning(disable:4996)
-
-namespace WAVEFRONT
-{
-
-
-/*******************************************************************/
-/******************** InParser.h  ********************************/
-/*******************************************************************/
-class InPlaceParserInterface
-{
-public:
-	virtual NxI32 ParseLine(NxI32 lineno,NxI32 argc,const char **argv) =0;  // return TRUE to continue parsing, return FALSE to abort parsing process
-};
-
-enum SeparatorType
-{
-	ST_DATA,        // is data
-	ST_HARD,        // is a hard separator
-	ST_SOFT,        // is a soft separator
-	ST_EOS          // is a comment symbol, and everything past this character should be ignored
-};
-
-class InPlaceParser
-{
-public:
-	InPlaceParser(void)
-	{
-		Init();
-	}
-
-	InPlaceParser(char *data,NxI32 len)
-	{
-		Init();
-		SetSourceData(data,len);
-	}
-
-	InPlaceParser(const char *fname)
-	{
-		Init();
-		SetFile(fname);
-	}
-
-	~InPlaceParser(void);
-
-	void Init(void)
-	{
-		mQuoteChar = 34;
-		mData = 0;
-		mLen  = 0;
-		mMyAlloc = false;
-		for (NxI32 i=0; i<256; i++)
-		{
-			mHard[i] = ST_DATA;
-			mHardString[i*2] = i;
-			mHardString[i*2+1] = 0;
-		}
-		mHard[0]  = ST_EOS;
-		mHard[32] = ST_SOFT;
-		mHard[9]  = ST_SOFT;
-		mHard[13] = ST_SOFT;
-		mHard[10] = ST_SOFT;
-	}
-
-	void SetFile(const char *fname); // use this file as source data to parse.
-
-	void SetSourceData(char *data,NxI32 len)
-	{
-		mData = data;
-		mLen  = len;
-		mMyAlloc = false;
-	};
-
-	NxI32  Parse(InPlaceParserInterface *callback); // returns true if entire file was parsed, false if it aborted for some reason
-
-	NxI32 ProcessLine(NxI32 lineno,char *line,InPlaceParserInterface *callback);
-
-	const char ** GetArglist(char *source,NxI32 &count); // convert source string into an arg list, this is a destructive parse.
-
-	void SetHardSeparator(char c) // add a hard separator
-	{
-		mHard[c] = ST_HARD;
-	}
-
-	void SetHard(char c) // add a hard separator
-	{
-		mHard[c] = ST_HARD;
-	}
-
-
-	void SetCommentSymbol(char c) // comment character, treated as 'end of string'
-	{
-		mHard[c] = ST_EOS;
-	}
-
-	void ClearHardSeparator(char c)
-	{
-		mHard[c] = ST_DATA;
-	}
-
-
-	void DefaultSymbols(void); // set up default symbols for hard seperator and comment symbol of the '#' character.
-
-	bool EOS(char c)
-	{
-		if ( mHard[c] == ST_EOS )
-		{
-			return true;
-		}
-		return false;
-	}
-
-	void SetQuoteChar(char c)
-	{
-		mQuoteChar = c;
-	}
-
-private:
-
-
-	inline char * AddHard(NxI32 &argc,const char **argv,char *foo);
-	inline bool   IsHard(char c);
-	inline char * SkipSpaces(char *foo);
-	inline bool   IsWhiteSpace(char c);
-	inline bool   IsNonSeparator(char c); // non seperator,neither hard nor soft
-
-	bool   mMyAlloc; // whether or not *I* allocated the buffer and am responsible for deleting it.
-	char  *mData;  // ascii data to parse.
-	NxI32    mLen;   // length of data
-	SeparatorType  mHard[256];
-	char   mHardString[256*2];
-	char           mQuoteChar;
-};
-
-/*******************************************************************/
-/******************** InParser.cpp  ********************************/
-/*******************************************************************/
-void InPlaceParser::SetFile(const char *fname)
-{
-	if ( mMyAlloc )
-	{
-		free(mData);
-	}
-	mData = 0;
-	mLen  = 0;
-	mMyAlloc = false;
-
-	FILE *fph = fopen(fname,"rb");
-	if ( fph )
-	{
-		fseek(fph,0L,SEEK_END);
-		mLen = ftell(fph);
-		fseek(fph,0L,SEEK_SET);
-		if ( mLen )
-		{
-			mData = (char *) malloc(sizeof(char)*(mLen+1));
-			size_t ok = fread(mData, mLen, 1, fph);
-			if ( !ok )
-			{
-				free(mData);
-				mData = 0;
-			}
-			else
-			{
-				mData[mLen] = 0; // zero byte terminate end of file marker.
-				mMyAlloc = true;
-			}
-		}
-		fclose(fph);
-	}
-
-}
-
-InPlaceParser::~InPlaceParser(void)
-{
-	if ( mMyAlloc )
-	{
-		free(mData);
-	}
-}
-
-#define MAXARGS 512
-
-bool InPlaceParser::IsHard(char c)
-{
-	return mHard[c] == ST_HARD;
-}
-
-char * InPlaceParser::AddHard(NxI32 &argc,const char **argv,char *foo)
-{
-	while ( IsHard(*foo) )
-	{
-		const char *hard = &mHardString[*foo*2];
-		if ( argc < MAXARGS )
-		{
-			argv[argc++] = hard;
-		}
-		foo++;
-	}
-	return foo;
-}
-
-bool   InPlaceParser::IsWhiteSpace(char c)
-{
-	return mHard[c] == ST_SOFT;
-}
-
-char * InPlaceParser::SkipSpaces(char *foo)
-{
-	while ( !EOS(*foo) && IsWhiteSpace(*foo) ) foo++;
-	return foo;
-}
-
-bool InPlaceParser::IsNonSeparator(char c)
-{
-	if ( !IsHard(c) && !IsWhiteSpace(c) && c != 0 ) return true;
-	return false;
-}
-
-
-NxI32 InPlaceParser::ProcessLine(NxI32 lineno,char *line,InPlaceParserInterface *callback)
-{
-	NxI32 ret = 0;
-
-	const char *argv[MAXARGS];
-	NxI32 argc = 0;
-
-	char *foo = line;
-
-	while ( !EOS(*foo) && argc < MAXARGS )
-	{
-
-		foo = SkipSpaces(foo); // skip any leading spaces
-
-		if ( EOS(*foo) ) break;
-
-		if ( *foo == mQuoteChar ) // if it is an open quote
-		{
-			foo++;
-			if ( argc < MAXARGS )
-			{
-				argv[argc++] = foo;
-			}
-			while ( !EOS(*foo) && *foo != mQuoteChar ) foo++;
-			if ( !EOS(*foo) )
-			{
-				*foo = 0; // replace close quote with zero byte EOS
-				foo++;
-			}
-		}
-		else
-		{
-
-			foo = AddHard(argc,argv,foo); // add any hard separators, skip any spaces
-
-			if ( IsNonSeparator(*foo) )  // add non-hard argument.
-			{
-				bool quote  = false;
-				if ( *foo == mQuoteChar )
-				{
-					foo++;
-					quote = true;
-				}
-
-				if ( argc < MAXARGS )
-				{
-					argv[argc++] = foo;
-				}
-
-				if ( quote )
-				{
-					while (*foo && *foo != mQuoteChar ) foo++;
-					if ( *foo ) *foo = 32;
-				}
-
-				// continue..until we hit an eos ..
-				while ( !EOS(*foo) ) // until we hit EOS
-				{
-					if ( IsWhiteSpace(*foo) ) // if we hit a space, stomp a zero byte, and exit
-					{
-						*foo = 0;
-						foo++;
-						break;
-					}
-					else if ( IsHard(*foo) ) // if we hit a hard separator, stomp a zero byte and store the hard separator argument
-					{
-						const char *hard = &mHardString[*foo*2];
-						*foo = 0;
-						if ( argc < MAXARGS )
-						{
-							argv[argc++] = hard;
-						}
-						foo++;
-						break;
-					}
-					foo++;
-				} // end of while loop...
-			}
-		}
-	}
-
-	if ( argc )
-	{
-		ret = callback->ParseLine(lineno, argc, argv );
-	}
-
-	return ret;
-}
-
-NxI32  InPlaceParser::Parse(InPlaceParserInterface *callback) // returns true if entire file was parsed, false if it aborted for some reason
-{
-	assert( callback );
-	if ( !mData ) return 0;
-
-	NxI32 ret = 0;
-
-	NxI32 lineno = 0;
-
-	char *foo   = mData;
-	char *begin = foo;
-
-
-	while ( *foo )
-	{
-		if ( *foo == 10 || *foo == 13 )
-		{
-			lineno++;
-			*foo = 0;
-
-			if ( *begin ) // if there is any data to parse at all...
-			{
-				NxI32 v = ProcessLine(lineno,begin,callback);
-				if ( v ) ret = v;
-			}
-
-			foo++;
-			if ( *foo == 10 ) foo++; // skip line feed, if it is in the carraige-return line-feed format...
-			begin = foo;
-		}
-		else
-		{
-			foo++;
-		}
-	}
-
-	lineno++; // lasst line.
-
-	NxI32 v = ProcessLine(lineno,begin,callback);
-	if ( v ) ret = v;
-	return ret;
-}
-
-
-void InPlaceParser::DefaultSymbols(void)
-{
-	SetHardSeparator(',');
-	SetHardSeparator('(');
-	SetHardSeparator(')');
-	SetHardSeparator('=');
-	SetHardSeparator('[');
-	SetHardSeparator(']');
-	SetHardSeparator('{');
-	SetHardSeparator('}');
-	SetCommentSymbol('#');
-}
-
-
-const char ** InPlaceParser::GetArglist(char *line,NxI32 &count) // convert source string into an arg list, this is a destructive parse.
-{
-	const char **ret = 0;
-
-	static const char *argv[MAXARGS];
-	NxI32 argc = 0;
-
-	char *foo = line;
-
-	while ( !EOS(*foo) && argc < MAXARGS )
-	{
-
-		foo = SkipSpaces(foo); // skip any leading spaces
-
-		if ( EOS(*foo) ) break;
-
-		if ( *foo == mQuoteChar ) // if it is an open quote
-		{
-			foo++;
-			if ( argc < MAXARGS )
-			{
-				argv[argc++] = foo;
-			}
-			while ( !EOS(*foo) && *foo != mQuoteChar ) foo++;
-			if ( !EOS(*foo) )
-			{
-				*foo = 0; // replace close quote with zero byte EOS
-				foo++;
-			}
-		}
-		else
-		{
-
-			foo = AddHard(argc,argv,foo); // add any hard separators, skip any spaces
-
-			if ( IsNonSeparator(*foo) )  // add non-hard argument.
-			{
-				bool quote  = false;
-				if ( *foo == mQuoteChar )
-				{
-					foo++;
-					quote = true;
-				}
-
-				if ( argc < MAXARGS )
-				{
-					argv[argc++] = foo;
-				}
-
-				if ( quote )
-				{
-					while (*foo && *foo != mQuoteChar ) foo++;
-					if ( *foo ) *foo = 32;
-				}
-
-				// continue..until we hit an eos ..
-				while ( !EOS(*foo) ) // until we hit EOS
-				{
-					if ( IsWhiteSpace(*foo) ) // if we hit a space, stomp a zero byte, and exit
-					{
-						*foo = 0;
-						foo++;
-						break;
-					}
-					else if ( IsHard(*foo) ) // if we hit a hard separator, stomp a zero byte and store the hard separator argument
-					{
-						const char *hard = &mHardString[*foo*2];
-						*foo = 0;
-						if ( argc < MAXARGS )
-						{
-							argv[argc++] = hard;
-						}
-						foo++;
-						break;
-					}
-					foo++;
-				} // end of while loop...
-			}
-		}
-	}
-
-	count = argc;
-	if ( argc )
-	{
-		ret = argv;
-	}
-
-	return ret;
-}
-
-/*******************************************************************/
-/******************** Geometry.h  ********************************/
-/*******************************************************************/
-
-class GeometryVertex
-{
-public:
-	NxF32        mPos[3];
-	NxF32        mNormal[3];
-	NxF32        mTexel[2];
-};
-
-
-class GeometryInterface
-{
-public:
-
-	virtual void NodeTriangle(const GeometryVertex *v1,const GeometryVertex *v2,const GeometryVertex *v3, bool textured)
-	{
-	}
-
-};
-
-
-/*******************************************************************/
-/******************** Obj.h  ********************************/
-/*******************************************************************/
-
-
-class OBJ : public InPlaceParserInterface
-{
-public:
-  NxI32 LoadMesh(const char *fname,GeometryInterface *callback, bool textured);
-  NxI32 ParseLine(NxI32 lineno,NxI32 argc,const char **argv);  // return TRUE to continue parsing, return FALSE to abort parsing process
-private:
-
-  void GetVertex(GeometryVertex &v,const char *face) const;
-
-  FloatVector     mVerts;
-  FloatVector     mTexels;
-  FloatVector     mNormals;
-
-  bool            mTextured;
-
-  GeometryInterface *mCallback;
-};
-
-
-/*******************************************************************/
-/******************** Obj.cpp  ********************************/
-/*******************************************************************/
-
-NxI32 OBJ::LoadMesh(const char *fname,GeometryInterface *iface, bool textured)
-{
-  mTextured = textured;
-  NxI32 ret = 0;
-
-  mVerts.clear();
-  mTexels.clear();
-  mNormals.clear();
-
-  mCallback = iface;
-
-  InPlaceParser ipp(fname);
-
-  ipp.Parse(this);
-
-return ret;
-}
-
-static const char * GetArg(const char **argv,NxI32 i,NxI32 argc)
-{
-  const char * ret = 0;
-  if ( i < argc ) ret = argv[i];
-  return ret;
-}
-
-void OBJ::GetVertex(GeometryVertex &v,const char *face) const
-{
-  v.mPos[0] = 0;
-  v.mPos[1] = 0;
-  v.mPos[2] = 0;
-
-  v.mTexel[0] = 0;
-  v.mTexel[1] = 0;
-
-  v.mNormal[0] = 0;
-  v.mNormal[1] = 1;
-  v.mNormal[2] = 0;
-
-  NxI32 index = atoi( face )-1;
-
-  const char *texel = strstr(face,"/");
-
-  if ( texel )
-  {
-    NxI32 tindex = atoi( texel+1) - 1;
-
-    if ( tindex >=0 && tindex < (NxI32)(mTexels.size()/2) )
-    {
-    	const NxF32 *t = &mTexels[tindex*2];
-
-      v.mTexel[0] = t[0];
-      v.mTexel[1] = t[1];
-
-    }
-
-    const char *normal = strstr(texel+1,"/");
-    if ( normal )
-    {
-      NxI32 nindex = atoi( normal+1 ) - 1;
-
-      if (nindex >= 0 && nindex < (NxI32)(mNormals.size()/3) )
-      {
-      	const NxF32 *n = &mNormals[nindex*3];
-
-        v.mNormal[0] = n[0];
-        v.mNormal[1] = n[1];
-        v.mNormal[2] = n[2];
-      }
-    }
-  }
-
-  if ( index >= 0 && index < (NxI32)(mVerts.size()/3) )
-  {
-
-    const NxF32 *p = &mVerts[index*3];
-
-    v.mPos[0] = p[0];
-    v.mPos[1] = p[1];
-    v.mPos[2] = p[2];
-  }
-
-}
-
-NxI32 OBJ::ParseLine(NxI32 lineno,NxI32 argc,const char **argv)  // return TRUE to continue parsing, return FALSE to abort parsing process
-{
-  NxI32 ret = 0;
-
-  if ( argc >= 1 )
-  {
-    const char *foo = argv[0];
-    if ( *foo != '#' )
-    {
-      if ( _stricmp(argv[0],"v") == 0 && argc == 4 )
-      {
-        NxF32 vx = (NxF32) atof( argv[1] );
-        NxF32 vy = (NxF32) atof( argv[2] );
-        NxF32 vz = (NxF32) atof( argv[3] );
-        mVerts.push_back(vx);
-        mVerts.push_back(vy);
-        mVerts.push_back(vz);
-      }
-      else if ( _stricmp(argv[0],"vt") == 0 && (argc == 3 || argc == 4))
-      {
-        // ignore 4rd component if present
-        NxF32 tx = (NxF32) atof( argv[1] );
-        NxF32 ty = (NxF32) atof( argv[2] );
-        mTexels.push_back(tx);
-        mTexels.push_back(ty);
-      }
-      else if ( _stricmp(argv[0],"vn") == 0 && argc == 4 )
-      {
-        NxF32 normalx = (NxF32) atof(argv[1]);
-        NxF32 normaly = (NxF32) atof(argv[2]);
-        NxF32 normalz = (NxF32) atof(argv[3]);
-        mNormals.push_back(normalx);
-        mNormals.push_back(normaly);
-        mNormals.push_back(normalz);
-      }
-      else if ( _stricmp(argv[0],"f") == 0 && argc >= 4 )
-      {
-        GeometryVertex v[32];
-
-        NxI32 vcount = argc-1;
-
-        for (NxI32 i=1; i<argc; i++)
-        {
-          GetVertex(v[i-1],argv[i] );
-        }
-
-		mCallback->NodeTriangle(&v[0],&v[1],&v[2], mTextured);
-
-        if ( vcount >=3 ) // do the fan
-        {
-          for (NxI32 i=2; i<(vcount-1); i++)
-          {
-            mCallback->NodeTriangle(&v[0],&v[i],&v[i+1], mTextured);
-          }
-        }
-
-      }
-    }
-  }
-
-  return ret;
-}
-
-
-
-
-class BuildMesh : public GeometryInterface
-{
-public:
-
-	NxI32 GetIndex(const NxF32 *p, const NxF32 *texCoord)
-	{
-
-		NxI32 vcount = (NxI32)mVertices.size()/3;
-
-		if(vcount>0)
-		{
-			//New MS STL library checks indices in debug build, so zero causes an assert if it is empty.
-			const NxF32 *v = &mVertices[0];
-			const NxF32 *t = texCoord != NULL ? &mTexCoords[0] : NULL;
-
-			for (NxI32 i=0; i<vcount; i++)
-			{
-				if ( v[0] == p[0] && v[1] == p[1] && v[2] == p[2] )
-				{
-					if (texCoord == NULL || (t[0] == texCoord[0] && t[1] == texCoord[1]))
-					{
-						return i;
-					}
-				}
-				v+=3;
-				if (t != NULL)
-					t += 2;
-			}
-		}
-
-		mVertices.push_back( p[0] );
-		mVertices.push_back( p[1] );
-		mVertices.push_back( p[2] );
-
-		if (texCoord != NULL)
-		{
-			mTexCoords.push_back( texCoord[0] );
-			mTexCoords.push_back( texCoord[1] );
-		}
-
-		return vcount;
-	}
-
-	virtual void NodeTriangle(const GeometryVertex *v1,const GeometryVertex *v2,const GeometryVertex *v3, bool textured)
-	{
-		mIndices.push_back( GetIndex(v1->mPos, textured ? v1->mTexel : NULL) );
-		mIndices.push_back( GetIndex(v2->mPos, textured ? v2->mTexel : NULL) );
-		mIndices.push_back( GetIndex(v3->mPos, textured ? v3->mTexel : NULL) );
-	}
-
-  const FloatVector& GetVertices(void) const { return mVertices; };
-  const FloatVector& GetTexCoords(void) const { return mTexCoords; };
-  const IntVector& GetIndices(void) const { return mIndices; };
-
-private:
-  FloatVector     mVertices;
-  FloatVector     mTexCoords;
-  IntVector       mIndices;
-};
-
-};
-
-using namespace WAVEFRONT;
-
-WavefrontObj::WavefrontObj(void)
-{
-	mVertexCount = 0;
-	mTriCount    = 0;
-	mIndices     = 0;
-	mVertices    = NULL;
-	mTexCoords   = NULL;
-}
-
-WavefrontObj::~WavefrontObj(void)
-{
-	delete mIndices;
-	delete mVertices;
-}
-
-NxU32 WavefrontObj::loadObj(const char *fname, bool textured) // load a wavefront obj returns number of triangles that were loaded.  Data is persists until the class is destructed.
-{
-
-	NxU32 ret = 0;
-
-	delete mVertices;
-	mVertices = 0;
-	delete mIndices;
-	mIndices = 0;
-	mVertexCount = 0;
-	mTriCount = 0;
-
-
-  BuildMesh bm;
-
-  OBJ obj;
-
-  obj.LoadMesh(fname,&bm, textured);
-
-
-	const FloatVector &vlist = bm.GetVertices();
-	const IntVector &indices = bm.GetIndices();
-	if ( vlist.size() )
-	{
-		mVertexCount = (NxI32)vlist.size()/3;
-		mVertices = new NxF32[mVertexCount*3];
-		memcpy( mVertices, &vlist[0], sizeof(NxF32)*mVertexCount*3 );
-
-		if (textured)
-		{
-			mTexCoords = new NxF32[mVertexCount * 2];
-			const FloatVector& tList = bm.GetTexCoords();
-			memcpy( mTexCoords, &tList[0], sizeof(NxF32) * mVertexCount * 2);
-		}
-
-		mTriCount = (NxI32)indices.size()/3;
-		mIndices = new NxU32[mTriCount*3*sizeof(NxU32)];
-		memcpy(mIndices, &indices[0], sizeof(NxU32)*mTriCount*3);
-		ret = mTriCount;
-	}
-
-
-	return ret;
-}
-
-#endif

+ 0 - 77
Engine/lib/convexDecomp/wavefront.h

@@ -1,77 +0,0 @@
-#ifndef WAVEFRONT_OBJ_H
-
-#define WAVEFRONT_OBJ_H
-
-/*
-
-wavefront.h : A very small code snippet to read a Wavefront OBJ file into memory.
-
-*/
-
-/*!
-**
-** Copyright (c) 2009 by John W. Ratcliff mailto:[email protected]
-**
-** Portions of this source has been released with the PhysXViewer application, as well as
-** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
-**
-** If you find this code useful or you are feeling particularily generous I would
-** ask that you please go to http://www.amillionpixels.us and make a donation
-** to Troy DeMolay.
-**
-** DeMolay is a youth group for young men between the ages of 12 and 21.
-** It teaches strong moral principles, as well as leadership skills and
-** public speaking.  The donations page uses the 'pay for pixels' paradigm
-** where, in this case, a pixel is only a single penny.  Donations can be
-** made for as small as $4 or as high as a $100 block.  Each person who donates
-** will get a link to their own site as well as acknowledgement on the
-** donations blog located here http://www.amillionpixels.blogspot.com/
-**
-** If you wish to contact me you can use the following methods:
-**
-** Skype ID: jratcliff63367
-** Yahoo: jratcliff63367
-** AOL: jratcliff1961
-** email: [email protected]
-**
-**
-** The MIT license:
-**
-** Permission is hereby granted, free of charge, to any person obtaining a copy
-** of this software and associated documentation files (the "Software"), to deal
-** in the Software without restriction, including without limitation the rights
-** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-** copies of the Software, and to permit persons to whom the Software is furnished
-** to do so, subject to the following conditions:
-**
-** The above copyright notice and this permission notice shall be included in all
-** copies or substantial portions of the Software.
-
-** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-#include "NvUserMemAlloc.h"
-
-class WavefrontObj
-{
-public:
-
-  WavefrontObj(void);
-  ~WavefrontObj(void);
-
-	NxU32 loadObj(const char *fname, bool textured); // load a wavefront obj returns number of triangles that were loaded.  Data is persists until the class is destructed.
-
-  NxU32          mVertexCount;
-  NxU32          mTriCount;
-  NxU32          *mIndices;
-  NxF32        *mVertices;
-  NxF32        *mTexCoords;
-};
-
-#endif

+ 3 - 0
Engine/lib/convexMath/CMakeLists.txt

@@ -0,0 +1,3 @@
+file(GLOB CONVEX_DECOMP_SOURCES "*.cpp")
+add_library(convexMath STATIC ${CONVEX_DECOMP_SOURCES})
+target_include_directories(convexMath PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

+ 17 - 0
Engine/lib/convexMath/FloatMath.cpp

@@ -0,0 +1,17 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <math.h>
+#include <float.h>
+#include "FloatMath.h"
+#include <vector>
+
+#define REAL float
+
+#include "FloatMath.inl"
+
+#undef REAL
+#define REAL double
+
+#include "FloatMath.inl"

+ 525 - 0
Engine/lib/convexMath/FloatMath.h

@@ -0,0 +1,525 @@
+#ifndef FLOAT_MATH_LIB_H
+
+#define FLOAT_MATH_LIB_H
+
+
+#include <float.h>
+#include <stdint.h>
+
+namespace FLOAT_MATH
+{
+
+enum FM_ClipState
+{
+  FMCS_XMIN       = (1<<0),
+  FMCS_XMAX       = (1<<1),
+  FMCS_YMIN       = (1<<2),
+  FMCS_YMAX       = (1<<3),
+  FMCS_ZMIN       = (1<<4),
+  FMCS_ZMAX       = (1<<5),
+};
+
+enum FM_Axis
+{
+  FM_XAXIS   = (1<<0),
+  FM_YAXIS   = (1<<1),
+  FM_ZAXIS   = (1<<2)
+};
+
+enum LineSegmentType
+{
+  LS_START,
+  LS_MIDDLE,
+  LS_END
+};
+
+
+const float FM_PI = 3.1415926535897932384626433832795028841971693993751f;
+const float FM_DEG_TO_RAD = ((2.0f * FM_PI) / 360.0f);
+const float FM_RAD_TO_DEG = (360.0f / (2.0f * FM_PI));
+
+//***************** Float versions
+//***
+//*** vectors are assumed to be 3 floats or 3 doubles representing X, Y, Z
+//*** quaternions are assumed to be 4 floats or 4 doubles representing X,Y,Z,W
+//*** matrices are assumed to be 16 floats or 16 doubles representing a standard D3D or OpenGL style 4x4 matrix
+//*** bounding volumes are expressed as two sets of 3 floats/double representing bmin(x,y,z) and bmax(x,y,z)
+//*** Plane equations are assumed to be 4 floats or 4 doubles representing Ax,By,Cz,D
+
+FM_Axis fm_getDominantAxis(const float normal[3]);
+FM_Axis fm_getDominantAxis(const double normal[3]);
+
+void fm_decomposeTransform(const float local_transform[16],float trans[3],float rot[4],float scale[3]);
+void fm_decomposeTransform(const double local_transform[16],double trans[3],double rot[4],double scale[3]);
+
+void  fm_multiplyTransform(const float *pA,const float *pB,float *pM);
+void  fm_multiplyTransform(const double *pA,const double *pB,double *pM);
+
+void  fm_inverseTransform(const float matrix[16],float inverse_matrix[16]);
+void  fm_inverseTransform(const double matrix[16],double inverse_matrix[16]);
+
+void  fm_identity(float matrix[16]); // set 4x4 matrix to identity.
+void  fm_identity(double matrix[16]); // set 4x4 matrix to identity.
+
+void  fm_inverseRT(const float matrix[16], const float pos[3], float t[3]); // inverse rotate translate the point.
+void  fm_inverseRT(const double matrix[16],const double pos[3],double t[3]); // inverse rotate translate the point.
+
+void  fm_transform(const float matrix[16], const float pos[3], float t[3]); // rotate and translate this point.
+void  fm_transform(const double matrix[16],const double pos[3],double t[3]); // rotate and translate this point.
+
+float  fm_getDeterminant(const float matrix[16]);
+double fm_getDeterminant(const double matrix[16]);
+
+void fm_getSubMatrix(int32_t ki,int32_t kj,float pDst[16],const float matrix[16]);
+void fm_getSubMatrix(int32_t ki,int32_t kj,double pDst[16],const float matrix[16]);
+
+void  fm_rotate(const float matrix[16],const float pos[3],float t[3]); // only rotate the point by a 4x4 matrix, don't translate.
+void  fm_rotate(const double matrix[16],const double pos[3],double t[3]); // only rotate the point by a 4x4 matrix, don't translate.
+
+void  fm_eulerToMatrix(float ax,float ay,float az,float matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero)
+void  fm_eulerToMatrix(double ax,double ay,double az,double matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero)
+
+void  fm_getAABB(uint32_t vcount,const float *points,uint32_t pstride,float bmin[3],float bmax[3]);
+void  fm_getAABB(uint32_t vcount,const double *points,uint32_t pstride,double bmin[3],double bmax[3]);
+
+void  fm_getAABBCenter(const float bmin[3],const float bmax[3],float center[3]);
+void  fm_getAABBCenter(const double bmin[3],const double bmax[3],double center[3]);
+
+void fm_transformAABB(const float bmin[3],const float bmax[3],const float matrix[16],float tbmin[3],float tbmax[3]);
+void fm_transformAABB(const double bmin[3],const double bmax[3],const double matrix[16],double tbmin[3],double tbmax[3]);
+
+void  fm_eulerToQuat(float x,float y,float z,float quat[4]); // convert euler angles to quaternion.
+void  fm_eulerToQuat(double x,double y,double z,double quat[4]); // convert euler angles to quaternion.
+
+void  fm_quatToEuler(const float quat[4],float &ax,float &ay,float &az);
+void  fm_quatToEuler(const double quat[4],double &ax,double &ay,double &az);
+
+void  fm_eulerToQuat(const float euler[3],float quat[4]); // convert euler angles to quaternion. Angles must be radians not degrees!
+void  fm_eulerToQuat(const double euler[3],double quat[4]); // convert euler angles to quaternion.
+
+void  fm_scale(float x,float y,float z,float matrix[16]); // apply scale to the matrix.
+void  fm_scale(double x,double y,double z,double matrix[16]); // apply scale to the matrix.
+
+void  fm_eulerToQuatDX(float x,float y,float z,float quat[4]); // convert euler angles to quaternion using the fucked up DirectX method
+void  fm_eulerToQuatDX(double x,double y,double z,double quat[4]); // convert euler angles to quaternion using the fucked up DirectX method
+
+void  fm_eulerToMatrixDX(float x,float y,float z,float matrix[16]); // convert euler angles to quaternion using the fucked up DirectX method.
+void  fm_eulerToMatrixDX(double x,double y,double z,double matrix[16]); // convert euler angles to quaternion using the fucked up DirectX method.
+
+void  fm_quatToMatrix(const float quat[4],float matrix[16]); // convert quaternion rotation to matrix, translation set to zero.
+void  fm_quatToMatrix(const double quat[4],double matrix[16]); // convert quaternion rotation to matrix, translation set to zero.
+
+void  fm_quatRotate(const float quat[4],const float v[3],float r[3]); // rotate a vector directly by a quaternion.
+void  fm_quatRotate(const double quat[4],const double v[3],double r[3]); // rotate a vector directly by a quaternion.
+
+void  fm_getTranslation(const float matrix[16],float t[3]);
+void  fm_getTranslation(const double matrix[16],double t[3]);
+
+void  fm_setTranslation(const float *translation,float matrix[16]);
+void  fm_setTranslation(const double *translation,double matrix[16]);
+
+void  fm_multiplyQuat(const float *qa,const float *qb,float *quat);
+void  fm_multiplyQuat(const double *qa,const double *qb,double *quat);
+
+void  fm_matrixToQuat(const float matrix[16],float quat[4]); // convert the 3x3 portion of a 4x4 matrix into a quaternion as x,y,z,w
+void  fm_matrixToQuat(const double matrix[16],double quat[4]); // convert the 3x3 portion of a 4x4 matrix into a quaternion as x,y,z,w
+
+float fm_sphereVolume(float radius); // return's the volume of a sphere of this radius (4/3 PI * R cubed )
+double fm_sphereVolume(double radius); // return's the volume of a sphere of this radius (4/3 PI * R cubed )
+
+float fm_cylinderVolume(float radius,float h);
+double fm_cylinderVolume(double radius,double h);
+
+float fm_capsuleVolume(float radius,float h);
+double fm_capsuleVolume(double radius,double h);
+
+float fm_distance(const float p1[3],const float p2[3]);
+double fm_distance(const double p1[3],const double p2[3]);
+
+float fm_distanceSquared(const float p1[3],const float p2[3]);
+double fm_distanceSquared(const double p1[3],const double p2[3]);
+
+float fm_distanceSquaredXZ(const float p1[3],const float p2[3]);
+double fm_distanceSquaredXZ(const double p1[3],const double p2[3]);
+
+float fm_computePlane(const float p1[3],const float p2[3],const float p3[3],float *n); // return D
+double fm_computePlane(const double p1[3],const double p2[3],const double p3[3],double *n); // return D
+
+float fm_distToPlane(const float plane[4],const float pos[3]); // computes the distance of this point from the plane.
+double fm_distToPlane(const double plane[4],const double pos[3]); // computes the distance of this point from the plane.
+
+float fm_dot(const float p1[3],const float p2[3]);
+double fm_dot(const double p1[3],const double p2[3]);
+
+void  fm_cross(float cross[3],const float a[3],const float b[3]);
+void  fm_cross(double cross[3],const double a[3],const double b[3]);
+
+float  fm_computeNormalVector(float n[3],const float p1[3],const float p2[3]); // as P2-P1 normalized.
+double  fm_computeNormalVector(double n[3],const double p1[3],const double p2[3]); // as P2-P1 normalized.
+
+bool  fm_computeWindingOrder(const float p1[3],const float p2[3],const float p3[3]); // returns true if the triangle is clockwise.
+bool  fm_computeWindingOrder(const double p1[3],const double p2[3],const double p3[3]); // returns true if the triangle is clockwise.
+
+float  fm_normalize(float n[3]); // normalize this vector and return the distance
+double  fm_normalize(double n[3]); // normalize this vector and return the distance
+
+float  fm_normalizeQuat(float n[4]); // normalize this quat
+double  fm_normalizeQuat(double n[4]); // normalize this quat
+
+void  fm_matrixMultiply(const float A[16],const float B[16],float dest[16]);
+void  fm_matrixMultiply(const double A[16],const double B[16],double dest[16]);
+
+void  fm_composeTransform(const float position[3],const float quat[4],const float scale[3],float matrix[16]);
+void  fm_composeTransform(const double position[3],const double quat[4],const double scale[3],double matrix[16]);
+
+float fm_computeArea(const float p1[3],const float p2[3],const float p3[3]);
+double fm_computeArea(const double p1[3],const double p2[3],const double p3[3]);
+
+void  fm_lerp(const float p1[3],const float p2[3],float dest[3],float lerpValue);
+void  fm_lerp(const double p1[3],const double p2[3],double dest[3],double lerpValue);
+
+bool  fm_insideTriangleXZ(const float test[3],const float p1[3],const float p2[3],const float p3[3]);
+bool  fm_insideTriangleXZ(const double test[3],const double p1[3],const double p2[3],const double p3[3]);
+
+bool  fm_insideAABB(const float pos[3],const float bmin[3],const float bmax[3]);
+bool  fm_insideAABB(const double pos[3],const double bmin[3],const double bmax[3]);
+
+bool  fm_insideAABB(const float obmin[3],const float obmax[3],const float tbmin[3],const float tbmax[3]); // test if bounding box tbmin/tmbax is fully inside obmin/obmax
+bool  fm_insideAABB(const double obmin[3],const double obmax[3],const double tbmin[3],const double tbmax[3]); // test if bounding box tbmin/tmbax is fully inside obmin/obmax
+
+uint32_t fm_clipTestPoint(const float bmin[3],const float bmax[3],const float pos[3]);
+uint32_t fm_clipTestPoint(const double bmin[3],const double bmax[3],const double pos[3]);
+
+uint32_t fm_clipTestPointXZ(const float bmin[3],const float bmax[3],const float pos[3]); // only tests X and Z, not Y
+uint32_t fm_clipTestPointXZ(const double bmin[3],const double bmax[3],const double pos[3]); // only tests X and Z, not Y
+
+
+uint32_t fm_clipTestAABB(const float bmin[3],const float bmax[3],const float p1[3],const float p2[3],const float p3[3],uint32_t &andCode);
+uint32_t fm_clipTestAABB(const double bmin[3],const double bmax[3],const double p1[3],const double p2[3],const double p3[3],uint32_t &andCode);
+
+
+bool     fm_lineTestAABBXZ(const float p1[3],const float p2[3],const float bmin[3],const float bmax[3],float &time);
+bool     fm_lineTestAABBXZ(const double p1[3],const double p2[3],const double bmin[3],const double bmax[3],double &time);
+
+bool     fm_lineTestAABB(const float p1[3],const float p2[3],const float bmin[3],const float bmax[3],float &time);
+bool     fm_lineTestAABB(const double p1[3],const double p2[3],const double bmin[3],const double bmax[3],double &time);
+
+
+void  fm_initMinMax(const float p[3],float bmin[3],float bmax[3]);
+void  fm_initMinMax(const double p[3],double bmin[3],double bmax[3]);
+
+void  fm_initMinMax(float bmin[3],float bmax[3]);
+void  fm_initMinMax(double bmin[3],double bmax[3]);
+
+void  fm_minmax(const float p[3],float bmin[3],float bmax[3]); // accumulate to a min-max value
+void  fm_minmax(const double p[3],double bmin[3],double bmax[3]); // accumulate to a min-max value
+
+// Computes the diagonal length of the bounding box and then inflates the bounding box on all sides
+// by the ratio provided.
+void fm_inflateMinMax(float bmin[3], float bmax[3], float ratio);
+void fm_inflateMinMax(double bmin[3], double bmax[3], double ratio);
+
+float fm_solveX(const float plane[4],float y,float z); // solve for X given this plane equation and the other two components.
+double fm_solveX(const double plane[4],double y,double z); // solve for X given this plane equation and the other two components.
+
+float fm_solveY(const float plane[4],float x,float z); // solve for Y given this plane equation and the other two components.
+double fm_solveY(const double plane[4],double x,double z); // solve for Y given this plane equation and the other two components.
+
+float fm_solveZ(const float plane[4],float x,float y); // solve for Z given this plane equation and the other two components.
+double fm_solveZ(const double plane[4],double x,double y); // solve for Z given this plane equation and the other two components.
+
+bool  fm_computeBestFitPlane(uint32_t vcount,     // number of input data points
+	const float *points,     // starting address of points array.
+	uint32_t vstride,    // stride between input points.
+	const float *weights,    // *optional point weighting values.
+	uint32_t wstride,    // weight stride for each vertex.
+	float plane[4],		// Best fit plane equation
+	float center[3]);  // Best fit weighted center of input points
+
+bool  fm_computeBestFitPlane(uint32_t vcount,     // number of input data points
+	const double *points,     // starting address of points array.
+	uint32_t vstride,    // stride between input points.
+	const double *weights,    // *optional point weighting values.
+	uint32_t wstride,    // weight stride for each vertex.
+	double plane[4],
+	double center[3]); 
+
+// Computes the average center of a set of data points
+bool  fm_computeCentroid(uint32_t vcount,     // number of input data points
+						 const float *points,     // starting address of points array.
+						 float *center);
+
+bool  fm_computeCentroid(uint32_t vcount,     // number of input data points
+						 const double *points,     // starting address of points array.
+						 double *center);
+
+// Compute centroid of a triangle mesh; takes area of each triangle into account
+// weighted average
+bool  fm_computeCentroid(uint32_t vcount,     // number of input data points
+						const float *points,     // starting address of points array.
+						uint32_t triangleCount,
+						const uint32_t *indices,
+						float *center);
+
+// Compute centroid of a triangle mesh; takes area of each triangle into account
+// weighted average
+bool  fm_computeCentroid(uint32_t vcount,     // number of input data points
+	const double *points,     // starting address of points array.
+	uint32_t triangleCount,
+	const uint32_t *indices,
+	double *center);
+
+
+float  fm_computeBestFitAABB(uint32_t vcount,const float *points,uint32_t pstride,float bmin[3],float bmax[3]); // returns the diagonal distance
+double fm_computeBestFitAABB(uint32_t vcount,const double *points,uint32_t pstride,double bmin[3],double bmax[3]); // returns the diagonal distance
+
+float  fm_computeBestFitSphere(uint32_t vcount,const float *points,uint32_t pstride,float center[3]);
+double  fm_computeBestFitSphere(uint32_t vcount,const double *points,uint32_t pstride,double center[3]);
+
+bool fm_lineSphereIntersect(const float center[3],float radius,const float p1[3],const float p2[3],float intersect[3]);
+bool fm_lineSphereIntersect(const double center[3],double radius,const double p1[3],const double p2[3],double intersect[3]);
+
+bool fm_intersectRayAABB(const float bmin[3],const float bmax[3],const float pos[3],const float dir[3],float intersect[3]);
+bool fm_intersectLineSegmentAABB(const float bmin[3],const float bmax[3],const float p1[3],const float p2[3],float intersect[3]);
+
+bool fm_lineIntersectsTriangle(const float rayStart[3],const float rayEnd[3],const float p1[3],const float p2[3],const float p3[3],float sect[3]);
+bool fm_lineIntersectsTriangle(const double rayStart[3],const double rayEnd[3],const double p1[3],const double p2[3],const double p3[3],double sect[3]);
+
+bool fm_rayIntersectsTriangle(const float origin[3],const float dir[3],const float v0[3],const float v1[3],const float v2[3],float &t);
+bool fm_rayIntersectsTriangle(const double origin[3],const double dir[3],const double v0[3],const double v1[3],const double v2[3],double &t);
+
+bool fm_raySphereIntersect(const float center[3],float radius,const float pos[3],const float dir[3],float distance,float intersect[3]);
+bool fm_raySphereIntersect(const double center[3],double radius,const double pos[3],const double dir[3],double distance,double intersect[3]);
+
+void fm_catmullRom(float out_vector[3],const float p1[3],const float p2[3],const float p3[3],const float *p4, const float s);
+void fm_catmullRom(double out_vector[3],const double p1[3],const double p2[3],const double p3[3],const double *p4, const double s);
+
+bool fm_intersectAABB(const float bmin1[3],const float bmax1[3],const float bmin2[3],const float bmax2[3]);
+bool fm_intersectAABB(const double bmin1[3],const double bmax1[3],const double bmin2[3],const double bmax2[3]);
+
+
+// computes the rotation quaternion to go from unit-vector v0 to unit-vector v1
+void fm_rotationArc(const float v0[3],const float v1[3],float quat[4]);
+void fm_rotationArc(const double v0[3],const double v1[3],double quat[4]);
+
+float  fm_distancePointLineSegment(const float Point[3],const float LineStart[3],const float LineEnd[3],float intersection[3],LineSegmentType &type,float epsilon);
+double fm_distancePointLineSegment(const double Point[3],const double LineStart[3],const double LineEnd[3],double intersection[3],LineSegmentType &type,double epsilon);
+
+
+bool fm_colinear(const double p1[3],const double p2[3],const double p3[3],double epsilon=0.999);               // true if these three points in a row are co-linear
+bool fm_colinear(const float  p1[3],const float  p2[3],const float p3[3],float epsilon=0.999f);
+
+bool fm_colinear(const float a1[3],const float a2[3],const float b1[3],const float b2[3],float epsilon=0.999f);  // true if these two line segments are co-linear.
+bool fm_colinear(const double a1[3],const double a2[3],const double b1[3],const double b2[3],double epsilon=0.999);  // true if these two line segments are co-linear.
+
+enum IntersectResult
+{
+  IR_DONT_INTERSECT,
+  IR_DO_INTERSECT,
+  IR_COINCIDENT,
+  IR_PARALLEL,
+};
+
+IntersectResult fm_intersectLineSegments2d(const float a1[3], const float a2[3], const float b1[3], const float b2[3], float intersectionPoint[3]);
+IntersectResult fm_intersectLineSegments2d(const double a1[3],const double a2[3],const double b1[3],const double b2[3],double intersectionPoint[3]);
+
+IntersectResult fm_intersectLineSegments2dTime(const float a1[3], const float a2[3], const float b1[3], const float b2[3],float &t1,float &t2);
+IntersectResult fm_intersectLineSegments2dTime(const double a1[3],const double a2[3],const double b1[3],const double b2[3],double &t1,double &t2);
+
+// Plane-Triangle splitting
+
+enum PlaneTriResult
+{
+  PTR_ON_PLANE,
+  PTR_FRONT,
+  PTR_BACK,
+  PTR_SPLIT,
+};
+
+PlaneTriResult fm_planeTriIntersection(const float plane[4],    // the plane equation in Ax+By+Cz+D format
+									const float *triangle, // the source triangle.
+									uint32_t tstride,  // stride in bytes of the input and output *vertices*
+									float        epsilon,  // the co-planer epsilon value.
+									float       *front,    // the triangle in front of the
+									uint32_t &fcount,  // number of vertices in the 'front' triangle
+									float       *back,     // the triangle in back of the plane
+									uint32_t &bcount); // the number of vertices in the 'back' triangle.
+
+
+PlaneTriResult fm_planeTriIntersection(const double plane[4],    // the plane equation in Ax+By+Cz+D format
+									const double *triangle, // the source triangle.
+									uint32_t tstride,  // stride in bytes of the input and output *vertices*
+									double        epsilon,  // the co-planer epsilon value.
+									double       *front,    // the triangle in front of the
+									uint32_t &fcount,  // number of vertices in the 'front' triangle
+									double       *back,     // the triangle in back of the plane
+									uint32_t &bcount); // the number of vertices in the 'back' triangle.
+
+
+bool fm_intersectPointPlane(const float p1[3],const float p2[3],float *split,const float plane[4]);
+bool fm_intersectPointPlane(const double p1[3],const double p2[3],double *split,const double plane[4]);
+
+PlaneTriResult fm_getSidePlane(const float p[3],const float plane[4],float epsilon);
+PlaneTriResult fm_getSidePlane(const double p[3],const double plane[4],double epsilon);
+
+
+void fm_computeBestFitOBB(uint32_t vcount,const float *points,uint32_t pstride,float *sides,float matrix[16],bool bruteForce=true);
+void fm_computeBestFitOBB(uint32_t vcount,const double *points,uint32_t pstride,double *sides,double matrix[16],bool bruteForce=true);
+
+void fm_computeBestFitOBB(uint32_t vcount,const float *points,uint32_t pstride,float *sides,float pos[3],float quat[4],bool bruteForce=true);
+void fm_computeBestFitOBB(uint32_t vcount,const double *points,uint32_t pstride,double *sides,double pos[3],double quat[4],bool bruteForce=true);
+
+void fm_computeBestFitABB(uint32_t vcount,const float *points,uint32_t pstride,float *sides,float pos[3]);
+void fm_computeBestFitABB(uint32_t vcount,const double *points,uint32_t pstride,double *sides,double pos[3]);
+
+
+//** Note, if the returned capsule height is less than zero, then you must represent it is a sphere of size radius.
+void fm_computeBestFitCapsule(uint32_t vcount,const float *points,uint32_t pstride,float &radius,float &height,float matrix[16],bool bruteForce=true);
+void fm_computeBestFitCapsule(uint32_t vcount,const double *points,uint32_t pstride,float &radius,float &height,double matrix[16],bool bruteForce=true);
+
+
+void fm_planeToMatrix(const float plane[4],float matrix[16]); // convert a plane equation to a 4x4 rotation matrix.  Reference vector is 0,1,0
+void fm_planeToQuat(const float plane[4],float quat[4],float pos[3]); // convert a plane equation to a quaternion and translation
+
+void fm_planeToMatrix(const double plane[4],double matrix[16]); // convert a plane equation to a 4x4 rotation matrix
+void fm_planeToQuat(const double plane[4],double quat[4],double pos[3]); // convert a plane equation to a quaternion and translation
+
+inline void fm_doubleToFloat3(const double p[3],float t[3]) { t[0] = (float) p[0]; t[1] = (float)p[1]; t[2] = (float)p[2]; };
+inline void fm_floatToDouble3(const float p[3],double t[3]) { t[0] = (double)p[0]; t[1] = (double)p[1]; t[2] = (double)p[2]; };
+
+
+void  fm_eulerMatrix(float ax,float ay,float az,float matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero)
+void  fm_eulerMatrix(double ax,double ay,double az,double matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero)
+
+
+float  fm_computeMeshVolume(const float *vertices,uint32_t tcount,const uint32_t *indices);
+double fm_computeMeshVolume(const double *vertices,uint32_t tcount,const uint32_t *indices);
+
+
+#define FM_DEFAULT_GRANULARITY 0.001f  // 1 millimeter is the default granularity
+
+class fm_VertexIndex
+{
+public:
+  virtual uint32_t          getIndex(const float pos[3],bool &newPos) = 0;  // get welded index for this float vector[3]
+  virtual uint32_t          getIndex(const double pos[3],bool &newPos) = 0;  // get welded index for this double vector[3]
+  virtual const float *   getVerticesFloat(void) const = 0;
+  virtual const double *  getVerticesDouble(void) const = 0;
+  virtual const float *   getVertexFloat(uint32_t index) const = 0;
+  virtual const double *  getVertexDouble(uint32_t index) const = 0;
+  virtual uint32_t          getVcount(void) const = 0;
+  virtual bool            isDouble(void) const = 0;
+  virtual bool            saveAsObj(const char *fname,uint32_t tcount,uint32_t *indices) = 0;
+};
+
+fm_VertexIndex * fm_createVertexIndex(double granularity,bool snapToGrid); // create an indexed vertex system for doubles
+fm_VertexIndex * fm_createVertexIndex(float granularity,bool snapToGrid);  // create an indexed vertex system for floats
+void             fm_releaseVertexIndex(fm_VertexIndex *vindex);
+
+
+class fm_Triangulate
+{
+public:
+  virtual const double *       triangulate3d(uint32_t pcount,
+											 const double *points,
+											 uint32_t vstride,
+											 uint32_t &tcount,
+											 bool consolidate,
+											 double epsilon) = 0;
+
+  virtual const float  *       triangulate3d(uint32_t pcount,
+											 const float  *points,
+											 uint32_t vstride,
+											 uint32_t &tcount,
+											 bool consolidate,
+											 float epsilon) = 0;
+};
+
+fm_Triangulate * fm_createTriangulate(void);
+void             fm_releaseTriangulate(fm_Triangulate *t);
+
+
+const float * fm_getPoint(const float *points,uint32_t pstride,uint32_t index);
+const double * fm_getPoint(const double *points,uint32_t pstride,uint32_t index);
+
+bool   fm_insideTriangle(float Ax, float Ay,float Bx, float By,float Cx, float Cy,float Px, float Py);
+bool   fm_insideTriangle(double Ax, double Ay,double Bx, double By,double Cx, double Cy,double Px, double Py);
+float  fm_areaPolygon2d(uint32_t pcount,const float *points,uint32_t pstride);
+double fm_areaPolygon2d(uint32_t pcount,const double *points,uint32_t pstride);
+
+bool  fm_pointInsidePolygon2d(uint32_t pcount,const float *points,uint32_t pstride,const float *point,uint32_t xindex=0,uint32_t yindex=1);
+bool  fm_pointInsidePolygon2d(uint32_t pcount,const double *points,uint32_t pstride,const double *point,uint32_t xindex=0,uint32_t yindex=1);
+
+uint32_t fm_consolidatePolygon(uint32_t pcount,const float *points,uint32_t pstride,float *dest,float epsilon=0.999999f); // collapses co-linear edges.
+uint32_t fm_consolidatePolygon(uint32_t pcount,const double *points,uint32_t pstride,double *dest,double epsilon=0.999999); // collapses co-linear edges.
+
+
+bool fm_computeSplitPlane(uint32_t vcount,const double *vertices,uint32_t tcount,const uint32_t *indices,double *plane);
+bool fm_computeSplitPlane(uint32_t vcount,const float *vertices,uint32_t tcount,const uint32_t *indices,float *plane);
+
+void fm_nearestPointInTriangle(const float *pos,const float *p1,const float *p2,const float *p3,float *nearest);
+void fm_nearestPointInTriangle(const double *pos,const double *p1,const double *p2,const double *p3,double *nearest);
+
+float  fm_areaTriangle(const float *p1,const float *p2,const float *p3);
+double fm_areaTriangle(const double *p1,const double *p2,const double *p3);
+
+void fm_subtract(const float *A,const float *B,float *diff); // compute A-B and store the result in 'diff'
+void fm_subtract(const double *A,const double *B,double *diff); // compute A-B and store the result in 'diff'
+
+void fm_multiply(float *A,float scalar);
+void fm_multiply(double *A,double scalar);
+
+void fm_add(const float *A,const float *B,float *sum);
+void fm_add(const double *A,const double *B,double *sum);
+
+void fm_copy3(const float *source,float *dest);
+void fm_copy3(const double *source,double *dest);
+
+// re-indexes an indexed triangle mesh but drops unused vertices.  The output_indices can be the same pointer as the input indices.
+// the output_vertices can point to the input vertices if you desire.  The output_vertices buffer should be at least the same size
+// is the input buffer.  The routine returns the new vertex count after re-indexing.
+uint32_t  fm_copyUniqueVertices(uint32_t vcount,const float *input_vertices,float *output_vertices,uint32_t tcount,const uint32_t *input_indices,uint32_t *output_indices);
+uint32_t  fm_copyUniqueVertices(uint32_t vcount,const double *input_vertices,double *output_vertices,uint32_t tcount,const uint32_t *input_indices,uint32_t *output_indices);
+
+bool    fm_isMeshCoplanar(uint32_t tcount,const uint32_t *indices,const float *vertices,bool doubleSided); // returns true if this collection of indexed triangles are co-planar!
+bool    fm_isMeshCoplanar(uint32_t tcount,const uint32_t *indices,const double *vertices,bool doubleSided); // returns true if this collection of indexed triangles are co-planar!
+
+bool    fm_samePlane(const float p1[4],const float p2[4],float normalEpsilon=0.01f,float dEpsilon=0.001f,bool doubleSided=false); // returns true if these two plane equations are identical within an epsilon
+bool    fm_samePlane(const double p1[4],const double p2[4],double normalEpsilon=0.01,double dEpsilon=0.001,bool doubleSided=false);
+
+void    fm_OBBtoAABB(const float obmin[3],const float obmax[3],const float matrix[16],float abmin[3],float abmax[3]);
+
+// a utility class that will tessellate a mesh.
+class fm_Tesselate
+{
+public:
+  virtual const uint32_t * tesselate(fm_VertexIndex *vindex,uint32_t tcount,const uint32_t *indices,float longEdge,uint32_t maxDepth,uint32_t &outcount) = 0;
+};
+
+fm_Tesselate * fm_createTesselate(void);
+void           fm_releaseTesselate(fm_Tesselate *t);
+
+void fm_computeMeanNormals(uint32_t vcount,       // the number of vertices
+						   const float *vertices,     // the base address of the vertex position data.
+						   uint32_t vstride,      // the stride between position data.
+						   float *normals,            // the base address  of the destination for mean vector normals
+						   uint32_t nstride,      // the stride between normals
+						   uint32_t tcount,       // the number of triangles
+						   const uint32_t *indices);     // the triangle indices
+
+void fm_computeMeanNormals(uint32_t vcount,       // the number of vertices
+						   const double *vertices,     // the base address of the vertex position data.
+						   uint32_t vstride,      // the stride between position data.
+						   double *normals,            // the base address  of the destination for mean vector normals
+						   uint32_t nstride,      // the stride between normals
+						   uint32_t tcount,       // the number of triangles
+						   const uint32_t *indices);     // the triangle indices
+
+
+bool fm_isValidTriangle(const float *p1,const float *p2,const float *p3,float epsilon=0.00001f);
+bool fm_isValidTriangle(const double *p1,const double *p2,const double *p3,double epsilon=0.00001f);
+
+
+}; // end of namespace
+
+#endif

+ 5280 - 0
Engine/lib/convexMath/FloatMath.inl

@@ -0,0 +1,5280 @@
+// a set of routines that let you do common 3d math
+// operations without any vector, matrix, or quaternion
+// classes or templates.
+//
+// a vector (or point) is a 'float *' to 3 floating point numbers.
+// a matrix is a 'float *' to an array of 16 floating point numbers representing a 4x4 transformation matrix compatible with D3D or OGL
+// a quaternion is a 'float *' to 4 floats representing a quaternion x,y,z,w
+//
+
+#ifdef _MSC_VER
+#pragma warning(disable:4996)
+#endif
+
+namespace FLOAT_MATH
+{
+
+void fm_inverseRT(const REAL matrix[16],const REAL pos[3],REAL t[3]) // inverse rotate translate the point.
+{
+
+	REAL _x = pos[0] - matrix[3*4+0];
+	REAL _y = pos[1] - matrix[3*4+1];
+	REAL _z = pos[2] - matrix[3*4+2];
+
+	// Multiply inverse-translated source vector by inverted rotation transform
+
+	t[0] = (matrix[0*4+0] * _x) + (matrix[0*4+1] * _y) + (matrix[0*4+2] * _z);
+	t[1] = (matrix[1*4+0] * _x) + (matrix[1*4+1] * _y) + (matrix[1*4+2] * _z);
+	t[2] = (matrix[2*4+0] * _x) + (matrix[2*4+1] * _y) + (matrix[2*4+2] * _z);
+
+}
+
+REAL fm_getDeterminant(const REAL matrix[16])
+{
+  REAL tempv[3];
+  REAL p0[3];
+  REAL p1[3];
+  REAL p2[3];
+
+
+	p0[0] = matrix[0*4+0];
+	p0[1] = matrix[0*4+1];
+	p0[2] = matrix[0*4+2];
+
+	p1[0] = matrix[1*4+0];
+	p1[1] = matrix[1*4+1];
+	p1[2] = matrix[1*4+2];
+
+	p2[0] = matrix[2*4+0];
+	p2[1] = matrix[2*4+1];
+	p2[2] = matrix[2*4+2];
+
+  fm_cross(tempv,p1,p2);
+
+  return fm_dot(p0,tempv);
+
+}
+
+REAL fm_squared(REAL x) { return x*x; };
+
+void fm_decomposeTransform(const REAL local_transform[16],REAL trans[3],REAL rot[4],REAL scale[3])
+{
+
+  trans[0] = local_transform[12];
+  trans[1] = local_transform[13];
+  trans[2] = local_transform[14];
+
+  scale[0] = (REAL)sqrt(fm_squared(local_transform[0*4+0]) + fm_squared(local_transform[0*4+1]) + fm_squared(local_transform[0*4+2]));
+  scale[1] = (REAL)sqrt(fm_squared(local_transform[1*4+0]) + fm_squared(local_transform[1*4+1]) + fm_squared(local_transform[1*4+2]));
+  scale[2] = (REAL)sqrt(fm_squared(local_transform[2*4+0]) + fm_squared(local_transform[2*4+1]) + fm_squared(local_transform[2*4+2]));
+
+  REAL m[16];
+  memcpy(m,local_transform,sizeof(REAL)*16);
+
+  REAL sx = 1.0f / scale[0];
+  REAL sy = 1.0f / scale[1];
+  REAL sz = 1.0f / scale[2];
+
+  m[0*4+0]*=sx;
+  m[0*4+1]*=sx;
+  m[0*4+2]*=sx;
+
+  m[1*4+0]*=sy;
+  m[1*4+1]*=sy;
+  m[1*4+2]*=sy;
+
+  m[2*4+0]*=sz;
+  m[2*4+1]*=sz;
+  m[2*4+2]*=sz;
+
+  fm_matrixToQuat(m,rot);
+
+}
+
+void fm_getSubMatrix(int32_t ki,int32_t kj,REAL pDst[16],const REAL matrix[16])
+{
+	int32_t row, col;
+	int32_t dstCol = 0, dstRow = 0;
+
+	for ( col = 0; col < 4; col++ )
+	{
+		if ( col == kj )
+		{
+			continue;
+		}
+		for ( dstRow = 0, row = 0; row < 4; row++ )
+		{
+			if ( row == ki )
+			{
+				continue;
+			}
+			pDst[dstCol*4+dstRow] = matrix[col*4+row];
+			dstRow++;
+		}
+		dstCol++;
+	}
+}
+
+void  fm_inverseTransform(const REAL matrix[16],REAL inverse_matrix[16])
+{
+	REAL determinant = fm_getDeterminant(matrix);
+	determinant = 1.0f / determinant;
+	for (int32_t i = 0; i < 4; i++ )
+	{
+		for (int32_t j = 0; j < 4; j++ )
+		{
+			int32_t sign = 1 - ( ( i + j ) % 2 ) * 2;
+			REAL subMat[16];
+			fm_identity(subMat);
+			fm_getSubMatrix( i, j, subMat, matrix );
+			REAL subDeterminant = fm_getDeterminant(subMat);
+			inverse_matrix[i*4+j] = ( subDeterminant * sign ) * determinant;
+		}
+	}
+}
+
+void fm_identity(REAL matrix[16]) // set 4x4 matrix to identity.
+{
+	matrix[0*4+0] = 1;
+	matrix[1*4+1] = 1;
+	matrix[2*4+2] = 1;
+	matrix[3*4+3] = 1;
+
+	matrix[1*4+0] = 0;
+	matrix[2*4+0] = 0;
+	matrix[3*4+0] = 0;
+
+	matrix[0*4+1] = 0;
+	matrix[2*4+1] = 0;
+	matrix[3*4+1] = 0;
+
+	matrix[0*4+2] = 0;
+	matrix[1*4+2] = 0;
+	matrix[3*4+2] = 0;
+
+	matrix[0*4+3] = 0;
+	matrix[1*4+3] = 0;
+	matrix[2*4+3] = 0;
+
+}
+
+void  fm_quatToEuler(const REAL quat[4],REAL &ax,REAL &ay,REAL &az)
+{
+	REAL x = quat[0];
+	REAL y = quat[1];
+	REAL z = quat[2];
+	REAL w = quat[3];
+
+	REAL sint	     = (2.0f * w * y) - (2.0f * x * z);
+	REAL cost_temp = 1.0f - (sint * sint);
+	REAL cost	   	 = 0;
+
+	if ( (REAL)fabs(cost_temp) > 0.001f )
+	{
+		cost = (REAL)sqrt( cost_temp );
+	}
+
+	REAL sinv, cosv, sinf, cosf;
+	if ( (REAL)fabs(cost) > 0.001f )
+	{
+	cost = 1.0f / cost;
+		sinv = ((2.0f * y * z) + (2.0f * w * x)) * cost;
+		cosv = (1.0f - (2.0f * x * x) - (2.0f * y * y)) * cost;
+		sinf = ((2.0f * x * y) + (2.0f * w * z)) * cost;
+		cosf = (1.0f - (2.0f * y * y) - (2.0f * z * z)) * cost;
+	}
+	else
+	{
+		sinv = (2.0f * w * x) - (2.0f * y * z);
+		cosv = 1.0f - (2.0f * x * x) - (2.0f * z * z);
+		sinf = 0;
+		cosf = 1.0f;
+	}
+
+	// compute output rotations
+	ax	= (REAL)atan2( sinv, cosv );
+	ay	= (REAL)atan2( sint, cost );
+	az	= (REAL)atan2( sinf, cosf );
+
+}
+
+void fm_eulerToMatrix(REAL ax,REAL ay,REAL az,REAL *matrix) // convert euler (in radians) to a dest 4x4 matrix (translation set to zero)
+{
+  REAL quat[4];
+  fm_eulerToQuat(ax,ay,az,quat);
+  fm_quatToMatrix(quat,matrix);
+}
+
+void fm_getAABB(uint32_t vcount,const REAL *points,uint32_t pstride,REAL *bmin,REAL *bmax)
+{
+
+  const uint8_t *source = (const uint8_t *) points;
+
+	bmin[0] = points[0];
+	bmin[1] = points[1];
+	bmin[2] = points[2];
+
+	bmax[0] = points[0];
+	bmax[1] = points[1];
+	bmax[2] = points[2];
+
+
+  for (uint32_t i=1; i<vcount; i++)
+  {
+	source+=pstride;
+	const REAL *p = (const REAL *) source;
+
+	if ( p[0] < bmin[0] ) bmin[0] = p[0];
+	if ( p[1] < bmin[1] ) bmin[1] = p[1];
+	if ( p[2] < bmin[2] ) bmin[2] = p[2];
+
+		if ( p[0] > bmax[0] ) bmax[0] = p[0];
+		if ( p[1] > bmax[1] ) bmax[1] = p[1];
+		if ( p[2] > bmax[2] ) bmax[2] = p[2];
+
+  }
+}
+
+void  fm_eulerToQuat(const REAL *euler,REAL *quat) // convert euler angles to quaternion.
+{
+  fm_eulerToQuat(euler[0],euler[1],euler[2],quat);
+}
+
+void fm_eulerToQuat(REAL roll,REAL pitch,REAL yaw,REAL *quat) // convert euler angles to quaternion.
+{
+	roll  *= 0.5f;
+	pitch *= 0.5f;
+	yaw   *= 0.5f;
+
+	REAL cr = (REAL)cos(roll);
+	REAL cp = (REAL)cos(pitch);
+	REAL cy = (REAL)cos(yaw);
+
+	REAL sr = (REAL)sin(roll);
+	REAL sp = (REAL)sin(pitch);
+	REAL sy = (REAL)sin(yaw);
+
+	REAL cpcy = cp * cy;
+	REAL spsy = sp * sy;
+	REAL spcy = sp * cy;
+	REAL cpsy = cp * sy;
+
+	quat[0]   = ( sr * cpcy - cr * spsy);
+	quat[1]   = ( cr * spcy + sr * cpsy);
+	quat[2]   = ( cr * cpsy - sr * spcy);
+	quat[3]   = cr * cpcy + sr * spsy;
+}
+
+void fm_quatToMatrix(const REAL *quat,REAL *matrix) // convert quaternion rotation to matrix, zeros out the translation component.
+{
+
+	REAL xx = quat[0]*quat[0];
+	REAL yy = quat[1]*quat[1];
+	REAL zz = quat[2]*quat[2];
+	REAL xy = quat[0]*quat[1];
+	REAL xz = quat[0]*quat[2];
+	REAL yz = quat[1]*quat[2];
+	REAL wx = quat[3]*quat[0];
+	REAL wy = quat[3]*quat[1];
+	REAL wz = quat[3]*quat[2];
+
+	matrix[0*4+0] = 1 - 2 * ( yy + zz );
+	matrix[1*4+0] =     2 * ( xy - wz );
+	matrix[2*4+0] =     2 * ( xz + wy );
+
+	matrix[0*4+1] =     2 * ( xy + wz );
+	matrix[1*4+1] = 1 - 2 * ( xx + zz );
+	matrix[2*4+1] =     2 * ( yz - wx );
+
+	matrix[0*4+2] =     2 * ( xz - wy );
+	matrix[1*4+2] =     2 * ( yz + wx );
+	matrix[2*4+2] = 1 - 2 * ( xx + yy );
+
+	matrix[3*4+0] = matrix[3*4+1] = matrix[3*4+2] = (REAL) 0.0f;
+	matrix[0*4+3] = matrix[1*4+3] = matrix[2*4+3] = (REAL) 0.0f;
+	matrix[3*4+3] =(REAL) 1.0f;
+
+}
+
+
+void fm_quatRotate(const REAL *quat,const REAL *v,REAL *r) // rotate a vector directly by a quaternion.
+{
+  REAL left[4];
+
+	left[0] =   quat[3]*v[0] + quat[1]*v[2] - v[1]*quat[2];
+	left[1] =   quat[3]*v[1] + quat[2]*v[0] - v[2]*quat[0];
+	left[2] =   quat[3]*v[2] + quat[0]*v[1] - v[0]*quat[1];
+	left[3] = - quat[0]*v[0] - quat[1]*v[1] - quat[2]*v[2];
+
+	r[0] = (left[3]*-quat[0]) + (quat[3]*left[0]) + (left[1]*-quat[2]) - (-quat[1]*left[2]);
+	r[1] = (left[3]*-quat[1]) + (quat[3]*left[1]) + (left[2]*-quat[0]) - (-quat[2]*left[0]);
+	r[2] = (left[3]*-quat[2]) + (quat[3]*left[2]) + (left[0]*-quat[1]) - (-quat[0]*left[1]);
+
+}
+
+
+void fm_getTranslation(const REAL *matrix,REAL *t)
+{
+	t[0] = matrix[3*4+0];
+	t[1] = matrix[3*4+1];
+	t[2] = matrix[3*4+2];
+}
+
+void fm_matrixToQuat(const REAL *matrix,REAL *quat) // convert the 3x3 portion of a 4x4 matrix into a quaternion as x,y,z,w
+{
+
+	REAL tr = matrix[0*4+0] + matrix[1*4+1] + matrix[2*4+2];
+
+	// check the diagonal
+
+	if (tr > 0.0f )
+	{
+		REAL s = (REAL) sqrt ( (double) (tr + 1.0f) );
+		quat[3] = s * 0.5f;
+		s = 0.5f / s;
+		quat[0] = (matrix[1*4+2] - matrix[2*4+1]) * s;
+		quat[1] = (matrix[2*4+0] - matrix[0*4+2]) * s;
+		quat[2] = (matrix[0*4+1] - matrix[1*4+0]) * s;
+
+	}
+	else
+	{
+		// diagonal is negative
+		int32_t nxt[3] = {1, 2, 0};
+		REAL  qa[4];
+
+		int32_t i = 0;
+
+		if (matrix[1*4+1] > matrix[0*4+0]) i = 1;
+		if (matrix[2*4+2] > matrix[i*4+i]) i = 2;
+
+		int32_t j = nxt[i];
+		int32_t k = nxt[j];
+
+		REAL s = (REAL)sqrt ( ((matrix[i*4+i] - (matrix[j*4+j] + matrix[k*4+k])) + 1.0f) );
+
+		qa[i] = s * 0.5f;
+
+		if (s != 0.0f ) s = 0.5f / s;
+
+		qa[3] = (matrix[j*4+k] - matrix[k*4+j]) * s;
+		qa[j] = (matrix[i*4+j] + matrix[j*4+i]) * s;
+		qa[k] = (matrix[i*4+k] + matrix[k*4+i]) * s;
+
+		quat[0] = qa[0];
+		quat[1] = qa[1];
+		quat[2] = qa[2];
+		quat[3] = qa[3];
+	}
+//	fm_normalizeQuat(quat);
+}
+
+
+REAL fm_sphereVolume(REAL radius) // return's the volume of a sphere of this radius (4/3 PI * R cubed )
+{
+	return (4.0f / 3.0f ) * FM_PI * radius * radius * radius;
+}
+
+
+REAL fm_cylinderVolume(REAL radius,REAL h)
+{
+	return FM_PI * radius * radius *h;
+}
+
+REAL fm_capsuleVolume(REAL radius,REAL h)
+{
+	REAL volume = fm_sphereVolume(radius); // volume of the sphere portion.
+	REAL ch = h-radius*2; // this is the cylinder length
+	if ( ch > 0 )
+	{
+		volume+=fm_cylinderVolume(radius,ch);
+	}
+	return volume;
+}
+
+void  fm_transform(const REAL matrix[16],const REAL v[3],REAL t[3]) // rotate and translate this point
+{
+  if ( matrix )
+  {
+	REAL tx = (matrix[0*4+0] * v[0]) +  (matrix[1*4+0] * v[1]) + (matrix[2*4+0] * v[2]) + matrix[3*4+0];
+	REAL ty = (matrix[0*4+1] * v[0]) +  (matrix[1*4+1] * v[1]) + (matrix[2*4+1] * v[2]) + matrix[3*4+1];
+	REAL tz = (matrix[0*4+2] * v[0]) +  (matrix[1*4+2] * v[1]) + (matrix[2*4+2] * v[2]) + matrix[3*4+2];
+	t[0] = tx;
+	t[1] = ty;
+	t[2] = tz;
+  }
+  else
+  {
+	t[0] = v[0];
+	t[1] = v[1];
+	t[2] = v[2];
+  }
+}
+
+void  fm_rotate(const REAL matrix[16],const REAL v[3],REAL t[3]) // rotate and translate this point
+{
+  if ( matrix )
+  {
+	REAL tx = (matrix[0*4+0] * v[0]) +  (matrix[1*4+0] * v[1]) + (matrix[2*4+0] * v[2]);
+	REAL ty = (matrix[0*4+1] * v[0]) +  (matrix[1*4+1] * v[1]) + (matrix[2*4+1] * v[2]);
+	REAL tz = (matrix[0*4+2] * v[0]) +  (matrix[1*4+2] * v[1]) + (matrix[2*4+2] * v[2]);
+	t[0] = tx;
+	t[1] = ty;
+	t[2] = tz;
+  }
+  else
+  {
+	t[0] = v[0];
+	t[1] = v[1];
+	t[2] = v[2];
+  }
+}
+
+
+REAL fm_distance(const REAL *p1,const REAL *p2)
+{
+	REAL dx = p1[0] - p2[0];
+	REAL dy = p1[1] - p2[1];
+	REAL dz = p1[2] - p2[2];
+
+	return (REAL)sqrt( dx*dx + dy*dy + dz *dz );
+}
+
+REAL fm_distanceSquared(const REAL *p1,const REAL *p2)
+{
+	REAL dx = p1[0] - p2[0];
+	REAL dy = p1[1] - p2[1];
+	REAL dz = p1[2] - p2[2];
+
+	return dx*dx + dy*dy + dz *dz;
+}
+
+
+REAL fm_distanceSquaredXZ(const REAL *p1,const REAL *p2)
+{
+	REAL dx = p1[0] - p2[0];
+	REAL dz = p1[2] - p2[2];
+
+	return dx*dx +  dz *dz;
+}
+
+
+REAL fm_computePlane(const REAL *A,const REAL *B,const REAL *C,REAL *n) // returns D
+{
+	REAL vx = (B[0] - C[0]);
+	REAL vy = (B[1] - C[1]);
+	REAL vz = (B[2] - C[2]);
+
+	REAL wx = (A[0] - B[0]);
+	REAL wy = (A[1] - B[1]);
+	REAL wz = (A[2] - B[2]);
+
+	REAL vw_x = vy * wz - vz * wy;
+	REAL vw_y = vz * wx - vx * wz;
+	REAL vw_z = vx * wy - vy * wx;
+
+	REAL mag = (REAL)sqrt((vw_x * vw_x) + (vw_y * vw_y) + (vw_z * vw_z));
+
+	if ( mag < 0.000001f )
+	{
+		mag = 0;
+	}
+	else
+	{
+		mag = 1.0f/mag;
+	}
+
+	REAL x = vw_x * mag;
+	REAL y = vw_y * mag;
+	REAL z = vw_z * mag;
+
+
+	REAL D = 0.0f - ((x*A[0])+(y*A[1])+(z*A[2]));
+
+  n[0] = x;
+  n[1] = y;
+  n[2] = z;
+
+	return D;
+}
+
+REAL fm_distToPlane(const REAL *plane,const REAL *p) // computes the distance of this point from the plane.
+{
+  return p[0]*plane[0]+p[1]*plane[1]+p[2]*plane[2]+plane[3];
+}
+
+REAL fm_dot(const REAL *p1,const REAL *p2)
+{
+  return p1[0]*p2[0]+p1[1]*p2[1]+p1[2]*p2[2];
+}
+
+void fm_cross(REAL *cross,const REAL *a,const REAL *b)
+{
+	cross[0] = a[1]*b[2] - a[2]*b[1];
+	cross[1] = a[2]*b[0] - a[0]*b[2];
+	cross[2] = a[0]*b[1] - a[1]*b[0];
+}
+
+REAL fm_computeNormalVector(REAL *n,const REAL *p1,const REAL *p2)
+{
+  n[0] = p2[0] - p1[0];
+  n[1] = p2[1] - p1[1];
+  n[2] = p2[2] - p1[2];
+  return fm_normalize(n);
+}
+
+bool  fm_computeWindingOrder(const REAL *p1,const REAL *p2,const REAL *p3) // returns true if the triangle is clockwise.
+{
+  bool ret = false;
+
+  REAL v1[3];
+  REAL v2[3];
+
+  fm_computeNormalVector(v1,p1,p2); // p2-p1 (as vector) and then normalized
+  fm_computeNormalVector(v2,p1,p3); // p3-p1 (as vector) and then normalized
+
+  REAL cross[3];
+
+  fm_cross(cross, v1, v2 );
+  REAL ref[3] = { 1, 0, 0 };
+
+  REAL d = fm_dot( cross, ref );
+
+
+  if ( d <= 0 )
+	ret = false;
+  else
+	ret = true;
+
+  return ret;
+}
+
+REAL fm_normalize(REAL *n) // normalize this vector
+{
+  REAL dist = (REAL)sqrt(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]);
+  if ( dist > 0.0000001f )
+  {
+	REAL mag = 1.0f / dist;
+	n[0]*=mag;
+	n[1]*=mag;
+	n[2]*=mag;
+  }
+  else
+  {
+	n[0] = 1;
+	n[1] = 0;
+	n[2] = 0;
+  }
+
+  return dist;
+}
+
+
+void  fm_matrixMultiply(const REAL *pA,const REAL *pB,REAL *pM)
+{
+#if 1
+
+  REAL a = pA[0*4+0] * pB[0*4+0] + pA[0*4+1] * pB[1*4+0] + pA[0*4+2] * pB[2*4+0] + pA[0*4+3] * pB[3*4+0];
+  REAL b = pA[0*4+0] * pB[0*4+1] + pA[0*4+1] * pB[1*4+1] + pA[0*4+2] * pB[2*4+1] + pA[0*4+3] * pB[3*4+1];
+  REAL c = pA[0*4+0] * pB[0*4+2] + pA[0*4+1] * pB[1*4+2] + pA[0*4+2] * pB[2*4+2] + pA[0*4+3] * pB[3*4+2];
+  REAL d = pA[0*4+0] * pB[0*4+3] + pA[0*4+1] * pB[1*4+3] + pA[0*4+2] * pB[2*4+3] + pA[0*4+3] * pB[3*4+3];
+
+  REAL e = pA[1*4+0] * pB[0*4+0] + pA[1*4+1] * pB[1*4+0] + pA[1*4+2] * pB[2*4+0] + pA[1*4+3] * pB[3*4+0];
+  REAL f = pA[1*4+0] * pB[0*4+1] + pA[1*4+1] * pB[1*4+1] + pA[1*4+2] * pB[2*4+1] + pA[1*4+3] * pB[3*4+1];
+  REAL g = pA[1*4+0] * pB[0*4+2] + pA[1*4+1] * pB[1*4+2] + pA[1*4+2] * pB[2*4+2] + pA[1*4+3] * pB[3*4+2];
+  REAL h = pA[1*4+0] * pB[0*4+3] + pA[1*4+1] * pB[1*4+3] + pA[1*4+2] * pB[2*4+3] + pA[1*4+3] * pB[3*4+3];
+
+  REAL i = pA[2*4+0] * pB[0*4+0] + pA[2*4+1] * pB[1*4+0] + pA[2*4+2] * pB[2*4+0] + pA[2*4+3] * pB[3*4+0];
+  REAL j = pA[2*4+0] * pB[0*4+1] + pA[2*4+1] * pB[1*4+1] + pA[2*4+2] * pB[2*4+1] + pA[2*4+3] * pB[3*4+1];
+  REAL k = pA[2*4+0] * pB[0*4+2] + pA[2*4+1] * pB[1*4+2] + pA[2*4+2] * pB[2*4+2] + pA[2*4+3] * pB[3*4+2];
+  REAL l = pA[2*4+0] * pB[0*4+3] + pA[2*4+1] * pB[1*4+3] + pA[2*4+2] * pB[2*4+3] + pA[2*4+3] * pB[3*4+3];
+
+  REAL m = pA[3*4+0] * pB[0*4+0] + pA[3*4+1] * pB[1*4+0] + pA[3*4+2] * pB[2*4+0] + pA[3*4+3] * pB[3*4+0];
+  REAL n = pA[3*4+0] * pB[0*4+1] + pA[3*4+1] * pB[1*4+1] + pA[3*4+2] * pB[2*4+1] + pA[3*4+3] * pB[3*4+1];
+  REAL o = pA[3*4+0] * pB[0*4+2] + pA[3*4+1] * pB[1*4+2] + pA[3*4+2] * pB[2*4+2] + pA[3*4+3] * pB[3*4+2];
+  REAL p = pA[3*4+0] * pB[0*4+3] + pA[3*4+1] * pB[1*4+3] + pA[3*4+2] * pB[2*4+3] + pA[3*4+3] * pB[3*4+3];
+
+  pM[0] = a;
+  pM[1] = b;
+  pM[2] = c;
+  pM[3] = d;
+
+  pM[4] = e;
+  pM[5] = f;
+  pM[6] = g;
+  pM[7] = h;
+
+  pM[8] = i;
+  pM[9] = j;
+  pM[10] = k;
+  pM[11] = l;
+
+  pM[12] = m;
+  pM[13] = n;
+  pM[14] = o;
+  pM[15] = p;
+
+
+#else
+	memset(pM, 0, sizeof(REAL)*16);
+	for(int32_t i=0; i<4; i++ )
+		for(int32_t j=0; j<4; j++ )
+			for(int32_t k=0; k<4; k++ )
+				pM[4*i+j] +=  pA[4*i+k] * pB[4*k+j];
+#endif
+}
+
+
+void  fm_eulerToQuatDX(REAL x,REAL y,REAL z,REAL *quat) // convert euler angles to quaternion using the fucked up DirectX method
+{
+  REAL matrix[16];
+  fm_eulerToMatrix(x,y,z,matrix);
+  fm_matrixToQuat(matrix,quat);
+}
+
+// implementation copied from: http://blogs.msdn.com/mikepelton/archive/2004/10/29/249501.aspx
+void  fm_eulerToMatrixDX(REAL x,REAL y,REAL z,REAL *matrix) // convert euler angles to quaternion using the fucked up DirectX method.
+{
+  fm_identity(matrix);
+  matrix[0*4+0] = (REAL)(cos(z)*cos(y) + sin(z)*sin(x)*sin(y));
+  matrix[0*4+1] = (REAL)(sin(z)*cos(x));
+  matrix[0*4+2] = (REAL)(cos(z)*-sin(y) + sin(z)*sin(x)*cos(y));
+
+  matrix[1*4+0] = (REAL)(-sin(z)*cos(y)+cos(z)*sin(x)*sin(y));
+  matrix[1*4+1] = (REAL)(cos(z)*cos(x));
+  matrix[1*4+2] = (REAL)(sin(z)*sin(y) +cos(z)*sin(x)*cos(y));
+
+  matrix[2*4+0] = (REAL)(cos(x)*sin(y));
+  matrix[2*4+1] = (REAL)(-sin(x));
+  matrix[2*4+2] = (REAL)(cos(x)*cos(y));
+}
+
+
+void  fm_scale(REAL x,REAL y,REAL z,REAL *fscale) // apply scale to the matrix.
+{
+  fscale[0*4+0] = x;
+  fscale[1*4+1] = y;
+  fscale[2*4+2] = z;
+}
+
+
+void  fm_composeTransform(const REAL *position,const REAL *quat,const REAL *scale,REAL *matrix)
+{
+  fm_identity(matrix);
+  fm_quatToMatrix(quat,matrix);
+
+  if ( scale && ( scale[0] != 1 || scale[1] != 1 || scale[2] != 1 ) )
+  {
+	REAL work[16];
+	memcpy(work,matrix,sizeof(REAL)*16);
+	REAL mscale[16];
+	fm_identity(mscale);
+	fm_scale(scale[0],scale[1],scale[2],mscale);
+	fm_matrixMultiply(work,mscale,matrix);
+  }
+
+  matrix[12] = position[0];
+  matrix[13] = position[1];
+  matrix[14] = position[2];
+}
+
+
+void  fm_setTranslation(const REAL *translation,REAL *matrix)
+{
+  matrix[12] = translation[0];
+  matrix[13] = translation[1];
+  matrix[14] = translation[2];
+}
+
+static REAL enorm0_3d ( REAL x0, REAL y0, REAL z0, REAL x1, REAL y1, REAL z1 )
+
+/**********************************************************************/
+
+/*
+Purpose:
+
+ENORM0_3D computes the Euclidean norm of (P1-P0) in 3D.
+
+Modified:
+
+18 April 1999
+
+Author:
+
+John Burkardt
+
+Parameters:
+
+Input, REAL X0, Y0, Z0, X1, Y1, Z1, the coordinates of the points 
+P0 and P1.
+
+Output, REAL ENORM0_3D, the Euclidean norm of (P1-P0).
+*/
+{
+  REAL value;
+
+  value = (REAL)sqrt (
+	( x1 - x0 ) * ( x1 - x0 ) + 
+	( y1 - y0 ) * ( y1 - y0 ) + 
+	( z1 - z0 ) * ( z1 - z0 ) );
+
+  return value;
+}
+
+
+static REAL triangle_area_3d ( REAL x1, REAL y1, REAL z1, REAL x2,REAL y2, REAL z2, REAL x3, REAL y3, REAL z3 )
+
+						/**********************************************************************/
+
+						/*
+						Purpose:
+
+						TRIANGLE_AREA_3D computes the area of a triangle in 3D.
+
+						Modified:
+
+						22 April 1999
+
+						Author:
+
+						John Burkardt
+
+						Parameters:
+
+						Input, REAL X1, Y1, Z1, X2, Y2, Z2, X3, Y3, Z3, the (X,Y,Z)
+						coordinates of the corners of the triangle.
+
+						Output, REAL TRIANGLE_AREA_3D, the area of the triangle.
+						*/
+{
+  REAL a;
+  REAL alpha;
+  REAL area;
+  REAL b;
+  REAL base;
+  REAL c;
+  REAL dot;
+  REAL height;
+  /*
+  Find the projection of (P3-P1) onto (P2-P1).
+  */
+  dot = 
+	( x2 - x1 ) * ( x3 - x1 ) +
+	( y2 - y1 ) * ( y3 - y1 ) +
+	( z2 - z1 ) * ( z3 - z1 );
+
+  base = enorm0_3d ( x1, y1, z1, x2, y2, z2 );
+  /*
+  The height of the triangle is the length of (P3-P1) after its
+  projection onto (P2-P1) has been subtracted.
+  */
+  if ( base == 0.0 ) {
+
+	height = 0.0;
+
+  }
+  else {
+
+	alpha = dot / ( base * base );
+
+	a = x3 - x1 - alpha * ( x2 - x1 );
+	b = y3 - y1 - alpha * ( y2 - y1 );
+	c = z3 - z1 - alpha * ( z2 - z1 );
+
+	height = (REAL)sqrt ( a * a + b * b + c * c );
+
+  }
+
+  area = 0.5f * base * height;
+
+  return area;
+}
+
+
+REAL fm_computeArea(const REAL *p1,const REAL *p2,const REAL *p3)
+{
+  REAL ret = 0;
+
+  ret = triangle_area_3d(p1[0],p1[1],p1[2],p2[0],p2[1],p2[2],p3[0],p3[1],p3[2]);
+
+  return ret;
+}
+
+
+void  fm_lerp(const REAL *p1,const REAL *p2,REAL *dest,REAL lerpValue)
+{
+  dest[0] = ((p2[0] - p1[0])*lerpValue) + p1[0];
+  dest[1] = ((p2[1] - p1[1])*lerpValue) + p1[1];
+  dest[2] = ((p2[2] - p1[2])*lerpValue) + p1[2];
+}
+
+bool fm_pointTestXZ(const REAL *p,const REAL *i,const REAL *j)
+{
+  bool ret = false;
+
+  if (((( i[2] <= p[2] ) && ( p[2]  < j[2] )) || (( j[2] <= p[2] ) && ( p[2]  < i[2] ))) && ( p[0] < (j[0] - i[0]) * (p[2] - i[2]) / (j[2] - i[2]) + i[0]))
+	ret = true;
+
+  return ret;
+};
+
+
+bool  fm_insideTriangleXZ(const REAL *p,const REAL *p1,const REAL *p2,const REAL *p3)
+{
+  bool ret = false;
+
+  int32_t c = 0;
+  if ( fm_pointTestXZ(p,p1,p2) ) c = !c;
+  if ( fm_pointTestXZ(p,p2,p3) ) c = !c;
+  if ( fm_pointTestXZ(p,p3,p1) ) c = !c;
+  if ( c ) ret = true;
+
+  return ret;
+}
+
+bool  fm_insideAABB(const REAL *pos,const REAL *bmin,const REAL *bmax)
+{
+  bool ret = false;
+
+  if ( pos[0] >= bmin[0] && pos[0] <= bmax[0] &&
+	   pos[1] >= bmin[1] && pos[1] <= bmax[1] &&
+	   pos[2] >= bmin[2] && pos[2] <= bmax[2] )
+	ret = true;
+
+  return ret;
+}
+
+
+uint32_t fm_clipTestPoint(const REAL *bmin,const REAL *bmax,const REAL *pos)
+{
+  uint32_t ret = 0;
+
+  if ( pos[0] < bmin[0] )
+	ret|=FMCS_XMIN;
+  else if ( pos[0] > bmax[0] )
+	ret|=FMCS_XMAX;
+
+  if ( pos[1] < bmin[1] )
+	ret|=FMCS_YMIN;
+  else if ( pos[1] > bmax[1] )
+	ret|=FMCS_YMAX;
+
+  if ( pos[2] < bmin[2] )
+	ret|=FMCS_ZMIN;
+  else if ( pos[2] > bmax[2] )
+	ret|=FMCS_ZMAX;
+
+  return ret;
+}
+
+uint32_t fm_clipTestPointXZ(const REAL *bmin,const REAL *bmax,const REAL *pos) // only tests X and Z, not Y
+{
+  uint32_t ret = 0;
+
+  if ( pos[0] < bmin[0] )
+	ret|=FMCS_XMIN;
+  else if ( pos[0] > bmax[0] )
+	ret|=FMCS_XMAX;
+
+  if ( pos[2] < bmin[2] )
+	ret|=FMCS_ZMIN;
+  else if ( pos[2] > bmax[2] )
+	ret|=FMCS_ZMAX;
+
+  return ret;
+}
+
+uint32_t fm_clipTestAABB(const REAL *bmin,const REAL *bmax,const REAL *p1,const REAL *p2,const REAL *p3,uint32_t &andCode)
+{
+  uint32_t orCode = 0;
+
+  andCode = FMCS_XMIN | FMCS_XMAX | FMCS_YMIN | FMCS_YMAX | FMCS_ZMIN | FMCS_ZMAX;
+
+  uint32_t c = fm_clipTestPoint(bmin,bmax,p1);
+  orCode|=c;
+  andCode&=c;
+
+  c = fm_clipTestPoint(bmin,bmax,p2);
+  orCode|=c;
+  andCode&=c;
+
+  c = fm_clipTestPoint(bmin,bmax,p3);
+  orCode|=c;
+  andCode&=c;
+
+  return orCode;
+}
+
+bool intersect(const REAL *si,const REAL *ei,const REAL *bmin,const REAL *bmax,REAL *time)
+{
+  REAL st,et,fst = 0,fet = 1;
+
+  for (int32_t i = 0; i < 3; i++)
+  {
+	if (*si < *ei)
+	{
+	  if (*si > *bmax || *ei < *bmin)
+		return false;
+	  REAL di = *ei - *si;
+	  st = (*si < *bmin)? (*bmin - *si) / di: 0;
+	  et = (*ei > *bmax)? (*bmax - *si) / di: 1;
+	}
+	else
+	{
+	  if (*ei > *bmax || *si < *bmin)
+		return false;
+	  REAL di = *ei - *si;
+	  st = (*si > *bmax)? (*bmax - *si) / di: 0;
+	  et = (*ei < *bmin)? (*bmin - *si) / di: 1;
+	}
+
+	if (st > fst) fst = st;
+	if (et < fet) fet = et;
+	if (fet < fst)
+	  return false;
+	bmin++; bmax++;
+	si++; ei++;
+  }
+
+  *time = fst;
+  return true;
+}
+
+
+
+bool fm_lineTestAABB(const REAL *p1,const REAL *p2,const REAL *bmin,const REAL *bmax,REAL &time)
+{
+  bool sect = intersect(p1,p2,bmin,bmax,&time);
+  return sect;
+}
+
+
+bool fm_lineTestAABBXZ(const REAL *p1,const REAL *p2,const REAL *bmin,const REAL *bmax,REAL &time)
+{
+  REAL _bmin[3];
+  REAL _bmax[3];
+
+  _bmin[0] = bmin[0];
+  _bmin[1] = -1e9;
+  _bmin[2] = bmin[2];
+
+  _bmax[0] = bmax[0];
+  _bmax[1] = 1e9;
+  _bmax[2] = bmax[2];
+
+  bool sect = intersect(p1,p2,_bmin,_bmax,&time);
+
+  return sect;
+}
+
+void  fm_minmax(const REAL *p,REAL *bmin,REAL *bmax) // accumulate to a min-max value
+{
+
+  if ( p[0] < bmin[0] ) bmin[0] = p[0];
+  if ( p[1] < bmin[1] ) bmin[1] = p[1];
+  if ( p[2] < bmin[2] ) bmin[2] = p[2];
+
+  if ( p[0] > bmax[0] ) bmax[0] = p[0];
+  if ( p[1] > bmax[1] ) bmax[1] = p[1];
+  if ( p[2] > bmax[2] ) bmax[2] = p[2];
+
+}
+
+REAL fm_solveX(const REAL *plane,REAL y,REAL z) // solve for X given this plane equation and the other two components.
+{
+  REAL x = (y*plane[1]+z*plane[2]+plane[3]) / -plane[0];
+  return x;
+}
+
+REAL fm_solveY(const REAL *plane,REAL x,REAL z) // solve for Y given this plane equation and the other two components.
+{
+  REAL y = (x*plane[0]+z*plane[2]+plane[3]) / -plane[1];
+  return y;
+}
+
+
+REAL fm_solveZ(const REAL *plane,REAL x,REAL y) // solve for Y given this plane equation and the other two components.
+{
+  REAL z = (x*plane[0]+y*plane[1]+plane[3]) / -plane[2];
+  return z;
+}
+
+
+void  fm_getAABBCenter(const REAL *bmin,const REAL *bmax,REAL *center)
+{
+  center[0] = (bmax[0]-bmin[0])*0.5f+bmin[0];
+  center[1] = (bmax[1]-bmin[1])*0.5f+bmin[1];
+  center[2] = (bmax[2]-bmin[2])*0.5f+bmin[2];
+}
+
+FM_Axis fm_getDominantAxis(const REAL normal[3])
+{
+  FM_Axis ret = FM_XAXIS;
+
+  REAL x = (REAL)fabs(normal[0]);
+  REAL y = (REAL)fabs(normal[1]);
+  REAL z = (REAL)fabs(normal[2]);
+
+  if ( y > x && y > z )
+	ret = FM_YAXIS;
+  else if ( z > x && z > y )
+	ret = FM_ZAXIS;
+
+  return ret;
+}
+
+
+bool fm_lineSphereIntersect(const REAL *center,REAL radius,const REAL *p1,const REAL *p2,REAL *intersect)
+{
+  bool ret = false;
+
+  REAL dir[3];
+
+  dir[0] = p2[0]-p1[0];
+  dir[1] = p2[1]-p1[1];
+  dir[2] = p2[2]-p1[2];
+
+  REAL distance = (REAL)sqrt( dir[0]*dir[0]+dir[1]*dir[1]+dir[2]*dir[2]);
+
+  if ( distance > 0 )
+  {
+	REAL recip = 1.0f / distance;
+	dir[0]*=recip;
+	dir[1]*=recip;
+	dir[2]*=recip;
+	ret = fm_raySphereIntersect(center,radius,p1,dir,distance,intersect);
+  }
+  else
+  {
+	dir[0] = center[0]-p1[0];
+	dir[1] = center[1]-p1[1];
+	dir[2] = center[2]-p1[2];
+	REAL d2 = dir[0]*dir[0]+dir[1]*dir[1]+dir[2]*dir[2];
+	REAL r2 = radius*radius;
+	if ( d2 < r2 )
+	{
+	  ret = true;
+	  if ( intersect )
+	  {
+		intersect[0] = p1[0];
+		intersect[1] = p1[1];
+		intersect[2] = p1[2];
+	  }
+	}
+  }
+  return ret;
+}
+
+#define DOT(p1,p2) (p1[0]*p2[0]+p1[1]*p2[1]+p1[2]*p2[2])
+
+bool fm_raySphereIntersect(const REAL *center,REAL radius,const REAL *pos,const REAL *dir,REAL distance,REAL *intersect)
+{
+  bool ret = false;
+
+  REAL E0[3];
+
+  E0[0] = center[0] - pos[0];
+  E0[1] = center[1] - pos[1];
+  E0[2] = center[2] - pos[2];
+
+  REAL V[3];
+
+  V[0]  = dir[0];
+  V[1]  = dir[1];
+  V[2]  = dir[2];
+
+
+  REAL dist2   = E0[0]*E0[0] + E0[1]*E0[1] + E0[2] * E0[2];
+  REAL radius2 = radius*radius; // radius squared..
+
+  // Bug Fix For Gem, if origin is *inside* the sphere, invert the
+  // direction vector so that we get a valid intersection location.
+  if ( dist2 < radius2 )
+  {
+	V[0]*=-1;
+	V[1]*=-1;
+	V[2]*=-1;
+  }
+
+
+	REAL v = DOT(E0,V);
+
+	REAL disc = radius2 - (dist2 - v*v);
+
+	if (disc > 0.0f)
+	{
+		if ( intersect )
+		{
+		  REAL d = (REAL)sqrt(disc);
+	  REAL diff = v-d;
+	  if ( diff < distance )
+	  {
+		intersect[0] = pos[0]+V[0]*diff;
+		intersect[1] = pos[1]+V[1]*diff;
+		intersect[2] = pos[2]+V[2]*diff;
+		ret = true;
+	  }
+	}
+	}
+
+	return ret;
+}
+
+
+void fm_catmullRom(REAL *out_vector,const REAL *p1,const REAL *p2,const REAL *p3,const REAL *p4, const REAL s)
+{
+  REAL s_squared = s * s;
+  REAL s_cubed = s_squared * s;
+
+  REAL coefficient_p1 = -s_cubed + 2*s_squared - s;
+  REAL coefficient_p2 = 3 * s_cubed - 5 * s_squared + 2;
+  REAL coefficient_p3 = -3 * s_cubed +4 * s_squared + s;
+  REAL coefficient_p4 = s_cubed - s_squared;
+
+  out_vector[0] = (coefficient_p1 * p1[0] + coefficient_p2 * p2[0] + coefficient_p3 * p3[0] + coefficient_p4 * p4[0])*0.5f;
+  out_vector[1] = (coefficient_p1 * p1[1] + coefficient_p2 * p2[1] + coefficient_p3 * p3[1] + coefficient_p4 * p4[1])*0.5f;
+  out_vector[2] = (coefficient_p1 * p1[2] + coefficient_p2 * p2[2] + coefficient_p3 * p3[2] + coefficient_p4 * p4[2])*0.5f;
+}
+
+bool fm_intersectAABB(const REAL *bmin1,const REAL *bmax1,const REAL *bmin2,const REAL *bmax2)
+{
+  if ((bmin1[0] > bmax2[0]) || (bmin2[0] > bmax1[0])) return false;
+  if ((bmin1[1] > bmax2[1]) || (bmin2[1] > bmax1[1])) return false;
+  if ((bmin1[2] > bmax2[2]) || (bmin2[2] > bmax1[2])) return false;
+  return true;
+
+}
+
+bool  fm_insideAABB(const REAL *obmin,const REAL *obmax,const REAL *tbmin,const REAL *tbmax) // test if bounding box tbmin/tmbax is fully inside obmin/obmax
+{
+  bool ret = false;
+
+  if ( tbmax[0] <= obmax[0] &&
+	   tbmax[1] <= obmax[1] &&
+	   tbmax[2] <= obmax[2] &&
+	   tbmin[0] >= obmin[0] &&
+	   tbmin[1] >= obmin[1] &&
+	   tbmin[2] >= obmin[2] ) ret = true;
+
+  return ret;
+}
+
+
+// Reference, from Stan Melax in Game Gems I
+//  Quaternion q;
+//  vector3 c = CrossProduct(v0,v1);
+//  REAL   d = DotProduct(v0,v1);
+//  REAL   s = (REAL)sqrt((1+d)*2);
+//  q.x = c.x / s;
+//  q.y = c.y / s;
+//  q.z = c.z / s;
+//  q.w = s /2.0f;
+//  return q;
+void fm_rotationArc(const REAL *v0,const REAL *v1,REAL *quat)
+{
+  REAL cross[3];
+
+  fm_cross(cross,v0,v1);
+  REAL d = fm_dot(v0,v1);
+
+  if( d<= -0.99999f ) // 180 about x axis
+  {
+	  if ( fabsf((float)v0[0]) < 0.1f )
+	  {
+		  quat[0] = 0;
+		  quat[1] = v0[2];
+		  quat[2] = -v0[1];
+		  quat[3] = 0;
+	  }
+	  else
+	  {
+		  quat[0] = v0[1];
+		  quat[1] = -v0[0];
+		  quat[2] = 0;
+		  quat[3] = 0;
+	  }
+	  REAL magnitudeSquared = quat[0]*quat[0] + quat[1]*quat[1] + quat[2]*quat[2] + quat[3]*quat[3];
+	  REAL magnitude = sqrtf((float)magnitudeSquared);
+	  REAL recip = 1.0f / magnitude;
+	  quat[0]*=recip;
+	  quat[1]*=recip;
+	  quat[2]*=recip;
+	  quat[3]*=recip;
+  }
+  else
+  {
+	  REAL s = (REAL)sqrt((1+d)*2);
+	  REAL recip = 1.0f / s;
+
+	  quat[0] = cross[0] * recip;
+	  quat[1] = cross[1] * recip;
+	  quat[2] = cross[2] * recip;
+	  quat[3] = s * 0.5f;
+  }
+}
+
+
+REAL fm_distancePointLineSegment(const REAL *Point,const REAL *LineStart,const REAL *LineEnd,REAL *intersection,LineSegmentType &type,REAL epsilon)
+{
+  REAL ret;
+
+  REAL LineMag = fm_distance( LineEnd, LineStart );
+
+  if ( LineMag > 0 )
+  {
+	REAL U = ( ( ( Point[0] - LineStart[0] ) * ( LineEnd[0] - LineStart[0] ) ) + ( ( Point[1] - LineStart[1] ) * ( LineEnd[1] - LineStart[1] ) ) + ( ( Point[2] - LineStart[2] ) * ( LineEnd[2] - LineStart[2] ) ) ) / ( LineMag * LineMag );
+	if( U < 0.0f || U > 1.0f )
+	{
+	  REAL d1 = fm_distanceSquared(Point,LineStart);
+	  REAL d2 = fm_distanceSquared(Point,LineEnd);
+	  if ( d1 <= d2 )
+	  {
+		ret = (REAL)sqrt(d1);
+		intersection[0] = LineStart[0];
+		intersection[1] = LineStart[1];
+		intersection[2] = LineStart[2];
+		type = LS_START;
+	  }
+	  else
+	  {
+		ret = (REAL)sqrt(d2);
+		intersection[0] = LineEnd[0];
+		intersection[1] = LineEnd[1];
+		intersection[2] = LineEnd[2];
+		type = LS_END;
+	  }
+	}
+	else
+	{
+	  intersection[0] = LineStart[0] + U * ( LineEnd[0] - LineStart[0] );
+	  intersection[1] = LineStart[1] + U * ( LineEnd[1] - LineStart[1] );
+	  intersection[2] = LineStart[2] + U * ( LineEnd[2] - LineStart[2] );
+
+	  ret = fm_distance(Point,intersection);
+
+	  REAL d1 = fm_distanceSquared(intersection,LineStart);
+	  REAL d2 = fm_distanceSquared(intersection,LineEnd);
+	  REAL mag = (epsilon*2)*(epsilon*2);
+
+	  if ( d1 < mag ) // if less than 1/100th the total distance, treat is as the 'start'
+	  {
+		type = LS_START;
+	  }
+	  else if ( d2 < mag )
+	  {
+		type = LS_END;
+	  }
+	  else
+	  {
+		type = LS_MIDDLE;
+	  }
+
+	}
+  }
+  else
+  {
+	ret = LineMag;
+	intersection[0] = LineEnd[0];
+	intersection[1] = LineEnd[1];
+	intersection[2] = LineEnd[2];
+	type = LS_END;
+  }
+
+  return ret;
+}
+
+
+#ifndef BEST_FIT_PLANE_H
+
+#define BEST_FIT_PLANE_H
+
+template <class Type> class Eigen
+{
+public:
+
+
+  void DecrSortEigenStuff(void)
+  {
+	Tridiagonal(); //diagonalize the matrix.
+	QLAlgorithm(); //
+	DecreasingSort();
+	GuaranteeRotation();
+  }
+
+  void Tridiagonal(void)
+  {
+	Type fM00 = mElement[0][0];
+	Type fM01 = mElement[0][1];
+	Type fM02 = mElement[0][2];
+	Type fM11 = mElement[1][1];
+	Type fM12 = mElement[1][2];
+	Type fM22 = mElement[2][2];
+
+	m_afDiag[0] = fM00;
+	m_afSubd[2] = 0;
+	if (fM02 != (Type)0.0)
+	{
+	  Type fLength = (REAL)sqrt(fM01*fM01+fM02*fM02);
+	  Type fInvLength = ((Type)1.0)/fLength;
+	  fM01 *= fInvLength;
+	  fM02 *= fInvLength;
+	  Type fQ = ((Type)2.0)*fM01*fM12+fM02*(fM22-fM11);
+	  m_afDiag[1] = fM11+fM02*fQ;
+	  m_afDiag[2] = fM22-fM02*fQ;
+	  m_afSubd[0] = fLength;
+	  m_afSubd[1] = fM12-fM01*fQ;
+	  mElement[0][0] = (Type)1.0;
+	  mElement[0][1] = (Type)0.0;
+	  mElement[0][2] = (Type)0.0;
+	  mElement[1][0] = (Type)0.0;
+	  mElement[1][1] = fM01;
+	  mElement[1][2] = fM02;
+	  mElement[2][0] = (Type)0.0;
+	  mElement[2][1] = fM02;
+	  mElement[2][2] = -fM01;
+	  m_bIsRotation = false;
+	}
+	else
+	{
+	  m_afDiag[1] = fM11;
+	  m_afDiag[2] = fM22;
+	  m_afSubd[0] = fM01;
+	  m_afSubd[1] = fM12;
+	  mElement[0][0] = (Type)1.0;
+	  mElement[0][1] = (Type)0.0;
+	  mElement[0][2] = (Type)0.0;
+	  mElement[1][0] = (Type)0.0;
+	  mElement[1][1] = (Type)1.0;
+	  mElement[1][2] = (Type)0.0;
+	  mElement[2][0] = (Type)0.0;
+	  mElement[2][1] = (Type)0.0;
+	  mElement[2][2] = (Type)1.0;
+	  m_bIsRotation = true;
+	}
+  }
+
+  bool QLAlgorithm(void)
+  {
+	const int32_t iMaxIter = 32;
+
+	for (int32_t i0 = 0; i0 <3; i0++)
+	{
+	  int32_t i1;
+	  for (i1 = 0; i1 < iMaxIter; i1++)
+	  {
+		int32_t i2;
+		for (i2 = i0; i2 <= (3-2); i2++)
+		{
+		  Type fTmp = Type(fabs(m_afDiag[i2]) + fabs(m_afDiag[i2+1]));
+		  if ( fabs(m_afSubd[i2]) + fTmp == fTmp )
+			break;
+		}
+		if (i2 == i0)
+		{
+		  break;
+		}
+
+		Type fG = (m_afDiag[i0+1] - m_afDiag[i0])/(((Type)2.0) * m_afSubd[i0]);
+		Type fR = (REAL)sqrt(fG*fG+(Type)1.0);
+		if (fG < (Type)0.0)
+		{
+		  fG = m_afDiag[i2]-m_afDiag[i0]+m_afSubd[i0]/(fG-fR);
+		}
+		else
+		{
+		  fG = m_afDiag[i2]-m_afDiag[i0]+m_afSubd[i0]/(fG+fR);
+		}
+		Type fSin = (Type)1.0, fCos = (Type)1.0, fP = (Type)0.0;
+		for (int32_t i3 = i2-1; i3 >= i0; i3--)
+		{
+		  Type fF = fSin*m_afSubd[i3];
+		  Type fB = fCos*m_afSubd[i3];
+		  if (fabs(fF) >= fabs(fG))
+		  {
+			fCos = fG/fF;
+			fR = (REAL)sqrt(fCos*fCos+(Type)1.0);
+			m_afSubd[i3+1] = fF*fR;
+			fSin = ((Type)1.0)/fR;
+			fCos *= fSin;
+		  }
+		  else
+		  {
+			fSin = fF/fG;
+			fR = (REAL)sqrt(fSin*fSin+(Type)1.0);
+			m_afSubd[i3+1] = fG*fR;
+			fCos = ((Type)1.0)/fR;
+			fSin *= fCos;
+		  }
+		  fG = m_afDiag[i3+1]-fP;
+		  fR = (m_afDiag[i3]-fG)*fSin+((Type)2.0)*fB*fCos;
+		  fP = fSin*fR;
+		  m_afDiag[i3+1] = fG+fP;
+		  fG = fCos*fR-fB;
+		  for (int32_t i4 = 0; i4 < 3; i4++)
+		  {
+			fF = mElement[i4][i3+1];
+			mElement[i4][i3+1] = fSin*mElement[i4][i3]+fCos*fF;
+			mElement[i4][i3] = fCos*mElement[i4][i3]-fSin*fF;
+		  }
+		}
+		m_afDiag[i0] -= fP;
+		m_afSubd[i0] = fG;
+		m_afSubd[i2] = (Type)0.0;
+	  }
+	  if (i1 == iMaxIter)
+	  {
+		return false;
+	  }
+	}
+	return true;
+  }
+
+  void DecreasingSort(void)
+  {
+	//sort eigenvalues in decreasing order, e[0] >= ... >= e[iSize-1]
+	for (int32_t i0 = 0, i1; i0 <= 3-2; i0++)
+	{
+	  // locate maximum eigenvalue
+	  i1 = i0;
+	  Type fMax = m_afDiag[i1];
+	  int32_t i2;
+	  for (i2 = i0+1; i2 < 3; i2++)
+	  {
+		if (m_afDiag[i2] > fMax)
+		{
+		  i1 = i2;
+		  fMax = m_afDiag[i1];
+		}
+	  }
+
+	  if (i1 != i0)
+	  {
+		// swap eigenvalues
+		m_afDiag[i1] = m_afDiag[i0];
+		m_afDiag[i0] = fMax;
+		// swap eigenvectors
+		for (i2 = 0; i2 < 3; i2++)
+		{
+		  Type fTmp = mElement[i2][i0];
+		  mElement[i2][i0] = mElement[i2][i1];
+		  mElement[i2][i1] = fTmp;
+		  m_bIsRotation = !m_bIsRotation;
+		}
+	  }
+	}
+  }
+
+
+  void GuaranteeRotation(void)
+  {
+	if (!m_bIsRotation)
+	{
+	  // change sign on the first column
+	  for (int32_t iRow = 0; iRow <3; iRow++)
+	  {
+		mElement[iRow][0] = -mElement[iRow][0];
+	  }
+	}
+  }
+
+  Type mElement[3][3];
+  Type m_afDiag[3];
+  Type m_afSubd[3];
+  bool m_bIsRotation;
+};
+
+#endif
+
+bool fm_computeBestFitPlane(uint32_t vcount,
+					 const REAL *points,
+					 uint32_t vstride,
+					 const REAL *weights,
+					 uint32_t wstride,
+					 REAL *plane,
+					REAL *center)
+{
+  bool ret = false;
+
+  REAL kOrigin[3] = { 0, 0, 0 };
+
+  REAL wtotal = 0;
+
+  {
+	const char *source  = (const char *) points;
+	const char *wsource = (const char *) weights;
+
+	for (uint32_t i=0; i<vcount; i++)
+	{
+
+	  const REAL *p = (const REAL *) source;
+
+	  REAL w = 1;
+
+	  if ( wsource )
+	  {
+		const REAL *ws = (const REAL *) wsource;
+		w = *ws; //
+		wsource+=wstride;
+	  }
+
+	  kOrigin[0]+=p[0]*w;
+	  kOrigin[1]+=p[1]*w;
+	  kOrigin[2]+=p[2]*w;
+
+	  wtotal+=w;
+
+	  source+=vstride;
+	}
+  }
+
+  REAL recip = 1.0f / wtotal; // reciprocal of total weighting
+
+  kOrigin[0]*=recip;
+  kOrigin[1]*=recip;
+  kOrigin[2]*=recip;
+
+  center[0] = kOrigin[0];
+  center[1] = kOrigin[1];
+  center[2] = kOrigin[2];
+
+
+  REAL fSumXX=0;
+  REAL fSumXY=0;
+  REAL fSumXZ=0;
+
+  REAL fSumYY=0;
+  REAL fSumYZ=0;
+  REAL fSumZZ=0;
+
+
+  {
+	const char *source  = (const char *) points;
+	const char *wsource = (const char *) weights;
+
+	for (uint32_t i=0; i<vcount; i++)
+	{
+
+	  const REAL *p = (const REAL *) source;
+
+	  REAL w = 1;
+
+	  if ( wsource )
+	  {
+		const REAL *ws = (const REAL *) wsource;
+		w = *ws; //
+		wsource+=wstride;
+	  }
+
+	  REAL kDiff[3];
+
+	  kDiff[0] = w*(p[0] - kOrigin[0]); // apply vertex weighting!
+	  kDiff[1] = w*(p[1] - kOrigin[1]);
+	  kDiff[2] = w*(p[2] - kOrigin[2]);
+
+	  fSumXX+= kDiff[0] * kDiff[0]; // sum of the squares of the differences.
+	  fSumXY+= kDiff[0] * kDiff[1]; // sum of the squares of the differences.
+	  fSumXZ+= kDiff[0] * kDiff[2]; // sum of the squares of the differences.
+
+	  fSumYY+= kDiff[1] * kDiff[1];
+	  fSumYZ+= kDiff[1] * kDiff[2];
+	  fSumZZ+= kDiff[2] * kDiff[2];
+
+
+	  source+=vstride;
+	}
+  }
+
+  fSumXX *= recip;
+  fSumXY *= recip;
+  fSumXZ *= recip;
+  fSumYY *= recip;
+  fSumYZ *= recip;
+  fSumZZ *= recip;
+
+  // setup the eigensolver
+  Eigen<REAL> kES;
+
+  kES.mElement[0][0] = fSumXX;
+  kES.mElement[0][1] = fSumXY;
+  kES.mElement[0][2] = fSumXZ;
+
+  kES.mElement[1][0] = fSumXY;
+  kES.mElement[1][1] = fSumYY;
+  kES.mElement[1][2] = fSumYZ;
+
+  kES.mElement[2][0] = fSumXZ;
+  kES.mElement[2][1] = fSumYZ;
+  kES.mElement[2][2] = fSumZZ;
+
+  // compute eigenstuff, smallest eigenvalue is in last position
+  kES.DecrSortEigenStuff();
+
+  REAL kNormal[3];
+
+  kNormal[0] = kES.mElement[0][2];
+  kNormal[1] = kES.mElement[1][2];
+  kNormal[2] = kES.mElement[2][2];
+
+  // the minimum energy
+  plane[0] = kNormal[0];
+  plane[1] = kNormal[1];
+  plane[2] = kNormal[2];
+
+  plane[3] = 0 - fm_dot(kNormal,kOrigin);
+
+  ret = true;
+
+  return ret;
+}
+
+
+bool fm_colinear(const REAL a1[3],const REAL a2[3],const REAL b1[3],const REAL b2[3],REAL epsilon)  // true if these two line segments are co-linear.
+{
+  bool ret = false;
+
+  REAL dir1[3];
+  REAL dir2[3];
+
+  dir1[0] = (a2[0] - a1[0]);
+  dir1[1] = (a2[1] - a1[1]);
+  dir1[2] = (a2[2] - a1[2]);
+
+  dir2[0] = (b2[0]-a1[0]) - (b1[0]-a1[0]);
+  dir2[1] = (b2[1]-a1[1]) - (b1[1]-a1[1]);
+  dir2[2] = (b2[2]-a2[2]) - (b1[2]-a2[2]);
+
+  fm_normalize(dir1);
+  fm_normalize(dir2);
+
+  REAL dot = fm_dot(dir1,dir2);
+
+  if ( dot >= epsilon )
+  {
+	ret = true;
+  }
+
+
+  return ret;
+}
+
+bool fm_colinear(const REAL *p1,const REAL *p2,const REAL *p3,REAL epsilon)
+{
+  bool ret = false;
+
+  REAL dir1[3];
+  REAL dir2[3];
+
+  dir1[0] = p2[0] - p1[0];
+  dir1[1] = p2[1] - p1[1];
+  dir1[2] = p2[2] - p1[2];
+
+  dir2[0] = p3[0] - p2[0];
+  dir2[1] = p3[1] - p2[1];
+  dir2[2] = p3[2] - p2[2];
+
+  fm_normalize(dir1);
+  fm_normalize(dir2);
+
+  REAL dot = fm_dot(dir1,dir2);
+
+  if ( dot >= epsilon )
+  {
+	ret = true;
+  }
+
+
+  return ret;
+}
+
+void  fm_initMinMax(const REAL *p,REAL *bmin,REAL *bmax)
+{
+  bmax[0] = bmin[0] = p[0];
+  bmax[1] = bmin[1] = p[1];
+  bmax[2] = bmin[2] = p[2];
+}
+
+IntersectResult fm_intersectLineSegments2d(const REAL *a1,const REAL *a2,const REAL *b1,const REAL *b2,REAL *intersection)
+{
+  IntersectResult ret;
+
+  REAL denom  = ((b2[1] - b1[1])*(a2[0] - a1[0])) - ((b2[0] - b1[0])*(a2[1] - a1[1]));
+  REAL nume_a = ((b2[0] - b1[0])*(a1[1] - b1[1])) - ((b2[1] - b1[1])*(a1[0] - b1[0]));
+  REAL nume_b = ((a2[0] - a1[0])*(a1[1] - b1[1])) - ((a2[1] - a1[1])*(a1[0] - b1[0]));
+  if (denom == 0 )
+  {
+	if(nume_a == 0 && nume_b == 0)
+	{
+	  ret = IR_COINCIDENT;
+	}
+	else
+	{
+	  ret = IR_PARALLEL;
+	}
+  }
+  else
+  {
+
+	REAL recip = 1 / denom;
+	REAL ua = nume_a * recip;
+	REAL ub = nume_b * recip;
+
+	if(ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1 )
+	{
+	  // Get the intersection point.
+	  intersection[0] = a1[0] + ua*(a2[0] - a1[0]);
+	  intersection[1] = a1[1] + ua*(a2[1] - a1[1]);
+	  ret = IR_DO_INTERSECT;
+	}
+	else
+	{
+	  ret = IR_DONT_INTERSECT;
+	}
+  }
+  return ret;
+}
+
+IntersectResult fm_intersectLineSegments2dTime(const REAL *a1,const REAL *a2,const REAL *b1,const REAL *b2,REAL &t1,REAL &t2)
+{
+  IntersectResult ret;
+
+  REAL denom  = ((b2[1] - b1[1])*(a2[0] - a1[0])) - ((b2[0] - b1[0])*(a2[1] - a1[1]));
+  REAL nume_a = ((b2[0] - b1[0])*(a1[1] - b1[1])) - ((b2[1] - b1[1])*(a1[0] - b1[0]));
+  REAL nume_b = ((a2[0] - a1[0])*(a1[1] - b1[1])) - ((a2[1] - a1[1])*(a1[0] - b1[0]));
+  if (denom == 0 )
+  {
+	if(nume_a == 0 && nume_b == 0)
+	{
+	  ret = IR_COINCIDENT;
+	}
+	else
+	{
+	  ret = IR_PARALLEL;
+	}
+  }
+  else
+  {
+
+	REAL recip = 1 / denom;
+	REAL ua = nume_a * recip;
+	REAL ub = nume_b * recip;
+
+	if(ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1 )
+	{
+	  t1 = ua;
+	  t2 = ub;
+	  ret = IR_DO_INTERSECT;
+	}
+	else
+	{
+	  ret = IR_DONT_INTERSECT;
+	}
+  }
+  return ret;
+}
+
+//**** Plane Triangle Intersection
+
+
+
+
+
+// assumes that the points are on opposite sides of the plane!
+bool fm_intersectPointPlane(const REAL *p1,const REAL *p2,REAL *split,const REAL *plane)
+{
+
+  REAL dp1 = fm_distToPlane(plane,p1);
+  REAL dp2 = fm_distToPlane(plane, p2);
+  if (dp1 <= 0 && dp2 <= 0)
+  {
+	  return false;
+  }
+  if (dp1 >= 0 && dp2 >= 0)
+  {
+	  return false;
+  }
+
+  REAL dir[3];
+
+  dir[0] = p2[0] - p1[0];
+  dir[1] = p2[1] - p1[1];
+  dir[2] = p2[2] - p1[2];
+
+  REAL dot1 = dir[0]*plane[0] + dir[1]*plane[1] + dir[2]*plane[2];
+  REAL dot2 = dp1 - plane[3];
+
+  REAL    t = -(plane[3] + dot2 ) / dot1;
+
+  split[0] = (dir[0]*t)+p1[0];
+  split[1] = (dir[1]*t)+p1[1];
+  split[2] = (dir[2]*t)+p1[2];
+
+  return true;
+}
+
+PlaneTriResult fm_getSidePlane(const REAL *p,const REAL *plane,REAL epsilon)
+{
+  PlaneTriResult ret = PTR_ON_PLANE;
+
+  REAL d = fm_distToPlane(plane,p);
+
+  if ( d < -epsilon || d > epsilon )
+  {
+	if ( d > 0 )
+		ret =  PTR_FRONT; // it is 'in front' within the provided epsilon value.
+	else
+	  ret = PTR_BACK;
+  }
+
+  return ret;
+}
+
+
+
+#ifndef PLANE_TRIANGLE_INTERSECTION_H
+
+#define PLANE_TRIANGLE_INTERSECTION_H
+
+#define MAXPTS 256
+
+template <class Type> class point
+{
+public:
+
+  void set(const Type *p)
+  {
+	x = p[0];
+	y = p[1];
+	z = p[2];
+  }
+
+  Type x;
+  Type y;
+  Type z;
+};
+
+template <class Type> class plane
+{
+public:
+  plane(const Type *p)
+  {
+	normal.x = p[0];
+	normal.y = p[1];
+	normal.z = p[2];
+	D        = p[3];
+  }
+
+  Type Classify_Point(const point<Type> &p)
+  {
+	return p.x*normal.x + p.y*normal.y + p.z*normal.z + D;
+  }
+
+  point<Type> normal;
+  Type  D;
+};
+
+template <class Type> class polygon
+{
+public:
+  polygon(void)
+  {
+	mVcount = 0;
+  }
+
+  polygon(const Type *p1,const Type *p2,const Type *p3)
+  {
+	mVcount = 3;
+	mVertices[0].set(p1);
+	mVertices[1].set(p2);
+	mVertices[2].set(p3);
+  }
+
+
+  int32_t NumVertices(void) const { return mVcount; };
+
+  const point<Type>& Vertex(int32_t index)
+  {
+	if ( index < 0 ) index+=mVcount;
+	return mVertices[index];
+  };
+
+
+  void set(const point<Type> *pts,int32_t count)
+  {
+	for (int32_t i=0; i<count; i++)
+	{
+	  mVertices[i] = pts[i];
+	}
+	mVcount = count;
+  }
+
+
+  void Split_Polygon(polygon<Type> *poly,plane<Type> *part, polygon<Type> &front, polygon<Type> &back)
+  {
+	int32_t   count = poly->NumVertices ();
+	int32_t   out_c = 0, in_c = 0;
+	point<Type> ptA, ptB,outpts[MAXPTS],inpts[MAXPTS];
+	Type sideA, sideB;
+	ptA = poly->Vertex (count - 1);
+	sideA = part->Classify_Point (ptA);
+	for (int32_t i = -1; ++i < count;)
+	{
+	  ptB = poly->Vertex(i);
+	  sideB = part->Classify_Point(ptB);
+	  if (sideB > 0)
+	  {
+		if (sideA < 0)
+		{
+			  point<Type> v;
+		  fm_intersectPointPlane(&ptB.x, &ptA.x, &v.x, &part->normal.x );
+		  outpts[out_c++] = inpts[in_c++] = v;
+		}
+		outpts[out_c++] = ptB;
+	  }
+	  else if (sideB < 0)
+	  {
+		if (sideA > 0)
+		{
+		  point<Type> v;
+		  fm_intersectPointPlane(&ptB.x, &ptA.x, &v.x, &part->normal.x );
+		  outpts[out_c++] = inpts[in_c++] = v;
+		}
+		inpts[in_c++] = ptB;
+	  }
+	  else
+		 outpts[out_c++] = inpts[in_c++] = ptB;
+	  ptA = ptB;
+	  sideA = sideB;
+	}
+
+	front.set(&outpts[0], out_c);
+	back.set(&inpts[0], in_c);
+  }
+
+  int32_t           mVcount;
+  point<Type>   mVertices[MAXPTS];
+};
+
+
+
+#endif
+
+static inline void add(const REAL *p,REAL *dest,uint32_t tstride,uint32_t &pcount)
+{
+  char *d = (char *) dest;
+  d = d + pcount*tstride;
+  dest = (REAL *) d;
+  dest[0] = p[0];
+  dest[1] = p[1];
+  dest[2] = p[2];
+  pcount++;
+	assert( pcount <= 4 );
+}
+
+
+PlaneTriResult fm_planeTriIntersection(const REAL *_plane,    // the plane equation in Ax+By+Cz+D format
+									const REAL *triangle, // the source triangle.
+									uint32_t tstride,  // stride in bytes of the input and output *vertices*
+									REAL        epsilon,  // the co-planar epsilon value.
+									REAL       *front,    // the triangle in front of the
+									uint32_t &fcount,  // number of vertices in the 'front' triangle
+									REAL       *back,     // the triangle in back of the plane
+									uint32_t &bcount) // the number of vertices in the 'back' triangle.
+{
+
+  fcount = 0;
+  bcount = 0;
+
+  const char *tsource = (const char *) triangle;
+
+  // get the three vertices of the triangle.
+  const REAL *p1     = (const REAL *) (tsource);
+  const REAL *p2     = (const REAL *) (tsource+tstride);
+  const REAL *p3     = (const REAL *) (tsource+tstride*2);
+
+
+  PlaneTriResult r1   = fm_getSidePlane(p1,_plane,epsilon); // compute the side of the plane each vertex is on
+  PlaneTriResult r2   = fm_getSidePlane(p2,_plane,epsilon);
+  PlaneTriResult r3   = fm_getSidePlane(p3,_plane,epsilon);
+
+  // If any of the points lay right *on* the plane....
+  if ( r1 == PTR_ON_PLANE || r2 == PTR_ON_PLANE || r3 == PTR_ON_PLANE )
+  {
+	// If the triangle is completely co-planar, then just treat it as 'front' and return!
+	if ( r1 == PTR_ON_PLANE && r2 == PTR_ON_PLANE && r3 == PTR_ON_PLANE )
+	{
+	  add(p1,front,tstride,fcount);
+	  add(p2,front,tstride,fcount);
+	  add(p3,front,tstride,fcount);
+	  return PTR_FRONT;
+	}
+	// Decide to place the co-planar points on the same side as the co-planar point.
+	PlaneTriResult r= PTR_ON_PLANE;
+	if ( r1 != PTR_ON_PLANE )
+	  r = r1;
+	else if ( r2 != PTR_ON_PLANE )
+	  r = r2;
+	else if ( r3 != PTR_ON_PLANE )
+	  r = r3;
+
+	if ( r1 == PTR_ON_PLANE ) r1 = r;
+	if ( r2 == PTR_ON_PLANE ) r2 = r;
+	if ( r3 == PTR_ON_PLANE ) r3 = r;
+
+  }
+
+  if ( r1 == r2 && r1 == r3 ) // if all three vertices are on the same side of the plane.
+  {
+	if ( r1 == PTR_FRONT ) // if all three are in front of the plane, then copy to the 'front' output triangle.
+	{
+	  add(p1,front,tstride,fcount);
+	  add(p2,front,tstride,fcount);
+	  add(p3,front,tstride,fcount);
+	}
+	else
+	{
+	  add(p1,back,tstride,bcount); // if all three are in 'back' then copy to the 'back' output triangle.
+	  add(p2,back,tstride,bcount);
+	  add(p3,back,tstride,bcount);
+	}
+	return r1; // if all three points are on the same side of the plane return result
+  }
+
+
+  polygon<REAL> pi(p1,p2,p3);
+  polygon<REAL>  pfront,pback;
+
+  plane<REAL>    part(_plane);
+
+  pi.Split_Polygon(&pi,&part,pfront,pback);
+
+  for (int32_t i=0; i<pfront.mVcount; i++)
+  {
+	add( &pfront.mVertices[i].x, front, tstride, fcount );
+  }
+
+  for (int32_t i=0; i<pback.mVcount; i++)
+  {
+	add( &pback.mVertices[i].x, back, tstride, bcount );
+  }
+
+  PlaneTriResult ret = PTR_SPLIT;
+
+  if ( fcount < 3 ) fcount = 0;
+  if ( bcount < 3 ) bcount = 0;
+
+  if ( fcount == 0 && bcount )
+	ret = PTR_BACK;
+
+  if ( bcount == 0 && fcount )
+	ret = PTR_FRONT;
+
+
+  return ret;
+}
+
+// computes the OBB for this set of points relative to this transform matrix.
+void computeOBB(uint32_t vcount,const REAL *points,uint32_t pstride,REAL *sides,REAL *matrix)
+{
+  const char *src = (const char *) points;
+
+  REAL bmin[3] = { 1e9, 1e9, 1e9 };
+  REAL bmax[3] = { -1e9, -1e9, -1e9 };
+
+  for (uint32_t i=0; i<vcount; i++)
+  {
+	const REAL *p = (const REAL *) src;
+	REAL t[3];
+
+	fm_inverseRT(matrix, p, t ); // inverse rotate translate
+
+	if ( t[0] < bmin[0] ) bmin[0] = t[0];
+	if ( t[1] < bmin[1] ) bmin[1] = t[1];
+	if ( t[2] < bmin[2] ) bmin[2] = t[2];
+
+	if ( t[0] > bmax[0] ) bmax[0] = t[0];
+	if ( t[1] > bmax[1] ) bmax[1] = t[1];
+	if ( t[2] > bmax[2] ) bmax[2] = t[2];
+
+	src+=pstride;
+  }
+
+  REAL center[3];
+
+  sides[0] = bmax[0]-bmin[0];
+  sides[1] = bmax[1]-bmin[1];
+  sides[2] = bmax[2]-bmin[2];
+
+  center[0] = sides[0]*0.5f+bmin[0];
+  center[1] = sides[1]*0.5f+bmin[1];
+  center[2] = sides[2]*0.5f+bmin[2];
+
+  REAL ocenter[3];
+
+  fm_rotate(matrix,center,ocenter);
+
+  matrix[12]+=ocenter[0];
+  matrix[13]+=ocenter[1];
+  matrix[14]+=ocenter[2];
+
+}
+
+void fm_computeBestFitOBB(uint32_t vcount,const REAL *points,uint32_t pstride,REAL *sides,REAL *matrix,bool bruteForce)
+{
+  REAL plane[4];
+  REAL center[3];
+  fm_computeBestFitPlane(vcount,points,pstride,0,0,plane,center);
+  fm_planeToMatrix(plane,matrix);
+  computeOBB( vcount, points, pstride, sides, matrix );
+
+  REAL refmatrix[16];
+  memcpy(refmatrix,matrix,16*sizeof(REAL));
+
+  REAL volume = sides[0]*sides[1]*sides[2];
+  if ( bruteForce )
+  {
+	for (REAL a=10; a<180; a+=10)
+	{
+	  REAL quat[4];
+	  fm_eulerToQuat(0,a*FM_DEG_TO_RAD,0,quat);
+	  REAL temp[16];
+	  REAL pmatrix[16];
+	  fm_quatToMatrix(quat,temp);
+	  fm_matrixMultiply(temp,refmatrix,pmatrix);
+	  REAL psides[3];
+	  computeOBB( vcount, points, pstride, psides, pmatrix );
+	  REAL v = psides[0]*psides[1]*psides[2];
+	  if ( v < volume )
+	  {
+		volume = v;
+		memcpy(matrix,pmatrix,sizeof(REAL)*16);
+		sides[0] = psides[0];
+		sides[1] = psides[1];
+		sides[2] = psides[2];
+	  }
+	}
+  }
+}
+
+void fm_computeBestFitOBB(uint32_t vcount,const REAL *points,uint32_t pstride,REAL *sides,REAL *pos,REAL *quat,bool bruteForce)
+{
+  REAL matrix[16];
+  fm_computeBestFitOBB(vcount,points,pstride,sides,matrix,bruteForce);
+  fm_getTranslation(matrix,pos);
+  fm_matrixToQuat(matrix,quat);
+}
+
+void fm_computeBestFitABB(uint32_t vcount,const REAL *points,uint32_t pstride,REAL *sides,REAL *pos)
+{
+	REAL bmin[3];
+	REAL bmax[3];
+
+  bmin[0] = points[0];
+  bmin[1] = points[1];
+  bmin[2] = points[2];
+
+  bmax[0] = points[0];
+  bmax[1] = points[1];
+  bmax[2] = points[2];
+
+	const char *cp = (const char *) points;
+	for (uint32_t i=0; i<vcount; i++)
+	{
+		const REAL *p = (const REAL *) cp;
+
+		if ( p[0] < bmin[0] ) bmin[0] = p[0];
+		if ( p[1] < bmin[1] ) bmin[1] = p[1];
+		if ( p[2] < bmin[2] ) bmin[2] = p[2];
+
+	if ( p[0] > bmax[0] ) bmax[0] = p[0];
+	if ( p[1] > bmax[1] ) bmax[1] = p[1];
+	if ( p[2] > bmax[2] ) bmax[2] = p[2];
+
+	cp+=pstride;
+	}
+
+
+	sides[0] = bmax[0] - bmin[0];
+	sides[1] = bmax[1] - bmin[1];
+	sides[2] = bmax[2] - bmin[2];
+
+	pos[0] = bmin[0]+sides[0]*0.5f;
+	pos[1] = bmin[1]+sides[1]*0.5f;
+	pos[2] = bmin[2]+sides[2]*0.5f;
+
+}
+
+
+void fm_planeToMatrix(const REAL *plane,REAL *matrix) // convert a plane equation to a 4x4 rotation matrix
+{
+  REAL ref[3] = { 0, 1, 0 };
+  REAL quat[4];
+  fm_rotationArc(ref,plane,quat);
+  fm_quatToMatrix(quat,matrix);
+  REAL origin[3] = { 0, -plane[3], 0 };
+  REAL center[3];
+  fm_transform(matrix,origin,center);
+  fm_setTranslation(center,matrix);
+}
+
+void fm_planeToQuat(const REAL *plane,REAL *quat,REAL *pos) // convert a plane equation to a quaternion and translation
+{
+  REAL ref[3] = { 0, 1, 0 };
+  REAL matrix[16];
+  fm_rotationArc(ref,plane,quat);
+  fm_quatToMatrix(quat,matrix);
+  REAL origin[3] = { 0, plane[3], 0 };
+  fm_transform(matrix,origin,pos);
+}
+
+void fm_eulerMatrix(REAL ax,REAL ay,REAL az,REAL *matrix) // convert euler (in radians) to a dest 4x4 matrix (translation set to zero)
+{
+  REAL quat[4];
+  fm_eulerToQuat(ax,ay,az,quat);
+  fm_quatToMatrix(quat,matrix);
+}
+
+
+//**********************************************************
+//**********************************************************
+//**** Vertex Welding
+//**********************************************************
+//**********************************************************
+
+#ifndef VERTEX_INDEX_H
+
+#define VERTEX_INDEX_H
+
+namespace VERTEX_INDEX
+{
+
+class KdTreeNode;
+
+typedef std::vector< KdTreeNode * > KdTreeNodeVector;
+
+enum Axes
+{
+  X_AXIS = 0,
+  Y_AXIS = 1,
+  Z_AXIS = 2
+};
+
+class KdTreeFindNode
+{
+public:
+  KdTreeFindNode(void)
+  {
+	mNode = 0;
+	mDistance = 0;
+  }
+  KdTreeNode  *mNode;
+  double        mDistance;
+};
+
+class KdTreeInterface
+{
+public:
+  virtual const double * getPositionDouble(uint32_t index) const = 0;
+  virtual const float  * getPositionFloat(uint32_t index) const = 0;
+};
+
+class KdTreeNode
+{
+public:
+  KdTreeNode(void)
+  {
+	mIndex = 0;
+	mLeft = 0;
+	mRight = 0;
+  }
+
+  KdTreeNode(uint32_t index)
+  {
+	mIndex = index;
+	mLeft = 0;
+	mRight = 0;
+  };
+
+	~KdTreeNode(void)
+  {
+  }
+
+
+  void addDouble(KdTreeNode *node,Axes dim,const KdTreeInterface *iface)
+  {
+	const double *nodePosition = iface->getPositionDouble( node->mIndex );
+	const double *position     = iface->getPositionDouble( mIndex );
+	switch ( dim )
+	{
+	  case X_AXIS:
+		if ( nodePosition[0] <= position[0] )
+		{
+		  if ( mLeft )
+			mLeft->addDouble(node,Y_AXIS,iface);
+		  else
+			mLeft = node;
+		}
+		else
+		{
+		  if ( mRight )
+			mRight->addDouble(node,Y_AXIS,iface);
+		  else
+			mRight = node;
+		}
+		break;
+	  case Y_AXIS:
+		if ( nodePosition[1] <= position[1] )
+		{
+		  if ( mLeft )
+			mLeft->addDouble(node,Z_AXIS,iface);
+		  else
+			mLeft = node;
+		}
+		else
+		{
+		  if ( mRight )
+			mRight->addDouble(node,Z_AXIS,iface);
+		  else
+			mRight = node;
+		}
+		break;
+	  case Z_AXIS:
+		if ( nodePosition[2] <= position[2] )
+		{
+		  if ( mLeft )
+			mLeft->addDouble(node,X_AXIS,iface);
+		  else
+			mLeft = node;
+		}
+		else
+		{
+		  if ( mRight )
+			mRight->addDouble(node,X_AXIS,iface);
+		  else
+			mRight = node;
+		}
+		break;
+	}
+
+  }
+
+
+  void addFloat(KdTreeNode *node,Axes dim,const KdTreeInterface *iface)
+  {
+	const float *nodePosition = iface->getPositionFloat( node->mIndex );
+	const float *position     = iface->getPositionFloat( mIndex );
+	switch ( dim )
+	{
+	  case X_AXIS:
+		if ( nodePosition[0] <= position[0] )
+		{
+		  if ( mLeft )
+			mLeft->addFloat(node,Y_AXIS,iface);
+		  else
+			mLeft = node;
+		}
+		else
+		{
+		  if ( mRight )
+			mRight->addFloat(node,Y_AXIS,iface);
+		  else
+			mRight = node;
+		}
+		break;
+	  case Y_AXIS:
+		if ( nodePosition[1] <= position[1] )
+		{
+		  if ( mLeft )
+			mLeft->addFloat(node,Z_AXIS,iface);
+		  else
+			mLeft = node;
+		}
+		else
+		{
+		  if ( mRight )
+			mRight->addFloat(node,Z_AXIS,iface);
+		  else
+			mRight = node;
+		}
+		break;
+	  case Z_AXIS:
+		if ( nodePosition[2] <= position[2] )
+		{
+		  if ( mLeft )
+			mLeft->addFloat(node,X_AXIS,iface);
+		  else
+			mLeft = node;
+		}
+		else
+		{
+		  if ( mRight )
+			mRight->addFloat(node,X_AXIS,iface);
+		  else
+			mRight = node;
+		}
+		break;
+	}
+
+  }
+
+
+  uint32_t getIndex(void) const { return mIndex; };
+
+  void search(Axes axis,const double *pos,double radius,uint32_t &count,uint32_t maxObjects,KdTreeFindNode *found,const KdTreeInterface *iface)
+  {
+
+	const double *position = iface->getPositionDouble(mIndex);
+
+	double dx = pos[0] - position[0];
+	double dy = pos[1] - position[1];
+	double dz = pos[2] - position[2];
+
+	KdTreeNode *search1 = 0;
+	KdTreeNode *search2 = 0;
+
+	switch ( axis )
+	{
+	  case X_AXIS:
+	   if ( dx <= 0 )     // JWR  if we are to the left
+	   {
+		search1 = mLeft; // JWR  then search to the left
+		if ( -dx < radius )  // JWR  if distance to the right is less than our search radius, continue on the right as well.
+		  search2 = mRight;
+	   }
+	   else
+	   {
+		 search1 = mRight; // JWR  ok, we go down the left tree
+		 if ( dx < radius ) // JWR  if the distance from the right is less than our search radius
+				search2 = mLeft;
+		}
+		axis = Y_AXIS;
+		break;
+	  case Y_AXIS:
+		if ( dy <= 0 )
+		{
+		  search1 = mLeft;
+		  if ( -dy < radius )
+					search2 = mRight;
+		}
+		else
+		{
+		  search1 = mRight;
+		  if ( dy < radius )
+					search2 = mLeft;
+		}
+		axis = Z_AXIS;
+		break;
+	  case Z_AXIS:
+		if ( dz <= 0 )
+		{
+		  search1 = mLeft;
+		  if ( -dz < radius )
+					search2 = mRight;
+		}
+		else
+		{
+		  search1 = mRight;
+		  if ( dz < radius )
+					search2 = mLeft;
+		}
+		axis = X_AXIS;
+		break;
+	}
+
+	double r2 = radius*radius;
+	double m  = dx*dx+dy*dy+dz*dz;
+
+	if ( m < r2 )
+	{
+	  switch ( count )
+	  {
+		case 0:
+		  found[count].mNode = this;
+		  found[count].mDistance = m;
+		  break;
+		case 1:
+		  if ( m < found[0].mDistance )
+		  {
+			if ( maxObjects == 1 )
+			{
+			  found[0].mNode = this;
+			  found[0].mDistance = m;
+			}
+			else
+			{
+			  found[1] = found[0];
+			  found[0].mNode = this;
+			  found[0].mDistance = m;
+			}
+		  }
+		  else if ( maxObjects > 1)
+		  {
+			found[1].mNode = this;
+			found[1].mDistance = m;
+		  }
+		  break;
+		default:
+		  {
+			bool inserted = false;
+
+			for (uint32_t i=0; i<count; i++)
+			{
+			  if ( m < found[i].mDistance ) // if this one is closer than a pre-existing one...
+			  {
+				// insertion sort...
+				uint32_t scan = count;
+				if ( scan >= maxObjects ) scan=maxObjects-1;
+				for (uint32_t j=scan; j>i; j--)
+				{
+				  found[j] = found[j-1];
+				}
+				found[i].mNode = this;
+				found[i].mDistance = m;
+				inserted = true;
+				break;
+			  }
+			}
+
+			if ( !inserted && count < maxObjects )
+			{
+			  found[count].mNode = this;
+			  found[count].mDistance = m;
+			}
+		  }
+		  break;
+	  }
+	  count++;
+	  if ( count > maxObjects )
+	  {
+		count = maxObjects;
+	  }
+	}
+
+
+	if ( search1 )
+		search1->search( axis, pos,radius, count, maxObjects, found, iface);
+
+	if ( search2 )
+		search2->search( axis, pos,radius, count, maxObjects, found, iface);
+
+  }
+
+  void search(Axes axis,const float *pos,float radius,uint32_t &count,uint32_t maxObjects,KdTreeFindNode *found,const KdTreeInterface *iface)
+  {
+
+	const float *position = iface->getPositionFloat(mIndex);
+
+	float dx = pos[0] - position[0];
+	float dy = pos[1] - position[1];
+	float dz = pos[2] - position[2];
+
+	KdTreeNode *search1 = 0;
+	KdTreeNode *search2 = 0;
+
+	switch ( axis )
+	{
+	  case X_AXIS:
+	   if ( dx <= 0 )     // JWR  if we are to the left
+	   {
+		search1 = mLeft; // JWR  then search to the left
+		if ( -dx < radius )  // JWR  if distance to the right is less than our search radius, continue on the right as well.
+		  search2 = mRight;
+	   }
+	   else
+	   {
+		 search1 = mRight; // JWR  ok, we go down the left tree
+		 if ( dx < radius ) // JWR  if the distance from the right is less than our search radius
+				search2 = mLeft;
+		}
+		axis = Y_AXIS;
+		break;
+	  case Y_AXIS:
+		if ( dy <= 0 )
+		{
+		  search1 = mLeft;
+		  if ( -dy < radius )
+					search2 = mRight;
+		}
+		else
+		{
+		  search1 = mRight;
+		  if ( dy < radius )
+					search2 = mLeft;
+		}
+		axis = Z_AXIS;
+		break;
+	  case Z_AXIS:
+		if ( dz <= 0 )
+		{
+		  search1 = mLeft;
+		  if ( -dz < radius )
+					search2 = mRight;
+		}
+		else
+		{
+		  search1 = mRight;
+		  if ( dz < radius )
+					search2 = mLeft;
+		}
+		axis = X_AXIS;
+		break;
+	}
+
+	float r2 = radius*radius;
+	float m  = dx*dx+dy*dy+dz*dz;
+
+	if ( m < r2 )
+	{
+	  switch ( count )
+	  {
+		case 0:
+		  found[count].mNode = this;
+		  found[count].mDistance = m;
+		  break;
+		case 1:
+		  if ( m < found[0].mDistance )
+		  {
+			if ( maxObjects == 1 )
+			{
+			  found[0].mNode = this;
+			  found[0].mDistance = m;
+			}
+			else
+			{
+			  found[1] = found[0];
+			  found[0].mNode = this;
+			  found[0].mDistance = m;
+			}
+		  }
+		  else if ( maxObjects > 1)
+		  {
+			found[1].mNode = this;
+			found[1].mDistance = m;
+		  }
+		  break;
+		default:
+		  {
+			bool inserted = false;
+
+			for (uint32_t i=0; i<count; i++)
+			{
+			  if ( m < found[i].mDistance ) // if this one is closer than a pre-existing one...
+			  {
+				// insertion sort...
+				uint32_t scan = count;
+				if ( scan >= maxObjects ) scan=maxObjects-1;
+				for (uint32_t j=scan; j>i; j--)
+				{
+				  found[j] = found[j-1];
+				}
+				found[i].mNode = this;
+				found[i].mDistance = m;
+				inserted = true;
+				break;
+			  }
+			}
+
+			if ( !inserted && count < maxObjects )
+			{
+			  found[count].mNode = this;
+			  found[count].mDistance = m;
+			}
+		  }
+		  break;
+	  }
+	  count++;
+	  if ( count > maxObjects )
+	  {
+		count = maxObjects;
+	  }
+	}
+
+
+	if ( search1 )
+		search1->search( axis, pos,radius, count, maxObjects, found, iface);
+
+	if ( search2 )
+		search2->search( axis, pos,radius, count, maxObjects, found, iface);
+
+  }
+
+private:
+
+  void setLeft(KdTreeNode *left) { mLeft = left; };
+  void setRight(KdTreeNode *right) { mRight = right; };
+
+	KdTreeNode *getLeft(void)         { return mLeft; }
+	KdTreeNode *getRight(void)        { return mRight; }
+
+  uint32_t          mIndex;
+  KdTreeNode     *mLeft;
+  KdTreeNode     *mRight;
+};
+
+
+#define MAX_BUNDLE_SIZE 1024  // 1024 nodes at a time, to minimize memory allocation and guarantee that pointers are persistent.
+
+class KdTreeNodeBundle 
+{
+public:
+
+  KdTreeNodeBundle(void)
+  {
+	mNext = 0;
+	mIndex = 0;
+  }
+
+  bool isFull(void) const
+  {
+	return (bool)( mIndex == MAX_BUNDLE_SIZE );
+  }
+
+  KdTreeNode * getNextNode(void)
+  {
+	assert(mIndex<MAX_BUNDLE_SIZE);
+	KdTreeNode *ret = &mNodes[mIndex];
+	mIndex++;
+	return ret;
+  }
+
+  KdTreeNodeBundle  *mNext;
+  uint32_t             mIndex;
+  KdTreeNode         mNodes[MAX_BUNDLE_SIZE];
+};
+
+
+typedef std::vector< double > DoubleVector;
+typedef std::vector< float >  FloatVector;
+
+class KdTree : public KdTreeInterface
+{
+public:
+  KdTree(void)
+  {
+	mRoot = 0;
+	mBundle = 0;
+	mVcount = 0;
+	mUseDouble = false;
+  }
+
+  virtual ~KdTree(void)
+  {
+	reset();
+  }
+
+  const double * getPositionDouble(uint32_t index) const
+  {
+	assert( mUseDouble );
+	assert ( index < mVcount );
+	return  &mVerticesDouble[index*3];
+  }
+
+  const float * getPositionFloat(uint32_t index) const
+  {
+	assert( !mUseDouble );
+	assert ( index < mVcount );
+	return  &mVerticesFloat[index*3];
+  }
+
+  uint32_t search(const double *pos,double radius,uint32_t maxObjects,KdTreeFindNode *found) const
+  {
+	assert( mUseDouble );
+	if ( !mRoot )	return 0;
+	uint32_t count = 0;
+	mRoot->search(X_AXIS,pos,radius,count,maxObjects,found,this);
+	return count;
+  }
+
+  uint32_t search(const float *pos,float radius,uint32_t maxObjects,KdTreeFindNode *found) const
+  {
+	assert( !mUseDouble );
+	if ( !mRoot )	return 0;
+	uint32_t count = 0;
+	mRoot->search(X_AXIS,pos,radius,count,maxObjects,found,this);
+	return count;
+  }
+
+  void reset(void)
+  {
+	mRoot = 0;
+	mVerticesDouble.clear();
+	mVerticesFloat.clear();
+	KdTreeNodeBundle *bundle = mBundle;
+	while ( bundle )
+	{
+	  KdTreeNodeBundle *next = bundle->mNext;
+	  delete bundle;
+	  bundle = next;
+	}
+	mBundle = 0;
+	mVcount = 0;
+  }
+
+  uint32_t add(double x,double y,double z)
+  {
+	assert(mUseDouble);
+	uint32_t ret = mVcount;
+	mVerticesDouble.push_back(x);
+	mVerticesDouble.push_back(y);
+	mVerticesDouble.push_back(z);
+	mVcount++;
+	KdTreeNode *node = getNewNode(ret);
+	if ( mRoot )
+	{
+	  mRoot->addDouble(node,X_AXIS,this);
+	}
+	else
+	{
+	  mRoot = node;
+	}
+	return ret;
+  }
+
+  uint32_t add(float x,float y,float z)
+  {
+	assert(!mUseDouble);
+	uint32_t ret = mVcount;
+	mVerticesFloat.push_back(x);
+	mVerticesFloat.push_back(y);
+	mVerticesFloat.push_back(z);
+	mVcount++;
+	KdTreeNode *node = getNewNode(ret);
+	if ( mRoot )
+	{
+	  mRoot->addFloat(node,X_AXIS,this);
+	}
+	else
+	{
+	  mRoot = node;
+	}
+	return ret;
+  }
+
+  KdTreeNode * getNewNode(uint32_t index)
+  {
+	if ( mBundle == 0 )
+	{
+	  mBundle = new KdTreeNodeBundle;
+	}
+	if ( mBundle->isFull() )
+	{
+	  KdTreeNodeBundle *bundle = new KdTreeNodeBundle;
+	  mBundle->mNext = bundle;
+	  mBundle = bundle;
+	}
+	KdTreeNode *node = mBundle->getNextNode();
+	new ( node ) KdTreeNode(index);
+	return node;
+  }
+
+  uint32_t getNearest(const double *pos,double radius,bool &_found) const // returns the nearest possible neighbor's index.
+  {
+	assert( mUseDouble );
+	uint32_t ret = 0;
+
+	_found = false;
+	KdTreeFindNode found[1];
+	uint32_t count = search(pos,radius,1,found);
+	if ( count )
+	{
+	  KdTreeNode *node = found[0].mNode;
+	  ret = node->getIndex();
+	  _found = true;
+	}
+	return ret;
+  }
+
+  uint32_t getNearest(const float *pos,float radius,bool &_found) const // returns the nearest possible neighbor's index.
+  {
+	assert( !mUseDouble );
+	uint32_t ret = 0;
+
+	_found = false;
+	KdTreeFindNode found[1];
+	uint32_t count = search(pos,radius,1,found);
+	if ( count )
+	{
+	  KdTreeNode *node = found[0].mNode;
+	  ret = node->getIndex();
+	  _found = true;
+	}
+	return ret;
+  }
+
+  const double * getVerticesDouble(void) const
+  {
+	assert( mUseDouble );
+	const double *ret = 0;
+	if ( !mVerticesDouble.empty() )
+	{
+	  ret = &mVerticesDouble[0];
+	}
+	return ret;
+  }
+
+  const float * getVerticesFloat(void) const
+  {
+	assert( !mUseDouble );
+	const float * ret = 0;
+	if ( !mVerticesFloat.empty() )
+	{
+	  ret = &mVerticesFloat[0];
+	}
+	return ret;
+  }
+
+  uint32_t getVcount(void) const { return mVcount; };
+
+  void setUseDouble(bool useDouble)
+  {
+	mUseDouble = useDouble;
+  }
+
+private:
+  bool                    mUseDouble;
+  KdTreeNode             *mRoot;
+  KdTreeNodeBundle       *mBundle;
+  uint32_t                  mVcount;
+  DoubleVector            mVerticesDouble;
+  FloatVector             mVerticesFloat;
+};
+
+}; // end of namespace VERTEX_INDEX
+
+class MyVertexIndex : public fm_VertexIndex
+{
+public:
+  MyVertexIndex(double granularity,bool snapToGrid)
+  {
+	mDoubleGranularity = granularity;
+	mFloatGranularity  = (float)granularity;
+	mSnapToGrid        = snapToGrid;
+	mUseDouble         = true;
+	mKdTree.setUseDouble(true);
+  }
+
+  MyVertexIndex(float granularity,bool snapToGrid)
+  {
+	mDoubleGranularity = granularity;
+	mFloatGranularity  = (float)granularity;
+	mSnapToGrid        = snapToGrid;
+	mUseDouble         = false;
+	mKdTree.setUseDouble(false);
+  }
+
+  virtual ~MyVertexIndex(void)
+  {
+
+  }
+
+
+  double snapToGrid(double p)
+  {
+	double m = fmod(p,mDoubleGranularity);
+	p-=m;
+	return p;
+  }
+
+  float snapToGrid(float p)
+  {
+	float m = fmodf(p,mFloatGranularity);
+	p-=m;
+	return p;
+  }
+
+  uint32_t    getIndex(const float *_p,bool &newPos)  // get index for a vector float
+  {
+	uint32_t ret;
+
+	if ( mUseDouble )
+	{
+	  double p[3];
+	  p[0] = _p[0];
+	  p[1] = _p[1];
+	  p[2] = _p[2];
+	  return getIndex(p,newPos);
+	}
+
+	newPos = false;
+
+	float p[3];
+
+	if ( mSnapToGrid )
+	{
+	  p[0] = snapToGrid(_p[0]);
+	  p[1] = snapToGrid(_p[1]);
+	  p[2] = snapToGrid(_p[2]);
+	}
+	else
+	{
+	  p[0] = _p[0];
+	  p[1] = _p[1];
+	  p[2] = _p[2];
+	}
+
+	bool found;
+	ret = mKdTree.getNearest(p,mFloatGranularity,found);
+	if ( !found )
+	{
+	  newPos = true;
+	  ret = mKdTree.add(p[0],p[1],p[2]);
+	}
+
+
+	return ret;
+  }
+
+  uint32_t    getIndex(const double *_p,bool &newPos)  // get index for a vector double
+  {
+	uint32_t ret;
+
+	if ( !mUseDouble )
+	{
+	  float p[3];
+	  p[0] = (float)_p[0];
+	  p[1] = (float)_p[1];
+	  p[2] = (float)_p[2];
+	  return getIndex(p,newPos);
+	}
+
+	newPos = false;
+
+	double p[3];
+
+	if ( mSnapToGrid )
+	{
+	  p[0] = snapToGrid(_p[0]);
+	  p[1] = snapToGrid(_p[1]);
+	  p[2] = snapToGrid(_p[2]);
+	}
+	else
+	{
+	  p[0] = _p[0];
+	  p[1] = _p[1];
+	  p[2] = _p[2];
+	}
+
+	bool found;
+	ret = mKdTree.getNearest(p,mDoubleGranularity,found);
+	if ( !found )
+	{
+	  newPos = true;
+	  ret = mKdTree.add(p[0],p[1],p[2]);
+	}
+
+
+	return ret;
+  }
+
+  const float *   getVerticesFloat(void) const
+  {
+	const float * ret = 0;
+
+	assert( !mUseDouble );
+
+	ret = mKdTree.getVerticesFloat();
+
+	return ret;
+  }
+
+  const double *  getVerticesDouble(void) const
+  {
+	const double * ret = 0;
+
+	assert( mUseDouble );
+
+	ret = mKdTree.getVerticesDouble();
+
+	return ret;
+  }
+
+  const float *   getVertexFloat(uint32_t index) const
+  {
+	const float * ret  = 0;
+	assert( !mUseDouble );
+#ifdef _DEBUG
+	uint32_t vcount = mKdTree.getVcount();
+	assert( index < vcount );
+#endif
+	ret =  mKdTree.getVerticesFloat();
+	ret = &ret[index*3];
+	return ret;
+  }
+
+  const double *   getVertexDouble(uint32_t index) const
+  {
+	const double * ret = 0;
+	assert( mUseDouble );
+#ifdef _DEBUG
+	uint32_t vcount = mKdTree.getVcount();
+	assert( index < vcount );
+#endif
+	ret =  mKdTree.getVerticesDouble();
+	ret = &ret[index*3];
+
+	return ret;
+  }
+
+  uint32_t    getVcount(void) const
+  {
+	return mKdTree.getVcount();
+  }
+
+  bool isDouble(void) const
+  {
+	return mUseDouble;
+  }
+
+
+  bool            saveAsObj(const char *fname,uint32_t tcount,uint32_t *indices)
+  {
+	bool ret = false;
+
+
+	FILE *fph = fopen(fname,"wb");
+	if ( fph )
+	{
+	  ret = true;
+
+	  uint32_t vcount    = getVcount();
+	  if ( mUseDouble )
+	  {
+		const double *v  = getVerticesDouble();
+		for (uint32_t i=0; i<vcount; i++)
+		{
+		  fprintf(fph,"v %0.9f %0.9f %0.9f\r\n", (float)v[0], (float)v[1], (float)v[2] );
+		  v+=3;
+		}
+	  }
+	  else
+	  {
+		const float *v  = getVerticesFloat();
+		for (uint32_t i=0; i<vcount; i++)
+		{
+		  fprintf(fph,"v %0.9f %0.9f %0.9f\r\n", v[0], v[1], v[2] );
+		  v+=3;
+		}
+	  }
+
+	  for (uint32_t i=0; i<tcount; i++)
+	  {
+		uint32_t i1 = *indices++;
+		uint32_t i2 = *indices++;
+		uint32_t i3 = *indices++;
+		fprintf(fph,"f %d %d %d\r\n", i1+1, i2+1, i3+1 );
+	  }
+	  fclose(fph);
+	}
+
+	return ret;
+  }
+
+private:
+  bool    mUseDouble:1;
+  bool    mSnapToGrid:1;
+  double  mDoubleGranularity;
+  float   mFloatGranularity;
+  VERTEX_INDEX::KdTree  mKdTree;
+};
+
+fm_VertexIndex * fm_createVertexIndex(double granularity,bool snapToGrid) // create an indexed vertex system for doubles
+{
+  MyVertexIndex *ret = new MyVertexIndex(granularity,snapToGrid);
+  return static_cast< fm_VertexIndex *>(ret);
+}
+
+fm_VertexIndex * fm_createVertexIndex(float granularity,bool snapToGrid)  // create an indexed vertext system for floats
+{
+  MyVertexIndex *ret = new MyVertexIndex(granularity,snapToGrid);
+  return static_cast< fm_VertexIndex *>(ret);
+}
+
+void          fm_releaseVertexIndex(fm_VertexIndex *vindex)
+{
+  MyVertexIndex *m = static_cast< MyVertexIndex *>(vindex);
+  delete m;
+}
+
+#endif   // END OF VERTEX WELDING CODE
+
+
+REAL fm_computeBestFitAABB(uint32_t vcount,const REAL *points,uint32_t pstride,REAL *bmin,REAL *bmax) // returns the diagonal distance
+{
+
+  const uint8_t *source = (const uint8_t *) points;
+
+	bmin[0] = points[0];
+	bmin[1] = points[1];
+	bmin[2] = points[2];
+
+	bmax[0] = points[0];
+	bmax[1] = points[1];
+	bmax[2] = points[2];
+
+
+  for (uint32_t i=1; i<vcount; i++)
+  {
+	source+=pstride;
+	const REAL *p = (const REAL *) source;
+
+	if ( p[0] < bmin[0] ) bmin[0] = p[0];
+	if ( p[1] < bmin[1] ) bmin[1] = p[1];
+	if ( p[2] < bmin[2] ) bmin[2] = p[2];
+
+		if ( p[0] > bmax[0] ) bmax[0] = p[0];
+		if ( p[1] > bmax[1] ) bmax[1] = p[1];
+		if ( p[2] > bmax[2] ) bmax[2] = p[2];
+
+  }
+
+  REAL dx = bmax[0] - bmin[0];
+  REAL dy = bmax[1] - bmin[1];
+  REAL dz = bmax[2] - bmin[2];
+
+	return (REAL) sqrt( dx*dx + dy*dy + dz*dz );
+
+}
+
+
+
+/* a = b - c */
+#define vector(a,b,c) \
+	(a)[0] = (b)[0] - (c)[0];	\
+	(a)[1] = (b)[1] - (c)[1];	\
+	(a)[2] = (b)[2] - (c)[2];
+
+
+
+#define innerProduct(v,q) \
+		((v)[0] * (q)[0] + \
+		(v)[1] * (q)[1] + \
+		(v)[2] * (q)[2])
+
+#define crossProduct(a,b,c) \
+	(a)[0] = (b)[1] * (c)[2] - (c)[1] * (b)[2]; \
+	(a)[1] = (b)[2] * (c)[0] - (c)[2] * (b)[0]; \
+	(a)[2] = (b)[0] * (c)[1] - (c)[0] * (b)[1];
+
+
+bool fm_lineIntersectsTriangle(const REAL *rayStart,const REAL *rayEnd,const REAL *p1,const REAL *p2,const REAL *p3,REAL *sect)
+{
+	REAL dir[3];
+
+  dir[0] = rayEnd[0] - rayStart[0];
+  dir[1] = rayEnd[1] - rayStart[1];
+  dir[2] = rayEnd[2] - rayStart[2];
+
+  REAL d = (REAL)sqrt(dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2]);
+  REAL r = 1.0f / d;
+
+  dir[0]*=r;
+  dir[1]*=r;
+  dir[2]*=r;
+
+
+  REAL t;
+
+	bool ret = fm_rayIntersectsTriangle(rayStart, dir, p1, p2, p3, t );
+
+	if ( ret )
+	{
+		if ( t > d )
+		{
+			sect[0] = rayStart[0] + dir[0]*t;
+			sect[1] = rayStart[1] + dir[1]*t;
+			sect[2] = rayStart[2] + dir[2]*t;
+		}
+		else
+		{
+			ret = false;
+		}
+	}
+
+  return ret;
+}
+
+
+
+bool fm_rayIntersectsTriangle(const REAL *p,const REAL *d,const REAL *v0,const REAL *v1,const REAL *v2,REAL &t)
+{
+	REAL e1[3],e2[3],h[3],s[3],q[3];
+	REAL a,f,u,v;
+
+	vector(e1,v1,v0);
+	vector(e2,v2,v0);
+	crossProduct(h,d,e2);
+	a = innerProduct(e1,h);
+
+	if (a > -0.00001 && a < 0.00001)
+		return(false);
+
+	f = 1/a;
+	vector(s,p,v0);
+	u = f * (innerProduct(s,h));
+
+	if (u < 0.0 || u > 1.0)
+		return(false);
+
+	crossProduct(q,s,e1);
+	v = f * innerProduct(d,q);
+	if (v < 0.0 || u + v > 1.0)
+		return(false);
+	// at this stage we can compute t to find out where
+	// the intersection point is on the line
+	t = f * innerProduct(e2,q);
+	if (t > 0) // ray intersection
+		return(true);
+	else // this means that there is a line intersection
+		 // but not a ray intersection
+		 return (false);
+}
+
+
+inline REAL det(const REAL *p1,const REAL *p2,const REAL *p3)
+{
+  return  p1[0]*p2[1]*p3[2] + p2[0]*p3[1]*p1[2] + p3[0]*p1[1]*p2[2] -p1[0]*p3[1]*p2[2] - p2[0]*p1[1]*p3[2] - p3[0]*p2[1]*p1[2];
+}
+
+
+REAL  fm_computeMeshVolume(const REAL *vertices,uint32_t tcount,const uint32_t *indices)
+{
+	REAL volume = 0;
+
+	for (uint32_t i=0; i<tcount; i++,indices+=3)
+	{
+	const REAL *p1 = &vertices[ indices[0]*3 ];
+		const REAL *p2 = &vertices[ indices[1]*3 ];
+		const REAL *p3 = &vertices[ indices[2]*3 ];
+		volume+=det(p1,p2,p3); // compute the volume of the tetrahedra relative to the origin.
+	}
+
+	volume*=(1.0f/6.0f);
+	if ( volume < 0 )
+		volume*=-1;
+	return volume;
+}
+
+
+const REAL * fm_getPoint(const REAL *points,uint32_t pstride,uint32_t index)
+{
+  const uint8_t *scan = (const uint8_t *)points;
+  scan+=(index*pstride);
+  return (REAL *)scan;
+}
+
+
+bool fm_insideTriangle(REAL Ax, REAL Ay,
+					  REAL Bx, REAL By,
+					  REAL Cx, REAL Cy,
+					  REAL Px, REAL Py)
+
+{
+  REAL ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy;
+  REAL cCROSSap, bCROSScp, aCROSSbp;
+
+  ax = Cx - Bx;  ay = Cy - By;
+  bx = Ax - Cx;  by = Ay - Cy;
+  cx = Bx - Ax;  cy = By - Ay;
+  apx= Px - Ax;  apy= Py - Ay;
+  bpx= Px - Bx;  bpy= Py - By;
+  cpx= Px - Cx;  cpy= Py - Cy;
+
+  aCROSSbp = ax*bpy - ay*bpx;
+  cCROSSap = cx*apy - cy*apx;
+  bCROSScp = bx*cpy - by*cpx;
+
+  return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f));
+}
+
+
+REAL fm_areaPolygon2d(uint32_t pcount,const REAL *points,uint32_t pstride)
+{
+  int32_t n = (int32_t)pcount;
+
+  REAL A=0.0f;
+  for(int32_t p=n-1,q=0; q<n; p=q++)
+  {
+	const REAL *p1 = fm_getPoint(points,pstride,p);
+	const REAL *p2 = fm_getPoint(points,pstride,q);
+	A+= p1[0]*p2[1] - p2[0]*p1[1];
+  }
+  return A*0.5f;
+}
+
+
+bool  fm_pointInsidePolygon2d(uint32_t pcount,const REAL *points,uint32_t pstride,const REAL *point,uint32_t xindex,uint32_t yindex)
+{
+  uint32_t j = pcount-1;
+  int32_t oddNodes = 0;
+
+  REAL x = point[xindex];
+  REAL y = point[yindex];
+
+  for (uint32_t i=0; i<pcount; i++)
+  {
+	const REAL *p1 = fm_getPoint(points,pstride,i);
+	const REAL *p2 = fm_getPoint(points,pstride,j);
+
+	REAL x1 = p1[xindex];
+	REAL y1 = p1[yindex];
+
+	REAL x2 = p2[xindex];
+	REAL y2 = p2[yindex];
+
+	if ( (y1 < y && y2 >= y) ||  (y2 < y && y1 >= y) )
+	{
+	  if (x1+(y-y1)/(y2-y1)*(x2-x1)<x)
+	  {
+		oddNodes = 1-oddNodes;
+	  }
+	}
+	j = i;
+  }
+
+  return oddNodes ? true : false;
+}
+
+
+uint32_t fm_consolidatePolygon(uint32_t pcount,const REAL *points,uint32_t pstride,REAL *_dest,REAL epsilon) // collapses co-linear edges.
+{
+  uint32_t ret = 0;
+
+
+  if ( pcount >= 3 )
+  {
+	const REAL *prev = fm_getPoint(points,pstride,pcount-1);
+	const REAL *current = points;
+	const REAL *next    = fm_getPoint(points,pstride,1);
+	REAL *dest = _dest;
+
+	for (uint32_t i=0; i<pcount; i++)
+	{
+
+	  next = (i+1)==pcount ? points : next;
+
+	  if ( !fm_colinear(prev,current,next,epsilon) )
+	  {
+		dest[0] = current[0];
+		dest[1] = current[1];
+		dest[2] = current[2];
+
+		dest+=3;
+		ret++;
+	  }
+
+	  prev = current;
+	  current+=3;
+	  next+=3;
+
+	}
+  }
+
+  return ret;
+}
+
+
+#ifndef RECT3D_TEMPLATE
+
+#define RECT3D_TEMPLATE
+
+template <class T> class Rect3d
+{
+public:
+  Rect3d(void) { };
+
+  Rect3d(const T *bmin,const T *bmax)
+  {
+
+	mMin[0] = bmin[0];
+	mMin[1] = bmin[1];
+	mMin[2] = bmin[2];
+
+	mMax[0] = bmax[0];
+	mMax[1] = bmax[1];
+	mMax[2] = bmax[2];
+
+  }
+
+  void SetMin(const T *bmin)
+  {
+	mMin[0] = bmin[0];
+	mMin[1] = bmin[1];
+	mMin[2] = bmin[2];
+  }
+
+  void SetMax(const T *bmax)
+  {
+	mMax[0] = bmax[0];
+	mMax[1] = bmax[1];
+	mMax[2] = bmax[2];
+  }
+
+	void SetMin(T x,T y,T z)
+	{
+		mMin[0] = x;
+		mMin[1] = y;
+		mMin[2] = z;
+	}
+
+	void SetMax(T x,T y,T z)
+	{
+		mMax[0] = x;
+		mMax[1] = y;
+		mMax[2] = z;
+	}
+
+  T mMin[3];
+  T mMax[3];
+};
+
+#endif
+
+void splitRect(uint32_t axis,
+						   const Rect3d<REAL> &source,
+							 Rect3d<REAL> &b1,
+							 Rect3d<REAL> &b2,
+							 const REAL *midpoint)
+{
+	switch ( axis )
+	{
+		case 0:
+			b1.SetMin(source.mMin);
+			b1.SetMax( midpoint[0], source.mMax[1], source.mMax[2] );
+
+			b2.SetMin( midpoint[0], source.mMin[1], source.mMin[2] );
+			b2.SetMax(source.mMax);
+
+			break;
+		case 1:
+			b1.SetMin(source.mMin);
+			b1.SetMax( source.mMax[0], midpoint[1], source.mMax[2] );
+
+			b2.SetMin( source.mMin[0], midpoint[1], source.mMin[2] );
+			b2.SetMax(source.mMax);
+
+			break;
+		case 2:
+			b1.SetMin(source.mMin);
+			b1.SetMax( source.mMax[0], source.mMax[1], midpoint[2] );
+
+			b2.SetMin( source.mMin[0], source.mMin[1], midpoint[2] );
+			b2.SetMax(source.mMax);
+
+			break;
+	}
+}
+
+bool fm_computeSplitPlane(uint32_t vcount,
+						  const REAL *vertices,
+						  uint32_t /* tcount */,
+						  const uint32_t * /* indices */,
+						  REAL *plane)
+{
+
+  REAL sides[3];
+  REAL matrix[16];
+
+  fm_computeBestFitOBB( vcount, vertices, sizeof(REAL)*3, sides, matrix );
+
+  REAL bmax[3];
+  REAL bmin[3];
+
+  bmax[0] = sides[0]*0.5f;
+  bmax[1] = sides[1]*0.5f;
+  bmax[2] = sides[2]*0.5f;
+
+  bmin[0] = -bmax[0];
+  bmin[1] = -bmax[1];
+  bmin[2] = -bmax[2];
+
+
+  REAL dx = sides[0];
+  REAL dy = sides[1];
+  REAL dz = sides[2];
+
+
+	uint32_t axis = 0;
+
+	if ( dy > dx )
+	{
+		axis = 1;
+	}
+
+	if ( dz > dx && dz > dy )
+	{
+		axis = 2;
+	}
+
+  REAL p1[3];
+  REAL p2[3];
+  REAL p3[3];
+
+  p3[0] = p2[0] = p1[0] = bmin[0] + dx*0.5f;
+  p3[1] = p2[1] = p1[1] = bmin[1] + dy*0.5f;
+  p3[2] = p2[2] = p1[2] = bmin[2] + dz*0.5f;
+
+  Rect3d<REAL> b(bmin,bmax);
+
+  Rect3d<REAL> b1,b2;
+
+  splitRect(axis,b,b1,b2,p1);
+
+
+  switch ( axis )
+  {
+	case 0:
+	  p2[1] = bmin[1];
+	  p2[2] = bmin[2];
+
+	  if ( dz > dy )
+	  {
+		p3[1] = bmax[1];
+		p3[2] = bmin[2];
+	  }
+	  else
+	  {
+		p3[1] = bmin[1];
+		p3[2] = bmax[2];
+	  }
+
+	  break;
+	case 1:
+	  p2[0] = bmin[0];
+	  p2[2] = bmin[2];
+
+	  if ( dx > dz )
+	  {
+		p3[0] = bmax[0];
+		p3[2] = bmin[2];
+	  }
+	  else
+	  {
+		p3[0] = bmin[0];
+		p3[2] = bmax[2];
+	  }
+
+	  break;
+	case 2:
+	  p2[0] = bmin[0];
+	  p2[1] = bmin[1];
+
+	  if ( dx > dy )
+	  {
+		p3[0] = bmax[0];
+		p3[1] = bmin[1];
+	  }
+	  else
+	  {
+		p3[0] = bmin[0];
+		p3[1] = bmax[1];
+	  }
+
+	  break;
+  }
+
+  REAL tp1[3];
+  REAL tp2[3];
+  REAL tp3[3];
+
+  fm_transform(matrix,p1,tp1);
+  fm_transform(matrix,p2,tp2);
+  fm_transform(matrix,p3,tp3);
+
+	plane[3] = fm_computePlane(tp1,tp2,tp3,plane);
+
+  return true;
+
+}
+
+#pragma warning(disable:4100)
+
+void fm_nearestPointInTriangle(const REAL * /*nearestPoint*/,const REAL * /*p1*/,const REAL * /*p2*/,const REAL * /*p3*/,REAL * /*nearest*/)
+{
+
+}
+
+static REAL Partial(const REAL *a,const REAL *p) 
+{
+	return (a[0]*p[1]) - (p[0]*a[1]);
+}
+
+REAL  fm_areaTriangle(const REAL *p0,const REAL *p1,const REAL *p2)
+{
+  REAL A = Partial(p0,p1);
+	A+= Partial(p1,p2);
+	A+= Partial(p2,p0);
+	return A*0.5f;
+}
+
+void fm_subtract(const REAL *A,const REAL *B,REAL *diff) // compute A-B and store the result in 'diff'
+{
+  diff[0] = A[0]-B[0];
+  diff[1] = A[1]-B[1];
+  diff[2] = A[2]-B[2];
+}
+
+
+void  fm_multiplyTransform(const REAL *pA,const REAL *pB,REAL *pM)
+{
+
+  REAL a = pA[0*4+0] * pB[0*4+0] + pA[0*4+1] * pB[1*4+0] + pA[0*4+2] * pB[2*4+0] + pA[0*4+3] * pB[3*4+0];
+  REAL b = pA[0*4+0] * pB[0*4+1] + pA[0*4+1] * pB[1*4+1] + pA[0*4+2] * pB[2*4+1] + pA[0*4+3] * pB[3*4+1];
+  REAL c = pA[0*4+0] * pB[0*4+2] + pA[0*4+1] * pB[1*4+2] + pA[0*4+2] * pB[2*4+2] + pA[0*4+3] * pB[3*4+2];
+  REAL d = pA[0*4+0] * pB[0*4+3] + pA[0*4+1] * pB[1*4+3] + pA[0*4+2] * pB[2*4+3] + pA[0*4+3] * pB[3*4+3];
+
+  REAL e = pA[1*4+0] * pB[0*4+0] + pA[1*4+1] * pB[1*4+0] + pA[1*4+2] * pB[2*4+0] + pA[1*4+3] * pB[3*4+0];
+  REAL f = pA[1*4+0] * pB[0*4+1] + pA[1*4+1] * pB[1*4+1] + pA[1*4+2] * pB[2*4+1] + pA[1*4+3] * pB[3*4+1];
+  REAL g = pA[1*4+0] * pB[0*4+2] + pA[1*4+1] * pB[1*4+2] + pA[1*4+2] * pB[2*4+2] + pA[1*4+3] * pB[3*4+2];
+  REAL h = pA[1*4+0] * pB[0*4+3] + pA[1*4+1] * pB[1*4+3] + pA[1*4+2] * pB[2*4+3] + pA[1*4+3] * pB[3*4+3];
+
+  REAL i = pA[2*4+0] * pB[0*4+0] + pA[2*4+1] * pB[1*4+0] + pA[2*4+2] * pB[2*4+0] + pA[2*4+3] * pB[3*4+0];
+  REAL j = pA[2*4+0] * pB[0*4+1] + pA[2*4+1] * pB[1*4+1] + pA[2*4+2] * pB[2*4+1] + pA[2*4+3] * pB[3*4+1];
+  REAL k = pA[2*4+0] * pB[0*4+2] + pA[2*4+1] * pB[1*4+2] + pA[2*4+2] * pB[2*4+2] + pA[2*4+3] * pB[3*4+2];
+  REAL l = pA[2*4+0] * pB[0*4+3] + pA[2*4+1] * pB[1*4+3] + pA[2*4+2] * pB[2*4+3] + pA[2*4+3] * pB[3*4+3];
+
+  REAL m = pA[3*4+0] * pB[0*4+0] + pA[3*4+1] * pB[1*4+0] + pA[3*4+2] * pB[2*4+0] + pA[3*4+3] * pB[3*4+0];
+  REAL n = pA[3*4+0] * pB[0*4+1] + pA[3*4+1] * pB[1*4+1] + pA[3*4+2] * pB[2*4+1] + pA[3*4+3] * pB[3*4+1];
+  REAL o = pA[3*4+0] * pB[0*4+2] + pA[3*4+1] * pB[1*4+2] + pA[3*4+2] * pB[2*4+2] + pA[3*4+3] * pB[3*4+2];
+  REAL p = pA[3*4+0] * pB[0*4+3] + pA[3*4+1] * pB[1*4+3] + pA[3*4+2] * pB[2*4+3] + pA[3*4+3] * pB[3*4+3];
+
+  pM[0] = a;  pM[1] = b;  pM[2] = c;  pM[3] = d;
+
+  pM[4] = e;  pM[5] = f;  pM[6] = g;  pM[7] = h;
+
+  pM[8] = i;  pM[9] = j;  pM[10] = k;  pM[11] = l;
+
+  pM[12] = m;  pM[13] = n;  pM[14] = o;  pM[15] = p;
+}
+
+void fm_multiply(REAL *A,REAL scalar)
+{
+  A[0]*=scalar;
+  A[1]*=scalar;
+  A[2]*=scalar;
+}
+
+void fm_add(const REAL *A,const REAL *B,REAL *sum)
+{
+  sum[0] = A[0]+B[0];
+  sum[1] = A[1]+B[1];
+  sum[2] = A[2]+B[2];
+}
+
+void fm_copy3(const REAL *source,REAL *dest)
+{
+  dest[0] = source[0];
+  dest[1] = source[1];
+  dest[2] = source[2];
+}
+
+
+uint32_t  fm_copyUniqueVertices(uint32_t vcount,const REAL *input_vertices,REAL *output_vertices,uint32_t tcount,const uint32_t *input_indices,uint32_t *output_indices)
+{
+  uint32_t ret = 0;
+
+  REAL *vertices = (REAL *)malloc(sizeof(REAL)*vcount*3);
+  memcpy(vertices,input_vertices,sizeof(REAL)*vcount*3);
+  REAL *dest = output_vertices;
+
+  uint32_t *reindex = (uint32_t *)malloc(sizeof(uint32_t)*vcount);
+  memset(reindex,0xFF,sizeof(uint32_t)*vcount);
+
+  uint32_t icount = tcount*3;
+
+  for (uint32_t i=0; i<icount; i++)
+  {
+	uint32_t index = *input_indices++;
+
+	assert( index < vcount );
+
+	if ( reindex[index] == 0xFFFFFFFF )
+	{
+	  *output_indices++ = ret;
+	  reindex[index] = ret;
+	  const REAL *pos = &vertices[index*3];
+	  dest[0] = pos[0];
+	  dest[1] = pos[1];
+	  dest[2] = pos[2];
+	  dest+=3;
+	  ret++;
+	}
+	else
+	{
+	  *output_indices++ = reindex[index];
+	}
+  }
+  free(vertices);
+  free(reindex);
+  return ret;
+}
+
+bool    fm_isMeshCoplanar(uint32_t tcount,const uint32_t *indices,const REAL *vertices,bool doubleSided) // returns true if this collection of indexed triangles are co-planar!
+{
+  bool ret = true;
+
+  if ( tcount > 0 )
+  {
+	uint32_t i1 = indices[0];
+	uint32_t i2 = indices[1];
+	uint32_t i3 = indices[2];
+	const REAL *p1 = &vertices[i1*3];
+	const REAL *p2 = &vertices[i2*3];
+	const REAL *p3 = &vertices[i3*3];
+	REAL plane[4];
+	plane[3] = fm_computePlane(p1,p2,p3,plane);
+	const uint32_t *scan = &indices[3];
+	for (uint32_t i=1; i<tcount; i++)
+	{
+	  i1 = *scan++;
+	  i2 = *scan++;
+	  i3 = *scan++;
+	  p1 = &vertices[i1*3];
+	  p2 = &vertices[i2*3];
+	  p3 = &vertices[i3*3];
+	  REAL _plane[4];
+	  _plane[3] = fm_computePlane(p1,p2,p3,_plane);
+	  if ( !fm_samePlane(plane,_plane,0.01f,0.001f,doubleSided) )
+	  {
+		ret = false;
+		break;
+	  }
+	}
+  }
+  return ret;
+}
+
+
+bool fm_samePlane(const REAL p1[4],const REAL p2[4],REAL normalEpsilon,REAL dEpsilon,bool doubleSided)
+{
+  bool ret = false;
+
+#if 0
+  if (p1[0] == p2[0] &&
+	  p1[1] == p2[1] &&
+	  p1[2] == p2[2] &&
+	  p1[3] == p2[3])
+  {
+	  ret = true;
+  }
+#else
+  REAL diff = (REAL) fabs(p1[3]-p2[3]);
+  if ( diff < dEpsilon ) // if the plane -d  co-efficient is within our epsilon
+  {
+	REAL dot = fm_dot(p1,p2); // compute the dot-product of the vector normals.
+	if ( doubleSided ) dot = (REAL)fabs(dot);
+	REAL dmin = 1 - normalEpsilon;
+	REAL dmax = 1 + normalEpsilon;
+	if ( dot >= dmin && dot <= dmax )
+	{
+	  ret = true; // then the plane equation is for practical purposes identical.
+	}
+  }
+#endif
+  return ret;
+}
+
+
+void  fm_initMinMax(REAL bmin[3],REAL bmax[3])
+{
+  bmin[0] = FLT_MAX;
+  bmin[1] = FLT_MAX;
+  bmin[2] = FLT_MAX;
+
+  bmax[0] = -FLT_MAX;
+  bmax[1] = -FLT_MAX;
+  bmax[2] = -FLT_MAX;
+}
+
+void fm_inflateMinMax(REAL bmin[3], REAL bmax[3], REAL ratio)
+{
+	REAL inflate = fm_distance(bmin, bmax)*0.5f*ratio;
+
+	bmin[0] -= inflate;
+	bmin[1] -= inflate;
+	bmin[2] -= inflate;
+
+	bmax[0] += inflate;
+	bmax[1] += inflate;
+	bmax[2] += inflate;
+}
+
+#ifndef TESSELATE_H
+
+#define TESSELATE_H
+
+typedef std::vector< uint32_t > UintVector;
+
+class Myfm_Tesselate : public fm_Tesselate
+{
+public:
+  virtual ~Myfm_Tesselate(void)
+  {
+
+  }
+
+  const uint32_t * tesselate(fm_VertexIndex *vindex,uint32_t tcount,const uint32_t *indices,float longEdge,uint32_t maxDepth,uint32_t &outcount)
+  {
+	const uint32_t *ret = 0;
+
+	mMaxDepth = maxDepth;
+	mLongEdge  = longEdge*longEdge;
+	mLongEdgeD = mLongEdge;
+	mVertices = vindex;
+
+	if ( mVertices->isDouble() )
+	{
+	  uint32_t vcount = mVertices->getVcount();
+	  double *vertices = (double *)malloc(sizeof(double)*vcount*3);
+	  memcpy(vertices,mVertices->getVerticesDouble(),sizeof(double)*vcount*3);
+
+	  for (uint32_t i=0; i<tcount; i++)
+	  {
+		uint32_t i1 = *indices++;
+		uint32_t i2 = *indices++;
+		uint32_t i3 = *indices++;
+
+		const double *p1 = &vertices[i1*3];
+		const double *p2 = &vertices[i2*3];
+		const double *p3 = &vertices[i3*3];
+
+		tesselate(p1,p2,p3,0);
+
+	  }
+	  free(vertices);
+	}
+	else
+	{
+	  uint32_t vcount = mVertices->getVcount();
+	  float *vertices = (float *)malloc(sizeof(float)*vcount*3);
+	  memcpy(vertices,mVertices->getVerticesFloat(),sizeof(float)*vcount*3);
+
+
+	  for (uint32_t i=0; i<tcount; i++)
+	  {
+		uint32_t i1 = *indices++;
+		uint32_t i2 = *indices++;
+		uint32_t i3 = *indices++;
+
+		const float *p1 = &vertices[i1*3];
+		const float *p2 = &vertices[i2*3];
+		const float *p3 = &vertices[i3*3];
+
+		tesselate(p1,p2,p3,0);
+
+	  }
+	  free(vertices);
+	}
+
+	outcount = (uint32_t)(mIndices.size()/3);
+	ret = &mIndices[0];
+
+
+	return ret;
+  }
+
+  void tesselate(const float *p1,const float *p2,const float *p3,uint32_t recurse)
+  {
+	bool split = false;
+	float l1,l2,l3;
+
+	l1 = l2 = l3 = 0;
+
+	if ( recurse < mMaxDepth )
+	{
+	  l1 = fm_distanceSquared(p1,p2);
+		l2 = fm_distanceSquared(p2,p3);
+		l3 = fm_distanceSquared(p3,p1);
+
+	  if (  l1 > mLongEdge || l2 > mLongEdge || l3 > mLongEdge )
+		split = true;
+
+	}
+
+	if ( split )
+	{
+		uint32_t edge;
+
+		if ( l1 >= l2 && l1 >= l3 )
+			edge = 0;
+		else if ( l2 >= l1 && l2 >= l3 )
+			edge = 1;
+		else
+			edge = 2;
+
+			float splits[3];
+
+		switch ( edge )
+		{
+			case 0:
+				{
+			fm_lerp(p1,p2,splits,0.5f);
+			tesselate(p1,splits,p3, recurse+1 );
+			tesselate(splits,p2,p3, recurse+1 );
+				}
+				break;
+			case 1:
+				{
+			fm_lerp(p2,p3,splits,0.5f);
+			tesselate(p1,p2,splits, recurse+1 );
+			tesselate(p1,splits,p3, recurse+1 );
+				}
+				break;
+			case 2:
+				{
+					fm_lerp(p3,p1,splits,0.5f);
+			tesselate(p1,p2,splits, recurse+1 );
+			tesselate(splits,p2,p3, recurse+1 );
+				}
+				break;
+		}
+	}
+	else
+	{
+	  bool newp;
+
+	  uint32_t i1 = mVertices->getIndex(p1,newp);
+	  uint32_t i2 = mVertices->getIndex(p2,newp);
+	  uint32_t i3 = mVertices->getIndex(p3,newp);
+
+	  mIndices.push_back(i1);
+	  mIndices.push_back(i2);
+	  mIndices.push_back(i3);
+	}
+
+  }
+
+  void tesselate(const double *p1,const double *p2,const double *p3,uint32_t recurse)
+  {
+	bool split = false;
+	double l1,l2,l3;
+
+	l1 = l2 = l3 = 0;
+
+	if ( recurse < mMaxDepth )
+	{
+	  l1 = fm_distanceSquared(p1,p2);
+		l2 = fm_distanceSquared(p2,p3);
+		l3 = fm_distanceSquared(p3,p1);
+
+	  if (  l1 > mLongEdgeD || l2 > mLongEdgeD || l3 > mLongEdgeD )
+		split = true;
+
+	}
+
+	if ( split )
+	{
+		uint32_t edge;
+
+		if ( l1 >= l2 && l1 >= l3 )
+			edge = 0;
+		else if ( l2 >= l1 && l2 >= l3 )
+			edge = 1;
+		else
+			edge = 2;
+
+			double splits[3];
+
+		switch ( edge )
+		{
+			case 0:
+				{
+			fm_lerp(p1,p2,splits,0.5);
+			tesselate(p1,splits,p3, recurse+1 );
+			tesselate(splits,p2,p3, recurse+1 );
+				}
+				break;
+			case 1:
+				{
+			fm_lerp(p2,p3,splits,0.5);
+			tesselate(p1,p2,splits, recurse+1 );
+			tesselate(p1,splits,p3, recurse+1 );
+				}
+				break;
+			case 2:
+				{
+					fm_lerp(p3,p1,splits,0.5);
+			tesselate(p1,p2,splits, recurse+1 );
+			tesselate(splits,p2,p3, recurse+1 );
+				}
+				break;
+		}
+	}
+	else
+	{
+	  bool newp;
+
+	  uint32_t i1 = mVertices->getIndex(p1,newp);
+	  uint32_t i2 = mVertices->getIndex(p2,newp);
+	  uint32_t i3 = mVertices->getIndex(p3,newp);
+
+	  mIndices.push_back(i1);
+	  mIndices.push_back(i2);
+	  mIndices.push_back(i3);
+	}
+
+  }
+
+private:
+  float           mLongEdge;
+  double          mLongEdgeD;
+  fm_VertexIndex *mVertices;
+  UintVector    mIndices;
+  uint32_t          mMaxDepth;
+};
+
+fm_Tesselate * fm_createTesselate(void)
+{
+  Myfm_Tesselate *m = new Myfm_Tesselate;
+  return static_cast< fm_Tesselate * >(m);
+}
+
+void           fm_releaseTesselate(fm_Tesselate *t)
+{
+  Myfm_Tesselate *m = static_cast< Myfm_Tesselate *>(t);
+  delete m;
+}
+
+#endif
+
+
+#ifndef RAY_ABB_INTERSECT
+
+#define RAY_ABB_INTERSECT
+
+//! Integer representation of a floating-point value.
+#define IR(x)	((uint32_t&)x)
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+*	A method to compute a ray-AABB intersection.
+*	Original code by Andrew Woo, from "Graphics Gems", Academic Press, 1990
+*	Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500)
+*	Epsilon value added by Klaus Hartmann. (discarding it saves a few cycles only)
+*
+*	Hence this version is faster as well as more robust than the original one.
+*
+*	Should work provided:
+*	1) the integer representation of 0.0f is 0x00000000
+*	2) the sign bit of the float is the most significant one
+*
+*	Report bugs: [email protected]
+*
+*	\param		aabb		[in] the axis-aligned bounding box
+*	\param		origin		[in] ray origin
+*	\param		dir			[in] ray direction
+*	\param		coord		[out] impact coordinates
+*	\return		true if ray intersects AABB
+*/
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#define RAYAABB_EPSILON 0.00001f
+bool fm_intersectRayAABB(const float MinB[3],const float MaxB[3],const float origin[3],const float dir[3],float coord[3])
+{
+  bool Inside = true;
+  float MaxT[3];
+  MaxT[0]=MaxT[1]=MaxT[2]=-1.0f;
+
+  // Find candidate planes.
+  for(uint32_t i=0;i<3;i++)
+  {
+	if(origin[i] < MinB[i])
+	{
+	  coord[i]	= MinB[i];
+	  Inside		= false;
+
+	  // Calculate T distances to candidate planes
+	  if(IR(dir[i]))	MaxT[i] = (MinB[i] - origin[i]) / dir[i];
+	}
+	else if(origin[i] > MaxB[i])
+	{
+	  coord[i]	= MaxB[i];
+	  Inside		= false;
+
+	  // Calculate T distances to candidate planes
+	  if(IR(dir[i]))	MaxT[i] = (MaxB[i] - origin[i]) / dir[i];
+	}
+  }
+
+  // Ray origin inside bounding box
+  if(Inside)
+  {
+	coord[0] = origin[0];
+	coord[1] = origin[1];
+	coord[2] = origin[2];
+	return true;
+  }
+
+  // Get largest of the maxT's for final choice of intersection
+  uint32_t WhichPlane = 0;
+  if(MaxT[1] > MaxT[WhichPlane])	WhichPlane = 1;
+  if(MaxT[2] > MaxT[WhichPlane])	WhichPlane = 2;
+
+  // Check final candidate actually inside box
+  if(IR(MaxT[WhichPlane])&0x80000000) return false;
+
+  for(uint32_t i=0;i<3;i++)
+  {
+	if(i!=WhichPlane)
+	{
+	  coord[i] = origin[i] + MaxT[WhichPlane] * dir[i];
+#ifdef RAYAABB_EPSILON
+	  if(coord[i] < MinB[i] - RAYAABB_EPSILON || coord[i] > MaxB[i] + RAYAABB_EPSILON)	return false;
+#else
+	  if(coord[i] < MinB[i] || coord[i] > MaxB[i])	return false;
+#endif
+	}
+  }
+  return true;	// ray hits box
+}
+
+bool fm_intersectLineSegmentAABB(const float bmin[3],const float bmax[3],const float p1[3],const float p2[3],float intersect[3])
+{
+  bool ret = false;
+
+  float dir[3];
+  dir[0] = p2[0] - p1[0];
+  dir[1] = p2[1] - p1[1];
+  dir[2] = p2[2] - p1[2];
+  float dist = fm_normalize(dir);
+  if ( dist > RAYAABB_EPSILON )
+  {
+	ret = fm_intersectRayAABB(bmin,bmax,p1,dir,intersect);
+	if ( ret )
+	{
+	  float d = fm_distanceSquared(p1,intersect);
+	  if ( d  > (dist*dist) )
+	  {
+		ret = false;
+	  }
+	}
+  }
+  return ret;
+}
+
+#endif
+
+#ifndef OBB_TO_AABB
+
+#define OBB_TO_AABB
+
+#pragma warning(disable:4100)
+
+void    fm_OBBtoAABB(const float /*obmin*/[3],const float /*obmax*/[3],const float /*matrix*/[16],float /*abmin*/[3],float /*abmax*/[3])
+{
+  assert(0); // not yet implemented.
+}
+
+
+const REAL * computePos(uint32_t index,const REAL *vertices,uint32_t vstride)
+{
+  const char *tmp = (const char *)vertices;
+  tmp+=(index*vstride);
+  return (const REAL*)tmp;
+}
+
+void computeNormal(uint32_t index,REAL *normals,uint32_t nstride,const REAL *normal)
+{
+  char *tmp = (char *)normals;
+  tmp+=(index*nstride);
+  REAL *dest = (REAL *)tmp;
+  dest[0]+=normal[0];
+  dest[1]+=normal[1];
+  dest[2]+=normal[2];
+}
+
+void fm_computeMeanNormals(uint32_t vcount,       // the number of vertices
+						   const REAL *vertices,     // the base address of the vertex position data.
+						   uint32_t vstride,      // the stride between position data.
+						   REAL *normals,            // the base address  of the destination for mean vector normals
+						   uint32_t nstride,      // the stride between normals
+						   uint32_t tcount,       // the number of triangles
+						   const uint32_t *indices)     // the triangle indices
+{
+
+  // Step #1 : Zero out the vertex normals
+  char *dest = (char *)normals;
+  for (uint32_t i=0; i<vcount; i++)
+  {
+	REAL *n = (REAL *)dest;
+	n[0] = 0;
+	n[1] = 0;
+	n[2] = 0;
+	dest+=nstride;
+  }
+
+  // Step #2 : Compute the face normals and accumulate them
+  const uint32_t *scan = indices;
+  for (uint32_t i=0; i<tcount; i++)
+  {
+
+	uint32_t i1 = *scan++;
+	uint32_t i2 = *scan++;
+	uint32_t i3 = *scan++;
+
+	const REAL *p1 = computePos(i1,vertices,vstride);
+	const REAL *p2 = computePos(i2,vertices,vstride);
+	const REAL *p3 = computePos(i3,vertices,vstride);
+
+	REAL normal[3];
+	fm_computePlane(p3,p2,p1,normal);
+
+	computeNormal(i1,normals,nstride,normal);
+	computeNormal(i2,normals,nstride,normal);
+	computeNormal(i3,normals,nstride,normal);
+  }
+
+
+  // Normalize the accumulated normals
+  dest = (char *)normals;
+  for (uint32_t i=0; i<vcount; i++)
+  {
+	REAL *n = (REAL *)dest;
+	fm_normalize(n);
+	dest+=nstride;
+  }
+
+}
+
+#endif
+
+
+#define BIGNUMBER 100000000.0  		/* hundred million */
+
+static inline void Set(REAL *n,REAL x,REAL y,REAL z)
+{
+	n[0] = x;
+	n[1] = y;
+	n[2] = z;
+};
+
+static inline void Copy(REAL *dest,const REAL *source)
+{
+	dest[0] = source[0];
+	dest[1] = source[1];
+	dest[2] = source[2];
+}
+
+
+REAL  fm_computeBestFitSphere(uint32_t vcount,const REAL *points,uint32_t pstride,REAL *center)
+{
+	REAL radius;
+	REAL radius2;
+
+	REAL xmin[3];
+	REAL xmax[3];
+	REAL ymin[3];
+	REAL ymax[3];
+	REAL zmin[3];
+	REAL zmax[3];
+	REAL dia1[3];
+	REAL dia2[3];
+
+	/* FIRST PASS: find 6 minima/maxima points */
+	Set(xmin,BIGNUMBER,BIGNUMBER,BIGNUMBER);
+	Set(xmax,-BIGNUMBER,-BIGNUMBER,-BIGNUMBER);
+	Set(ymin,BIGNUMBER,BIGNUMBER,BIGNUMBER);
+	Set(ymax,-BIGNUMBER,-BIGNUMBER,-BIGNUMBER);
+	Set(zmin,BIGNUMBER,BIGNUMBER,BIGNUMBER);
+	Set(zmax,-BIGNUMBER,-BIGNUMBER,-BIGNUMBER);
+
+	{
+		const char *scan = (const char *)points;
+		for (uint32_t i=0; i<vcount; i++)
+		{
+			const REAL *caller_p = (const REAL *)scan;
+			if (caller_p[0]<xmin[0])
+				Copy(xmin,caller_p); /* New xminimum point */
+			if (caller_p[0]>xmax[0])
+				Copy(xmax,caller_p);
+			if (caller_p[1]<ymin[1])
+				Copy(ymin,caller_p);
+			if (caller_p[1]>ymax[1])
+				Copy(ymax,caller_p);
+			if (caller_p[2]<zmin[2])
+				Copy(zmin,caller_p);
+			if (caller_p[2]>zmax[2])
+				Copy(zmax,caller_p);
+			scan+=pstride;
+		}
+	}
+
+	/* Set xspan = distance between the 2 points xmin & xmax (squared) */
+	REAL dx = xmax[0] - xmin[0];
+	REAL dy = xmax[1] - xmin[1];
+	REAL dz = xmax[2] - xmin[2];
+	REAL xspan = dx*dx + dy*dy + dz*dz;
+
+/* Same for y & z spans */
+	dx = ymax[0] - ymin[0];
+	dy = ymax[1] - ymin[1];
+	dz = ymax[2] - ymin[2];
+	REAL yspan = dx*dx + dy*dy + dz*dz;
+
+	dx = zmax[0] - zmin[0];
+	dy = zmax[1] - zmin[1];
+	dz = zmax[2] - zmin[2];
+	REAL zspan = dx*dx + dy*dy + dz*dz;
+
+	/* Set points dia1 & dia2 to the maximally separated pair */
+	Copy(dia1,xmin);
+	Copy(dia2,xmax); /* assume xspan biggest */
+	REAL maxspan = xspan;
+
+	if (yspan>maxspan)
+	{
+		maxspan = yspan;
+		Copy(dia1,ymin);
+		Copy(dia2,ymax);
+	}
+
+	if (zspan>maxspan)
+	{
+		maxspan = zspan;
+		Copy(dia1,zmin);
+		Copy(dia2,zmax);
+	}
+
+
+	/* dia1,dia2 is a diameter of initial sphere */
+	/* calc initial center */
+	center[0] = (dia1[0]+dia2[0])*0.5f;
+	center[1] = (dia1[1]+dia2[1])*0.5f;
+	center[2] = (dia1[2]+dia2[2])*0.5f;
+
+	/* calculate initial radius**2 and radius */
+
+	dx = dia2[0]-center[0]; /* x component of radius vector */
+	dy = dia2[1]-center[1]; /* y component of radius vector */
+	dz = dia2[2]-center[2]; /* z component of radius vector */
+
+	radius2 = dx*dx + dy*dy + dz*dz;
+	radius = REAL(sqrt(radius2));
+
+	/* SECOND PASS: increment current sphere */
+	{
+		const char *scan = (const char *)points;
+		for (uint32_t i=0; i<vcount; i++)
+		{
+			const REAL *caller_p = (const REAL *)scan;
+			dx = caller_p[0]-center[0];
+			dy = caller_p[1]-center[1];
+			dz = caller_p[2]-center[2];
+			REAL old_to_p_sq = dx*dx + dy*dy + dz*dz;
+			if (old_to_p_sq > radius2) 	/* do r**2 test first */
+			{ 	/* this point is outside of current sphere */
+				REAL old_to_p = REAL(sqrt(old_to_p_sq));
+				/* calc radius of new sphere */
+				radius = (radius + old_to_p) * 0.5f;
+				radius2 = radius*radius; 	/* for next r**2 compare */
+				REAL old_to_new = old_to_p - radius;
+				/* calc center of new sphere */
+				REAL recip = 1.0f /old_to_p;
+				REAL cx = (radius*center[0] + old_to_new*caller_p[0]) * recip;
+				REAL cy = (radius*center[1] + old_to_new*caller_p[1]) * recip;
+				REAL cz = (radius*center[2] + old_to_new*caller_p[2]) * recip;
+				Set(center,cx,cy,cz);
+				scan+=pstride;
+			}
+		}
+	}
+	return radius;
+}
+
+
+void fm_computeBestFitCapsule(uint32_t vcount,const REAL *points,uint32_t pstride,REAL &radius,REAL &height,REAL matrix[16],bool bruteForce)
+{
+  REAL sides[3];
+  REAL omatrix[16];
+  fm_computeBestFitOBB(vcount,points,pstride,sides,omatrix,bruteForce);
+
+  int32_t axis = 0;
+  if ( sides[0] > sides[1] && sides[0] > sides[2] )
+	axis = 0;
+  else if ( sides[1] > sides[0] && sides[1] > sides[2] )
+	axis = 1;
+  else 
+	axis = 2;
+
+  REAL localTransform[16];
+
+  REAL maxDist = 0;
+  REAL maxLen = 0;
+
+  switch ( axis )
+  {
+	case 0:
+	  {
+		fm_eulerMatrix(0,0,FM_PI/2,localTransform);
+		fm_matrixMultiply(localTransform,omatrix,matrix);
+
+		const uint8_t *scan = (const uint8_t *)points;
+		for (uint32_t i=0; i<vcount; i++)
+		{
+		  const REAL *p = (const REAL *)scan;
+		  REAL t[3];
+		  fm_inverseRT(omatrix,p,t);
+		  REAL dist = t[1]*t[1]+t[2]*t[2];
+		  if ( dist > maxDist )
+		  {
+			maxDist = dist;
+		  }
+		  REAL l = (REAL) fabs(t[0]);
+		  if ( l > maxLen )
+		  {
+			maxLen = l;
+		  }
+		  scan+=pstride;
+		}
+	  }
+	  height = sides[0];
+	  break;
+	case 1:
+	  {
+		fm_eulerMatrix(0,FM_PI/2,0,localTransform);
+		fm_matrixMultiply(localTransform,omatrix,matrix);
+
+		const uint8_t *scan = (const uint8_t *)points;
+		for (uint32_t i=0; i<vcount; i++)
+		{
+		  const REAL *p = (const REAL *)scan;
+		  REAL t[3];
+		  fm_inverseRT(omatrix,p,t);
+		  REAL dist = t[0]*t[0]+t[2]*t[2];
+		  if ( dist > maxDist )
+		  {
+			maxDist = dist;
+		  }
+		  REAL l = (REAL) fabs(t[1]);
+		  if ( l > maxLen )
+		  {
+			maxLen = l;
+		  }
+		  scan+=pstride;
+		}
+	  }
+	  height = sides[1];
+	  break;
+	case 2:
+	  {
+		fm_eulerMatrix(FM_PI/2,0,0,localTransform);
+		fm_matrixMultiply(localTransform,omatrix,matrix);
+
+		const uint8_t *scan = (const uint8_t *)points;
+		for (uint32_t i=0; i<vcount; i++)
+		{
+		  const REAL *p = (const REAL *)scan;
+		  REAL t[3];
+		  fm_inverseRT(omatrix,p,t);
+		  REAL dist = t[0]*t[0]+t[1]*t[1];
+		  if ( dist > maxDist )
+		  {
+			maxDist = dist;
+		  }
+		  REAL l = (REAL) fabs(t[2]);
+		  if ( l > maxLen )
+		  {
+			maxLen = l;
+		  }
+		  scan+=pstride;
+		}
+	  }
+	  height = sides[2];
+	  break;
+  }
+  radius = (REAL)sqrt(maxDist);
+  height = (maxLen*2)-(radius*2);
+}
+
+
+//************* Triangulation
+
+#ifndef TRIANGULATE_H
+
+#define TRIANGULATE_H
+
+typedef uint32_t TU32;
+
+class TVec
+{
+public:
+	TVec(double _x,double _y,double _z) { x = _x; y = _y; z = _z; };
+	TVec(void) { };
+
+  double x;
+  double y;
+  double z;
+};
+
+typedef std::vector< TVec >  TVecVector;
+typedef std::vector< TU32 >  TU32Vector;
+
+class CTriangulator
+{
+public:
+	///     Default constructor
+	CTriangulator();
+
+	///     Default destructor
+	virtual ~CTriangulator();
+
+	///     Triangulates the contour
+	void triangulate(TU32Vector &indices);
+
+	///     Returns the given point in the triangulator array
+	inline TVec get(const TU32 id) { return mPoints[id]; }
+
+	virtual void reset(void)
+	{
+		mInputPoints.clear();
+		mPoints.clear();
+		mIndices.clear();
+	}
+
+	virtual void addPoint(double x,double y,double z)
+	{
+		TVec v(x,y,z);
+		// update bounding box...
+		if ( mInputPoints.empty() )
+		{
+			mMin = v;
+			mMax = v;
+		}
+		else
+		{
+			if ( x < mMin.x ) mMin.x = x;
+			if ( y < mMin.y ) mMin.y = y;
+			if ( z < mMin.z ) mMin.z = z;
+
+			if ( x > mMax.x ) mMax.x = x;
+			if ( y > mMax.y ) mMax.y = y;
+			if ( z > mMax.z ) mMax.z = z;
+		}
+		mInputPoints.push_back(v);
+	}
+
+	// Triangulation happens in 2d.  We could inverse transform the polygon around the normal direction, or we just use the two most significant axes
+	// Here we find the two longest axes and use them to triangulate.  Inverse transforming them would introduce more doubling point error and isn't worth it.
+	virtual uint32_t * triangulate(uint32_t &tcount,double epsilon)
+	{
+		uint32_t *ret = 0;
+		tcount = 0;
+		mEpsilon = epsilon;
+
+		if ( !mInputPoints.empty() )
+		{
+			mPoints.clear();
+
+		  double dx = mMax.x - mMin.x; // locate the first, second and third longest edges and store them in i1, i2, i3
+		  double dy = mMax.y - mMin.y;
+		  double dz = mMax.z - mMin.z;
+
+		  uint32_t i1,i2,i3;
+
+		  if ( dx > dy && dx > dz )
+		  {
+			  i1 = 0;
+			  if ( dy > dz )
+			  {
+				  i2 = 1;
+				  i3 = 2;
+			  }
+			  else
+			  {
+				  i2 = 2;
+				  i3 = 1;
+			  }
+		  }
+		  else if ( dy > dx && dy > dz )
+		  {
+			  i1 = 1;
+			  if ( dx > dz )
+			  {
+				  i2 = 0;
+				  i3 = 2;
+			  }
+			  else
+			  {
+				  i2 = 2;
+				  i3 = 0;
+			  }
+		  }
+		  else
+		  {
+			  i1 = 2;
+			  if ( dx > dy )
+			  {
+				  i2 = 0;
+				  i3 = 1;
+			  }
+			  else
+			  {
+				  i2 = 1;
+				  i3 = 0;
+			  }
+		  }
+
+		  uint32_t pcount = (uint32_t)mInputPoints.size();
+		  const double *points = &mInputPoints[0].x;
+		  for (uint32_t i=0; i<pcount; i++)
+		  {
+			TVec v( points[i1], points[i2], points[i3] );
+			mPoints.push_back(v);
+			points+=3;
+		  }
+
+		  mIndices.clear();
+		  triangulate(mIndices);
+		  tcount = (uint32_t)mIndices.size()/3;
+		  if ( tcount )
+		  {
+			  ret = &mIndices[0];
+		  }
+		}
+		return ret;
+	}
+
+	virtual const double * getPoint(uint32_t index)
+	{
+		return &mInputPoints[index].x;
+	}
+
+
+private:
+	double                  mEpsilon;
+	TVec                   mMin;
+	TVec                   mMax;
+	TVecVector             mInputPoints;
+	TVecVector             mPoints;
+	TU32Vector             mIndices;
+
+	///     Tests if a point is inside the given triangle
+	bool _insideTriangle(const TVec& A, const TVec& B, const TVec& C,const TVec& P);
+
+	///     Returns the area of the contour
+	double _area();
+
+	bool _snip(int32_t u, int32_t v, int32_t w, int32_t n, int32_t *V);
+
+	///     Processes the triangulation
+	void _process(TU32Vector &indices);
+
+};
+
+///     Default constructor
+CTriangulator::CTriangulator(void)
+{
+}
+
+///     Default destructor
+CTriangulator::~CTriangulator()
+{
+}
+
+///     Triangulates the contour
+void CTriangulator::triangulate(TU32Vector &indices)
+{
+	_process(indices);
+}
+
+///     Processes the triangulation
+void CTriangulator::_process(TU32Vector &indices)
+{
+	const int32_t n = (const int32_t)mPoints.size();
+	if (n < 3)
+		return;
+	int32_t *V = (int32_t *)malloc(sizeof(int32_t)*n);
+
+	bool flipped = false;
+
+	if (0.0f < _area())
+	{
+		for (int32_t v = 0; v < n; v++)
+			V[v] = v;
+	}
+	else
+	{
+		flipped = true;
+		for (int32_t v = 0; v < n; v++)
+			V[v] = (n - 1) - v;
+	}
+
+	int32_t nv = n;
+	int32_t count = 2 * nv;
+	for (int32_t m = 0, v = nv - 1; nv > 2;)
+	{
+		if (0 >= (count--))
+			return;
+
+		int32_t u = v;
+		if (nv <= u)
+			u = 0;
+		v = u + 1;
+		if (nv <= v)
+			v = 0;
+		int32_t w = v + 1;
+		if (nv <= w)
+			w = 0;
+
+		if (_snip(u, v, w, nv, V))
+		{
+			int32_t a, b, c, s, t;
+			a = V[u];
+			b = V[v];
+			c = V[w];
+			if ( flipped )
+			{
+				indices.push_back(a);
+				indices.push_back(b);
+				indices.push_back(c);
+			}
+			else
+			{
+				indices.push_back(c);
+				indices.push_back(b);
+				indices.push_back(a);
+			}
+			m++;
+			for (s = v, t = v + 1; t < nv; s++, t++)
+				V[s] = V[t];
+			nv--;
+			count = 2 * nv;
+		}
+	}
+
+	free(V);
+}
+
+///     Returns the area of the contour
+double CTriangulator::_area()
+{
+	int32_t n = (uint32_t)mPoints.size();
+	double A = 0.0f;
+	for (int32_t p = n - 1, q = 0; q < n; p = q++)
+	{
+		const TVec &pval = mPoints[p];
+		const TVec &qval = mPoints[q];
+		A += pval.x * qval.y - qval.x * pval.y;
+	}
+	A*=0.5f;
+	return A;
+}
+
+bool CTriangulator::_snip(int32_t u, int32_t v, int32_t w, int32_t n, int32_t *V)
+{
+	int32_t p;
+
+	const TVec &A = mPoints[ V[u] ];
+	const TVec &B = mPoints[ V[v] ];
+	const TVec &C = mPoints[ V[w] ];
+
+	if (mEpsilon > (((B.x - A.x) * (C.y - A.y)) - ((B.y - A.y) * (C.x - A.x))) )
+		return false;
+
+	for (p = 0; p < n; p++)
+	{
+		if ((p == u) || (p == v) || (p == w))
+			continue;
+		const TVec &P = mPoints[ V[p] ];
+		if (_insideTriangle(A, B, C, P))
+			return false;
+	}
+	return true;
+}
+
+///     Tests if a point is inside the given triangle
+bool CTriangulator::_insideTriangle(const TVec& A, const TVec& B, const TVec& C,const TVec& P)
+{
+	double ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy;
+	double cCROSSap, bCROSScp, aCROSSbp;
+
+	ax = C.x - B.x;  ay = C.y - B.y;
+	bx = A.x - C.x;  by = A.y - C.y;
+	cx = B.x - A.x;  cy = B.y - A.y;
+	apx = P.x - A.x;  apy = P.y - A.y;
+	bpx = P.x - B.x;  bpy = P.y - B.y;
+	cpx = P.x - C.x;  cpy = P.y - C.y;
+
+	aCROSSbp = ax * bpy - ay * bpx;
+	cCROSSap = cx * apy - cy * apx;
+	bCROSScp = bx * cpy - by * cpx;
+
+	return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f));
+}
+
+class Triangulate : public fm_Triangulate
+{
+public:
+  Triangulate(void)
+  {
+	mPointsFloat = 0;
+	mPointsDouble = 0;
+  }
+
+  virtual ~Triangulate(void)
+  {
+	reset();
+  }
+  void reset(void)
+  {
+	free(mPointsFloat);
+	free(mPointsDouble);
+	mPointsFloat = 0;
+	mPointsDouble = 0;
+  }
+
+  virtual const double *       triangulate3d(uint32_t pcount,
+											 const double *_points,
+											 uint32_t vstride,
+											 uint32_t &tcount,
+											 bool consolidate,
+											 double epsilon)
+  {
+	reset();
+
+	double *points = (double *)malloc(sizeof(double)*pcount*3);
+	if ( consolidate )
+	{
+	  pcount = fm_consolidatePolygon(pcount,_points,vstride,points,1-epsilon);
+	}
+	else
+	{
+	  double *dest = points;
+	  for (uint32_t i=0; i<pcount; i++)
+	  {
+		const double *src = fm_getPoint(_points,vstride,i);
+		dest[0] = src[0];
+		dest[1] = src[1];
+		dest[2] = src[2];
+		dest+=3;
+	  }
+	  vstride = sizeof(double)*3;
+	}
+
+	if ( pcount >= 3 )
+	{
+	  CTriangulator ct;
+	  for (uint32_t i=0; i<pcount; i++)
+	  {
+		const double *src = fm_getPoint(points,vstride,i);
+		ct.addPoint( src[0], src[1], src[2] );
+	  }
+	  uint32_t _tcount;
+	  uint32_t *indices = ct.triangulate(_tcount,epsilon);
+	  if ( indices )
+	  {
+		tcount = _tcount;
+		mPointsDouble = (double *)malloc(sizeof(double)*tcount*3*3);
+		double *dest = mPointsDouble;
+		for (uint32_t i=0; i<tcount; i++)
+		{
+		  uint32_t i1 = indices[i*3+0];
+		  uint32_t i2 = indices[i*3+1];
+		  uint32_t i3 = indices[i*3+2];
+		  const double *p1 = ct.getPoint(i1);
+		  const double *p2 = ct.getPoint(i2);
+		  const double *p3 = ct.getPoint(i3);
+
+		  dest[0] = p1[0];
+		  dest[1] = p1[1];
+		  dest[2] = p1[2];
+
+		  dest[3] = p2[0];
+		  dest[4] = p2[1];
+		  dest[5] = p2[2];
+
+		  dest[6] = p3[0];
+		  dest[7] = p3[1];
+		  dest[8] = p3[2];
+		  dest+=9;
+		}
+	  }
+	}
+	free(points);
+
+	return mPointsDouble;
+  }
+
+  virtual const float  *       triangulate3d(uint32_t pcount,
+											 const float  *points,
+											 uint32_t vstride,
+											 uint32_t &tcount,
+											 bool consolidate,
+											 float epsilon)
+  {
+	reset();
+
+	double *temp = (double *)malloc(sizeof(double)*pcount*3);
+	double *dest = temp;
+	for (uint32_t i=0; i<pcount; i++)
+	{
+	  const float *p = fm_getPoint(points,vstride,i);
+	  dest[0] = p[0];
+	  dest[1] = p[1];
+	  dest[2] = p[2];
+	  dest+=3;
+	}
+	const double *results = triangulate3d(pcount,temp,sizeof(double)*3,tcount,consolidate,epsilon);
+	if ( results )
+	{
+	  uint32_t fcount = tcount*3*3;
+	  mPointsFloat = (float *)malloc(sizeof(float)*tcount*3*3);
+	  for (uint32_t i=0; i<fcount; i++)
+	  {
+		mPointsFloat[i] = (float) results[i];
+	  }
+	  free(mPointsDouble);
+	  mPointsDouble = 0;
+	}
+	free(temp);
+
+	return mPointsFloat;
+  }
+
+private:
+  float *mPointsFloat;
+  double *mPointsDouble;
+};
+
+fm_Triangulate * fm_createTriangulate(void)
+{
+  Triangulate *t = new Triangulate;
+  return static_cast< fm_Triangulate *>(t);
+}
+
+void             fm_releaseTriangulate(fm_Triangulate *t)
+{
+  Triangulate *tt = static_cast< Triangulate *>(t);
+  delete tt;
+}
+
+#endif
+
+bool validDistance(const REAL *p1,const REAL *p2,REAL epsilon)
+{
+	bool ret = true;
+
+	REAL dx = p1[0] - p2[0];
+	REAL dy = p1[1] - p2[1];
+	REAL dz = p1[2] - p2[2];
+	REAL dist = dx*dx+dy*dy+dz*dz;
+	if ( dist < (epsilon*epsilon) )
+	{
+		ret = false;
+	}
+	return ret;
+}
+
+bool fm_isValidTriangle(const REAL *p1,const REAL *p2,const REAL *p3,REAL epsilon)
+{
+  bool ret = false;
+
+  if ( validDistance(p1,p2,epsilon) &&
+	   validDistance(p1,p3,epsilon) &&
+	   validDistance(p2,p3,epsilon) )
+  {
+
+	  REAL area = fm_computeArea(p1,p2,p3);
+	  if ( area > epsilon )
+	  {
+		REAL _vertices[3*3],vertices[64*3];
+
+		_vertices[0] = p1[0];
+		_vertices[1] = p1[1];
+		_vertices[2] = p1[2];
+
+		_vertices[3] = p2[0];
+		_vertices[4] = p2[1];
+		_vertices[5] = p2[2];
+
+		_vertices[6] = p3[0];
+		_vertices[7] = p3[1];
+		_vertices[8] = p3[2];
+
+		uint32_t pcount = fm_consolidatePolygon(3,_vertices,sizeof(REAL)*3,vertices,1-epsilon);
+		if ( pcount == 3 )
+		{
+		  ret = true;
+		}
+	  }
+  }
+  return ret;
+}
+
+
+void  fm_multiplyQuat(const REAL *left,const REAL *right,REAL *quat)
+{
+	REAL a,b,c,d;
+
+	a = left[3]*right[3] - left[0]*right[0] - left[1]*right[1] - left[2]*right[2];
+	b = left[3]*right[0] + right[3]*left[0] + left[1]*right[2] - right[1]*left[2];
+	c = left[3]*right[1] + right[3]*left[1] + left[2]*right[0] - right[2]*left[0];
+	d = left[3]*right[2] + right[3]*left[2] + left[0]*right[1] - right[0]*left[1];
+
+	quat[3] = a;
+	quat[0] = b;
+	quat[1] = c;
+	quat[2] = d;
+}
+
+bool  fm_computeCentroid(uint32_t vcount,     // number of input data points
+						 const REAL *points,     // starting address of points array.
+						 REAL *center)
+
+{
+	bool ret = false;
+	if ( vcount )
+	{
+		center[0] = 0;
+		center[1] = 0;
+		center[2] = 0;
+		const REAL *p = points;
+		for (uint32_t i=0; i<vcount; i++)
+		{
+			center[0]+=p[0];
+			center[1]+=p[1];
+			center[2]+=p[2];
+			p += 3;
+		}
+		REAL recip = 1.0f / (REAL)vcount;
+		center[0]*=recip;
+		center[1]*=recip;
+		center[2]*=recip;
+		ret = true;
+	}
+	return ret;
+}
+
+bool  fm_computeCentroid(uint32_t vcount,     // number of input data points
+	const REAL *points,     // starting address of points array.
+	uint32_t triCount,
+	const uint32_t *indices,
+	REAL *center)
+
+{
+	bool ret = false;
+	if (vcount)
+	{
+		center[0] = 0;
+		center[1] = 0;
+		center[2] = 0;
+
+		REAL numerator[3] = { 0, 0, 0 };
+		REAL denominator = 0;
+
+		for (uint32_t i = 0; i < triCount; i++)
+		{
+			uint32_t i1 = indices[i * 3 + 0];
+			uint32_t i2 = indices[i * 3 + 1];
+			uint32_t i3 = indices[i * 3 + 2];
+
+			const REAL *p1 = &points[i1 * 3];
+			const REAL *p2 = &points[i2 * 3];
+			const REAL *p3 = &points[i3 * 3];
+
+			// Compute the sum of the three positions
+			REAL sum[3];
+			sum[0] = p1[0] + p2[0] + p3[0];
+			sum[1] = p1[1] + p2[1] + p3[1];
+			sum[2] = p1[2] + p2[2] + p3[2];
+
+			// Compute the average of the three positions
+			sum[0] = sum[0] / 3;
+			sum[1] = sum[1] / 3;
+			sum[2] = sum[2] / 3;
+
+			// Compute the area of this triangle
+			REAL area = fm_computeArea(p1, p2, p3);
+
+			numerator[0]+= (sum[0] * area);
+			numerator[1]+= (sum[1] * area);
+			numerator[2]+= (sum[2] * area);
+
+			denominator += area;
+
+		}
+		REAL recip = 1 / denominator;
+		center[0] = numerator[0] * recip;
+		center[1] = numerator[1] * recip;
+		center[2] = numerator[2] * recip;
+		ret = true;
+	}
+	return ret;
+}
+
+
+#ifndef TEMPLATE_VEC3
+#define TEMPLATE_VEC3
+template <class Type> class Vec3
+{
+public:
+	Vec3(void)
+	{
+
+	}
+	Vec3(Type _x,Type _y,Type _z)
+	{
+		x = _x;
+		y = _y;
+		z = _z;
+	}
+	Type x;
+	Type y;
+	Type z;
+};
+#endif
+
+void fm_transformAABB(const REAL bmin[3],const REAL bmax[3],const REAL matrix[16],REAL tbmin[3],REAL tbmax[3])
+{
+	Vec3<REAL> box[8];
+	box[0] = Vec3< REAL >( bmin[0], bmin[1], bmin[2] );
+	box[1] = Vec3< REAL >( bmax[0], bmin[1], bmin[2] );
+	box[2] = Vec3< REAL >( bmax[0], bmax[1], bmin[2] );
+	box[3] = Vec3< REAL >( bmin[0], bmax[1], bmin[2] );
+	box[4] = Vec3< REAL >( bmin[0], bmin[1], bmax[2] );
+	box[5] = Vec3< REAL >( bmax[0], bmin[1], bmax[2] );
+	box[6] = Vec3< REAL >( bmax[0], bmax[1], bmax[2] );
+	box[7] = Vec3< REAL >( bmin[0], bmax[1], bmax[2] );
+	// transform all 8 corners of the box and then recompute a new AABB
+	for (unsigned int i=0; i<8; i++)
+	{
+		Vec3< REAL > &p = box[i];
+		fm_transform(matrix,&p.x,&p.x);
+		if ( i == 0 )
+		{
+			tbmin[0] = tbmax[0] = p.x;
+			tbmin[1] = tbmax[1] = p.y;
+			tbmin[2] = tbmax[2] = p.z;
+		}
+		else
+		{
+			if ( p.x < tbmin[0] ) tbmin[0] = p.x;
+			if ( p.y < tbmin[1] ) tbmin[1] = p.y;
+			if ( p.z < tbmin[2] ) tbmin[2] = p.z;
+			if ( p.x > tbmax[0] ) tbmax[0] = p.x;
+			if ( p.y > tbmax[1] ) tbmax[1] = p.y;
+			if ( p.z > tbmax[2] ) tbmax[2] = p.z;
+		}
+	}
+}
+
+REAL  fm_normalizeQuat(REAL n[4]) // normalize this quat
+{
+	REAL dx = n[0]*n[0];
+	REAL dy = n[1]*n[1];
+	REAL dz = n[2]*n[2];
+	REAL dw = n[3]*n[3];
+
+	REAL dist = dx*dx+dy*dy+dz*dz+dw*dw;
+
+	dist = (REAL)sqrt(dist);
+
+	REAL recip = 1.0f / dist;
+
+	n[0]*=recip;
+	n[1]*=recip;
+	n[2]*=recip;
+	n[3]*=recip;
+
+	return dist;
+}
+
+
+}; // end of namespace

+ 41 - 5
Engine/lib/openal-soft/.github/workflows/ci.yml

@@ -1,6 +1,6 @@
 name: CI
 
-on: [push]
+on: [push, pull_request]
 
 jobs:
   build:
@@ -14,6 +14,7 @@ jobs:
             name: "Win32-Release",
             os: windows-latest,
             cmake_opts: "-A Win32 \
+              -DALSOFT_TESTS=ON \
               -DALSOFT_BUILD_ROUTER=ON \
               -DALSOFT_REQUIRE_WINMM=ON \
               -DALSOFT_REQUIRE_DSOUND=ON \
@@ -24,6 +25,7 @@ jobs:
             name: "Win32-Debug",
             os: windows-latest,
             cmake_opts: "-A Win32 \
+              -DALSOFT_TESTS=ON \
               -DALSOFT_BUILD_ROUTER=ON \
               -DALSOFT_REQUIRE_WINMM=ON \
               -DALSOFT_REQUIRE_DSOUND=ON \
@@ -34,6 +36,7 @@ jobs:
             name: "Win64-Release",
             os: windows-latest,
             cmake_opts: "-A x64 \
+              -DALSOFT_TESTS=ON \
               -DALSOFT_BUILD_ROUTER=ON \
               -DALSOFT_REQUIRE_WINMM=ON \
               -DALSOFT_REQUIRE_DSOUND=ON \
@@ -44,16 +47,42 @@ jobs:
             name: "Win64-Debug",
             os: windows-latest,
             cmake_opts: "-A x64 \
+              -DALSOFT_TESTS=ON \
               -DALSOFT_BUILD_ROUTER=ON \
               -DALSOFT_REQUIRE_WINMM=ON \
               -DALSOFT_REQUIRE_DSOUND=ON \
               -DALSOFT_REQUIRE_WASAPI=ON",
             build_type: "Debug"
           }
+        - {
+            name: "Win64-UWP",
+            os: windows-latest,
+            cmake_opts: "-A x64 \
+              -DALSOFT_TESTS=OFF \
+              -DCMAKE_SYSTEM_NAME=WindowsStore \
+              \"-DCMAKE_SYSTEM_VERSION=10.0\" \
+              -DALSOFT_BUILD_ROUTER=ON \
+              -DALSOFT_REQUIRE_WASAPI=ON",
+            build_type: "Release"
+          }
         - {
             name: "macOS-Release",
             os: macos-latest,
-            cmake_opts: "-DALSOFT_REQUIRE_COREAUDIO=ON",
+            cmake_opts: "-DALSOFT_REQUIRE_COREAUDIO=ON \
+              -DALSOFT_TESTS=ON",
+            build_type: "Release"
+          }
+        - {
+            name: "iOS-Release",
+            os: macos-latest,
+            cmake_opts: "-GXcode \
+            -DCMAKE_SYSTEM_NAME=iOS \
+            -DALSOFT_REQUIRE_COREAUDIO=ON \
+            -DALSOFT_UTILS=OFF \
+            -DALSOFT_EXAMPLES=OFF \
+            -DALSOFT_TESTS=OFF \
+            -DALSOFT_INSTALL=OFF \
+            \"-DCMAKE_OSX_ARCHITECTURES=arm64\"",
             build_type: "Release"
           }
         - {
@@ -65,7 +94,8 @@ jobs:
               -DALSOFT_REQUIRE_PORTAUDIO=ON \
               -DALSOFT_REQUIRE_PULSEAUDIO=ON \
               -DALSOFT_REQUIRE_JACK=ON \
-              -DALSOFT_REQUIRE_PIPEWIRE=ON",
+              -DALSOFT_REQUIRE_PIPEWIRE=ON \
+              -DALSOFT_TESTS=ON",
             deps_cmdline: "sudo apt update && sudo apt-get install -qq \
               libpulse-dev \
               portaudio19-dev \
@@ -78,7 +108,7 @@ jobs:
           }
 
     steps:
-    - uses: actions/checkout@v1
+    - uses: actions/checkout@v3
 
     - name: Install Dependencies
       shell: bash
@@ -97,6 +127,12 @@ jobs:
       run: |
         cmake --build build --config ${{matrix.config.build_type}}
 
+    - name: Test
+      shell: bash
+      run: |
+        cd build
+        ctest
+
     - name: Create Archive
       if: ${{ matrix.config.os == 'windows-latest' }}
       shell: bash
@@ -109,7 +145,7 @@ jobs:
 
     - name: Upload Archive
       # Upload package as an artifact of this workflow.
-      uses: actions/[email protected].1
+      uses: actions/[email protected].2
       if: ${{ matrix.config.os == 'windows-latest' }}
       with:
         name: soft_oal-${{matrix.config.name}}

+ 1 - 1
Engine/lib/openal-soft/.github/workflows/makemhr.yml

@@ -55,7 +55,7 @@ jobs:
         copy "libmysofa/windows/third-party/zlib-1.2.11/bin/zlib.dll" "Artifacts/zlib.dll"
 
     - name: Upload makemhr artifact
-      uses: actions/[email protected].1
+      uses: actions/[email protected].2
       with:
         name: makemhr
         path: "Artifacts/"

+ 1 - 0
Engine/lib/openal-soft/.gitignore

@@ -1,6 +1,7 @@
 build*/
 winbuild
 win64build
+.vs/
 
 ## kdevelop
 *.kdev4

+ 201 - 166
Engine/lib/openal-soft/CMakeLists.txt

@@ -1,6 +1,7 @@
 # CMake build file list for OpenAL
 
-cmake_minimum_required(VERSION 3.0.2)
+cmake_minimum_required(VERSION 3.13)
+enable_testing()
 
 if(APPLE)
     # The workaround for try_compile failing with code signing
@@ -28,11 +29,10 @@ if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
                 FORCE)
         endif()
     endif()
+elseif(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
+    set(ALSOFT_UWP TRUE)
 endif()
 
-set(CMAKE_C_VISIBILITY_PRESET hidden)
-set(CMAKE_CXX_VISIBILITY_PRESET hidden)
-
 if(COMMAND CMAKE_POLICY)
     cmake_policy(SET CMP0003 NEW)
     cmake_policy(SET CMP0005 NEW)
@@ -76,8 +76,8 @@ if(NOT CMAKE_DEBUG_POSTFIX)
 endif()
 
 set(DEFAULT_TARGET_PROPS
-    # Require C++14.
-    CXX_STANDARD 14
+    # Require C++17.
+    CXX_STANDARD 17
     CXX_STANDARD_REQUIRED TRUE
     # Prefer C11, but support C99 and earlier when possible.
     C_STANDARD 11)
@@ -109,6 +109,7 @@ option(ALSOFT_UTILS "Build utility programs"  ON)
 option(ALSOFT_NO_CONFIG_UTIL "Disable building the alsoft-config utility" OFF)
 
 option(ALSOFT_EXAMPLES  "Build example programs"  ON)
+option(ALSOFT_TESTS "Build test programs"  OFF)
 
 option(ALSOFT_INSTALL "Install main library" ON)
 option(ALSOFT_INSTALL_CONFIG "Install alsoft.conf sample configuration file" ON)
@@ -148,6 +149,8 @@ set(CPP_DEFS ) # C pre-processor, not C++
 set(INC_PATHS )
 set(C_FLAGS )
 set(LINKER_FLAGS )
+set(LINKER_FLAGS_DEBUG )
+set(LINKER_FLAGS_RELEASE )
 set(EXTRA_LIBS )
 
 if(WIN32)
@@ -165,7 +168,7 @@ elseif(APPLE)
 endif()
 
 
-# QNX's gcc do not uses /usr/include and /usr/lib pathes by default
+# QNX's gcc do not uses /usr/include and /usr/lib paths by default
 if("${CMAKE_C_PLATFORM_ID}" STREQUAL "QNX")
     set(INC_PATHS ${INC_PATHS} /usr/include)
     set(LINKER_FLAGS ${LINKER_FLAGS} -L/usr/lib)
@@ -186,29 +189,18 @@ set(LIB_VERSION_NUM ${LIB_MAJOR_VERSION},${LIB_MINOR_VERSION},${LIB_REVISION},0)
 set(EXPORT_DECL "")
 
 
-if(NOT WIN32)
-    # Check if _POSIX_C_SOURCE and _XOPEN_SOURCE needs to be set for POSIX functions
-    check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN_DEFAULT)
-    if(NOT HAVE_POSIX_MEMALIGN_DEFAULT)
-        set(OLD_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
-        set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=600")
-        check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN_POSIX)
-        if(NOT HAVE_POSIX_MEMALIGN_POSIX)
-            set(CMAKE_REQUIRED_FLAGS ${OLD_REQUIRED_FLAGS})
-        else()
-            set(CPP_DEFS ${CPP_DEFS} _POSIX_C_SOURCE=200112L _XOPEN_SOURCE=600)
-        endif()
-    endif()
-    unset(OLD_REQUIRED_FLAGS)
-endif()
-
-# C99 has restrict, but C++ does not, so we can only utilize __restrict.
-check_cxx_source_compiles("int *__restrict foo;
-int main() { return 0; }" HAVE___RESTRICT)
-if(HAVE___RESTRICT)
-    set(CPP_DEFS ${CPP_DEFS} RESTRICT=__restrict)
-else()
-    set(CPP_DEFS ${CPP_DEFS} "RESTRICT=")
+# Some systems erroneously require the __STDC_FORMAT_MACROS macro to be defined
+# to get the fixed-width integer type formatter macros.
+check_cxx_source_compiles("#include <cinttypes>
+#include <cstdio>
+int main()
+{
+    int64_t i64{};
+    std::printf(\"%\" PRId64, i64);
+}"
+HAVE_STDC_FORMAT_MACROS)
+if(NOT HAVE_STDC_FORMAT_MACROS)
+    set(CPP_DEFS ${CPP_DEFS} __STDC_FORMAT_MACROS)
 endif()
 
 # Some systems may need libatomic for atomic functions to work
@@ -278,6 +270,11 @@ else()
         endif()
     endif()
 
+    check_cxx_compiler_flag(-Wno-interference-size HAVE_WNO_INTERFERENCE_SIZE)
+    if(HAVE_WNO_INTERFERENCE_SIZE)
+        set(C_FLAGS ${C_FLAGS} $<$<COMPILE_LANGUAGE:CXX>:-Wno-interference-size>)
+    endif()
+
     if(ALSOFT_WERROR)
         set(C_FLAGS ${C_FLAGS} -Werror)
     endif()
@@ -312,7 +309,7 @@ else()
     option(ALSOFT_STATIC_STDCXX "Static link libstdc++" OFF)
     if(ALSOFT_STATIC_STDCXX)
         set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
-        set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-Wl,--push-state,-Bstatic,-lstdc++,--pop-state")
+        set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-static-libstdc++")
         check_cxx_source_compiles("int main() { }" HAVE_STATIC_LIBSTDCXX_SWITCH)
         set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES})
         unset(OLD_REQUIRED_LIBRARIES)
@@ -320,14 +317,14 @@ else()
         if(NOT HAVE_STATIC_LIBSTDCXX_SWITCH)
             message(FATAL_ERROR "Cannot static link libstdc++")
         endif()
-        set(LINKER_FLAGS ${LINKER_FLAGS} "-Wl,--push-state,-Bstatic,-lstdc++,--pop-state")
+        set(LINKER_FLAGS ${LINKER_FLAGS} "-static-libstdc++")
     endif()
 
     if(WIN32)
         option(ALSOFT_STATIC_WINPTHREAD "Static link libwinpthread" OFF)
         if(ALSOFT_STATIC_WINPTHREAD)
             set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
-            set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-Wl,--push-state,-Bstatic,-lwinpthread,--pop-state")
+            set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-Wl,--push-state,-Bstatic,-lstdc++,-lwinpthread,--pop-state")
             check_cxx_source_compiles("int main() { }" HAVE_STATIC_LIBWINPTHREAD_SWITCH)
             set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES})
             unset(OLD_REQUIRED_LIBRARIES)
@@ -335,38 +332,38 @@ else()
             if(NOT HAVE_STATIC_LIBWINPTHREAD_SWITCH)
                 message(FATAL_ERROR "Cannot static link libwinpthread")
             endif()
-            set(LINKER_FLAGS ${LINKER_FLAGS} "-Wl,--push-state,-Bstatic,-lwinpthread,--pop-state")
+            set(LINKER_FLAGS ${LINKER_FLAGS} "-Wl,--push-state,-Bstatic,-lstdc++,-lwinpthread,--pop-state")
         endif()
     endif()
 endif()
 
 # Set visibility/export options if available
-if(WIN32)
-    if(NOT LIBTYPE STREQUAL "STATIC")
+if(NOT LIBTYPE STREQUAL "STATIC")
+    if(WIN32)
         set(EXPORT_DECL "__declspec(dllexport)")
-    endif()
-else()
-    set(OLD_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
-    # Yes GCC, really don't accept visibility modes you don't support
-    set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS} -Wattributes -Werror")
-
-    check_c_source_compiles("int foo() __attribute__((visibility(\"protected\")));
-                             int main() {return 0;}" HAVE_GCC_PROTECTED_VISIBILITY)
-    if(HAVE_GCC_PROTECTED_VISIBILITY)
-        if(NOT LIBTYPE STREQUAL "STATIC")
-            set(EXPORT_DECL "__attribute__((visibility(\"protected\")))")
-        endif()
     else()
-        check_c_source_compiles("int foo() __attribute__((visibility(\"default\")));
-                                 int main() {return 0;}" HAVE_GCC_DEFAULT_VISIBILITY)
-        if(HAVE_GCC_DEFAULT_VISIBILITY)
-            if(NOT LIBTYPE STREQUAL "STATIC")
+        set(OLD_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
+        # Yes GCC, really don't accept visibility modes you don't support
+        set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS} -Wattributes -Werror")
+
+        check_c_source_compiles("int foo() __attribute__((visibility(\"protected\")));
+                                int main() {return 0;}" HAVE_GCC_PROTECTED_VISIBILITY)
+        if(HAVE_GCC_PROTECTED_VISIBILITY)
+            set(EXPORT_DECL "__attribute__((visibility(\"protected\")))")
+        else()
+            check_c_source_compiles("int foo() __attribute__((visibility(\"default\")));
+                                    int main() {return 0;}" HAVE_GCC_DEFAULT_VISIBILITY)
+            if(HAVE_GCC_DEFAULT_VISIBILITY)
                 set(EXPORT_DECL "__attribute__((visibility(\"default\")))")
             endif()
         endif()
-    endif()
+        if(HAVE_GCC_PROTECTED_VISIBILITY OR HAVE_GCC_DEFAULT_VISIBILITY)
+            set(CMAKE_C_VISIBILITY_PRESET hidden)
+            set(CMAKE_CXX_VISIBILITY_PRESET hidden)
+        endif()
 
-    set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}")
+        set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}")
+    endif()
 endif()
 
 
@@ -403,7 +400,7 @@ if(ALSOFT_CPUEXT_SSE AND HAVE_XMMINTRIN_H)
     set(HAVE_SSE 1)
 endif()
 if(ALSOFT_REQUIRE_SSE AND NOT HAVE_SSE)
-    message(FATAL_ERROR "Failed to enabled required SSE CPU extensions")
+    message(FATAL_ERROR "Failed to enable required SSE CPU extensions")
 endif()
 
 option(ALSOFT_CPUEXT_SSE2 "Enable SSE2 support" ON)
@@ -448,10 +445,11 @@ if(ALSOFT_CPUEXT_NEON AND HAVE_ARM_NEON_H)
     endif()
 endif()
 if(ALSOFT_REQUIRE_NEON AND NOT HAVE_NEON)
-    message(FATAL_ERROR "Failed to enabled required ARM NEON CPU extensions")
+    message(FATAL_ERROR "Failed to enable required ARM NEON CPU extensions")
 endif()
 
 
+set(ALSOFT_FORCE_ALIGN )
 set(SSE_FLAGS )
 set(FPMATH_SET "0")
 if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND HAVE_SSE2)
@@ -476,6 +474,7 @@ if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND HAVE_SSE2)
             # OSs don't guarantee this on 32-bit, so externally-callable
             # functions need to ensure an aligned stack.
             set(EXPORT_DECL "${EXPORT_DECL}__attribute__((force_align_arg_pointer))")
+            set(ALSOFT_FORCE_ALIGN "__attribute__((force_align_arg_pointer))")
         endif()
     endif()
 endif()
@@ -493,13 +492,9 @@ if(HAVE_SSE2)
 endif()
 
 
-check_include_file(malloc.h HAVE_MALLOC_H)
 check_include_file(cpuid.h HAVE_CPUID_H)
 check_include_file(intrin.h HAVE_INTRIN_H)
 check_include_file(guiddef.h HAVE_GUIDDEF_H)
-if(NOT HAVE_GUIDDEF_H)
-    check_include_file(initguid.h HAVE_INITGUID_H)
-endif()
 
 # Some systems need libm for some math functions to work
 set(MATH_LIB )
@@ -517,7 +512,7 @@ if(HAVE_LIBRT)
     set(RT_LIB rt)
 endif()
 
-# Check for the dlopen API (for dynamicly loading backend libs)
+# Check for the dlopen API (for dynamically loading backend libs)
 if(ALSOFT_DLOPEN)
     check_include_file(dlfcn.h HAVE_DLFCN_H)
     check_library_exists(dl dlopen "" HAVE_LIBDL)
@@ -546,8 +541,6 @@ if(HAVE_INTRIN_H)
         }" HAVE_CPUID_INTRINSIC)
 endif()
 
-check_symbol_exists(posix_memalign   stdlib.h HAVE_POSIX_MEMALIGN)
-check_symbol_exists(_aligned_malloc  malloc.h HAVE__ALIGNED_MALLOC)
 check_symbol_exists(proc_pidpath     libproc.h HAVE_PROC_PIDPATH)
 
 if(NOT WIN32)
@@ -582,34 +575,36 @@ if(NOT WIN32)
     endif()
 endif()
 
-check_symbol_exists(getopt unistd.h HAVE_GETOPT)
-
 
 # Common sources used by both the OpenAL implementation library, the OpenAL
 # router, and certain tools and examples.
 set(COMMON_OBJS
+    common/alassert.cpp
+    common/alassert.h
     common/albit.h
-    common/albyte.h
     common/alcomplex.cpp
     common/alcomplex.h
-    common/aldeque.h
-    common/alfstream.cpp
-    common/alfstream.h
-    common/almalloc.cpp
     common/almalloc.h
     common/alnumbers.h
     common/alnumeric.h
-    common/aloptional.h
+    common/alsem.cpp
+    common/alsem.h
     common/alspan.h
     common/alstring.cpp
     common/alstring.h
+    common/althrd_setname.cpp
+    common/althrd_setname.h
+    common/althreads.h
     common/altraits.h
     common/atomic.h
     common/comptr.h
     common/dynload.cpp
     common/dynload.h
+    common/flexarray.h
     common/intrusive_ptr.h
     common/opthelpers.h
+    common/pffft.cpp
+    common/pffft.h
     common/phase_shifter.h
     common/polyphase_resampler.cpp
     common/polyphase_resampler.h
@@ -618,8 +613,6 @@ set(COMMON_OBJS
     common/ringbuffer.h
     common/strutils.cpp
     common/strutils.h
-    common/threads.cpp
-    common/threads.h
     common/vecmat.h
     common/vector.h)
 
@@ -680,6 +673,8 @@ set(CORE_OBJS
     core/mixer.cpp
     core/mixer.h
     core/resampler_limits.h
+    core/storage_formats.cpp
+    core/storage_formats.h
     core/uhjfilter.cpp
     core/uhjfilter.h
     core/uiddefs.cpp
@@ -726,7 +721,7 @@ if(NOT WIN32)
     endif()
 endif()
 if(ALSOFT_REQUIRE_RTKIT AND NOT HAVE_RTKIT)
-    message(FATAL_ERROR "Failed to enabled required RTKit support")
+    message(FATAL_ERROR "Failed to enable required RTKit support")
 endif()
 
 # Default mixers, always available
@@ -742,6 +737,9 @@ set(OPENAL_OBJS
     al/auxeffectslot.h
     al/buffer.cpp
     al/buffer.h
+    al/debug.cpp
+    al/debug.h
+    al/direct_defs.h
     al/effect.cpp
     al/effect.h
     al/effects/autowah.cpp
@@ -761,6 +759,7 @@ set(OPENAL_OBJS
     al/effects/reverb.cpp
     al/effects/vmorpher.cpp
     al/error.cpp
+    al/error.h
     al/event.cpp
     al/event.h
     al/extension.cpp
@@ -798,6 +797,9 @@ set(ALC_OBJS
     alc/effects/pshifter.cpp
     alc/effects/reverb.cpp
     alc/effects/vmorpher.cpp
+    alc/events.cpp
+    alc/events.h
+    alc/export_list.h
     alc/inprogext.h
     alc/panning.cpp)
 
@@ -815,7 +817,6 @@ if(ALSOFT_EAX)
         al/eax/fx_slot_index.h
         al/eax/fx_slots.cpp
         al/eax/fx_slots.h
-        al/eax/globals.cpp
         al/eax/globals.h
         al/eax/utils.cpp
         al/eax/utils.h
@@ -899,7 +900,7 @@ if(ALSOFT_BACKEND_PIPEWIRE AND PkgConfig_FOUND)
     endif()
 endif()
 if(ALSOFT_REQUIRE_PIPEWIRE AND NOT HAVE_PIPEWIRE)
-    message(FATAL_ERROR "Failed to enabled required PipeWire backend")
+    message(FATAL_ERROR "Failed to enable required PipeWire backend")
 endif()
 
 # Check PulseAudio backend
@@ -916,7 +917,7 @@ if(ALSOFT_BACKEND_PULSEAUDIO)
     endif()
 endif()
 if(ALSOFT_REQUIRE_PULSEAUDIO AND NOT HAVE_PULSEAUDIO)
-    message(FATAL_ERROR "Failed to enabled required PulseAudio backend")
+    message(FATAL_ERROR "Failed to enable required PulseAudio backend")
 endif()
 
 if(NOT WIN32)
@@ -963,66 +964,72 @@ if(NOT WIN32)
         endif()
     endif()
 
-    # Check SndIO backend
-    option(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" ON)
+    # Check SndIO backend (disabled by default on non-BSDs)
+    if(BSD)
+        option(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" ON)
+    else()
+        option(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" OFF)
+    endif()
     option(ALSOFT_REQUIRE_SNDIO "Require SndIO backend" OFF)
     if(ALSOFT_BACKEND_SNDIO)
-        find_package(SoundIO)
-        if(SOUNDIO_FOUND)
+        find_package(SndIO)
+        if(SNDIO_FOUND)
             set(HAVE_SNDIO 1)
             set(BACKENDS  "${BACKENDS} SndIO (linked),")
             set(ALC_OBJS  ${ALC_OBJS} alc/backends/sndio.cpp alc/backends/sndio.h)
-            set(EXTRA_LIBS ${SOUNDIO_LIBRARIES} ${EXTRA_LIBS})
-            set(INC_PATHS ${INC_PATHS} ${SOUNDIO_INCLUDE_DIRS})
+            set(EXTRA_LIBS ${SNDIO_LIBRARIES} ${EXTRA_LIBS})
+            set(INC_PATHS ${INC_PATHS} ${SNDIO_INCLUDE_DIRS})
         endif()
     endif()
 endif()
 if(ALSOFT_REQUIRE_ALSA AND NOT HAVE_ALSA)
-    message(FATAL_ERROR "Failed to enabled required ALSA backend")
+    message(FATAL_ERROR "Failed to enable required ALSA backend")
 endif()
 if(ALSOFT_REQUIRE_OSS AND NOT HAVE_OSS)
-    message(FATAL_ERROR "Failed to enabled required OSS backend")
+    message(FATAL_ERROR "Failed to enable required OSS backend")
 endif()
 if(ALSOFT_REQUIRE_SOLARIS AND NOT HAVE_SOLARIS)
-    message(FATAL_ERROR "Failed to enabled required Solaris backend")
+    message(FATAL_ERROR "Failed to enable required Solaris backend")
 endif()
 if(ALSOFT_REQUIRE_SNDIO AND NOT HAVE_SNDIO)
-    message(FATAL_ERROR "Failed to enabled required SndIO backend")
+    message(FATAL_ERROR "Failed to enable required SndIO backend")
 endif()
 
 # Check Windows-only backends
 if(WIN32)
-    # Check MMSystem backend
-    option(ALSOFT_BACKEND_WINMM "Enable Windows Multimedia backend" ON)
-    option(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF)
-    if(ALSOFT_BACKEND_WINMM)
-        set(HAVE_WINMM 1)
-        set(BACKENDS "${BACKENDS} WinMM,")
-        set(ALC_OBJS ${ALC_OBJS} alc/backends/winmm.cpp alc/backends/winmm.h)
-        # There doesn't seem to be good way to search for winmm.lib for MSVC.
-        # find_library doesn't find it without being told to look in a specific
-        # place in the WindowsSDK, but it links anyway. If there ends up being
-        # Windows targets without this, another means to detect it is needed.
-        set(EXTRA_LIBS winmm ${EXTRA_LIBS})
-    endif()
-
-    # Check DSound backend
-    option(ALSOFT_BACKEND_DSOUND "Enable DirectSound backend" ON)
-    option(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF)
-    if(ALSOFT_BACKEND_DSOUND)
-        check_include_file(dsound.h HAVE_DSOUND_H)
-        if(DXSDK_DIR)
-            find_path(DSOUND_INCLUDE_DIR NAMES "dsound.h"
-                PATHS "${DXSDK_DIR}" PATH_SUFFIXES include
-                DOC "The DirectSound include directory")
+    if (NOT ALSOFT_UWP)
+        # Check MMSystem backend
+        option(ALSOFT_BACKEND_WINMM "Enable Windows Multimedia backend" ON)
+        option(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF)
+        if(ALSOFT_BACKEND_WINMM)
+            set(HAVE_WINMM 1)
+            set(BACKENDS "${BACKENDS} WinMM,")
+            set(ALC_OBJS ${ALC_OBJS} alc/backends/winmm.cpp alc/backends/winmm.h)
+            # There doesn't seem to be good way to search for winmm.lib for MSVC.
+            # find_library doesn't find it without being told to look in a specific
+            # place in the WindowsSDK, but it links anyway. If there ends up being
+            # Windows targets without this, another means to detect it is needed.
+            set(EXTRA_LIBS winmm ${EXTRA_LIBS})
         endif()
-        if(HAVE_DSOUND_H OR DSOUND_INCLUDE_DIR)
-            set(HAVE_DSOUND 1)
-            set(BACKENDS "${BACKENDS} DirectSound,")
-            set(ALC_OBJS ${ALC_OBJS} alc/backends/dsound.cpp alc/backends/dsound.h)
 
-            if(NOT HAVE_DSOUND_H)
-                set(INC_PATHS ${INC_PATHS} ${DSOUND_INCLUDE_DIR})
+        # Check DSound backend
+        option(ALSOFT_BACKEND_DSOUND "Enable DirectSound backend" ON)
+        option(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF)
+        if(ALSOFT_BACKEND_DSOUND)
+            check_include_file(dsound.h HAVE_DSOUND_H)
+            if(DXSDK_DIR)
+                find_path(DSOUND_INCLUDE_DIR NAMES "dsound.h"
+                    PATHS "${DXSDK_DIR}" PATH_SUFFIXES include
+                    DOC "The DirectSound include directory")
+            endif()
+            if(HAVE_DSOUND_H OR DSOUND_INCLUDE_DIR)
+                set(HAVE_DSOUND 1)
+                set(BACKENDS "${BACKENDS} DirectSound,")
+                set(ALC_OBJS ${ALC_OBJS} alc/backends/dsound.cpp alc/backends/dsound.h)
+
+                if(NOT HAVE_DSOUND_H)
+                    set(INC_PATHS ${INC_PATHS} ${DSOUND_INCLUDE_DIR})
+                endif()
             endif()
         endif()
     endif()
@@ -1040,13 +1047,13 @@ if(WIN32)
     endif()
 endif()
 if(ALSOFT_REQUIRE_WINMM AND NOT HAVE_WINMM)
-    message(FATAL_ERROR "Failed to enabled required WinMM backend")
+    message(FATAL_ERROR "Failed to enable required WinMM backend")
 endif()
 if(ALSOFT_REQUIRE_DSOUND AND NOT HAVE_DSOUND)
-    message(FATAL_ERROR "Failed to enabled required DSound backend")
+    message(FATAL_ERROR "Failed to enable required DSound backend")
 endif()
 if(ALSOFT_REQUIRE_WASAPI AND NOT HAVE_WASAPI)
-    message(FATAL_ERROR "Failed to enabled required WASAPI backend")
+    message(FATAL_ERROR "Failed to enable required WASAPI backend")
 endif()
 
 # Check JACK backend
@@ -1063,7 +1070,7 @@ if(ALSOFT_BACKEND_JACK)
     endif()
 endif()
 if(ALSOFT_REQUIRE_JACK AND NOT HAVE_JACK)
-    message(FATAL_ERROR "Failed to enabled required JACK backend")
+    message(FATAL_ERROR "Failed to enable required JACK backend")
 endif()
 
 # Check CoreAudio backend
@@ -1098,7 +1105,7 @@ if(ALSOFT_BACKEND_COREAUDIO)
     endif()
 endif()
 if(ALSOFT_REQUIRE_COREAUDIO AND NOT HAVE_COREAUDIO)
-    message(FATAL_ERROR "Failed to enabled required CoreAudio backend")
+    message(FATAL_ERROR "Failed to enable required CoreAudio backend")
 endif()
 
 # Check for Oboe (Android) backend
@@ -1130,7 +1137,7 @@ if(ALSOFT_BACKEND_OBOE)
     endif()
 endif()
 if(ALSOFT_REQUIRE_OBOE AND NOT HAVE_OBOE)
-    message(FATAL_ERROR "Failed to enabled required Oboe backend")
+    message(FATAL_ERROR "Failed to enable required Oboe backend")
 endif()
 
 # Check for OpenSL (Android) backend
@@ -1142,11 +1149,12 @@ if(ALSOFT_BACKEND_OPENSL)
         set(HAVE_OPENSL 1)
         set(ALC_OBJS  ${ALC_OBJS} alc/backends/opensl.cpp alc/backends/opensl.h)
         set(BACKENDS  "${BACKENDS} OpenSL,")
-        set(EXTRA_LIBS "OpenSL::OpenSLES" ${EXTRA_LIBS})
+        set(EXTRA_LIBS ${OPENSL_LIBRARIES} ${EXTRA_LIBS})
+        set(INC_PATHS ${INC_PATHS} ${OPENSL_INCLUDE_DIRS})
     endif()
 endif()
 if(ALSOFT_REQUIRE_OPENSL AND NOT HAVE_OPENSL)
-    message(FATAL_ERROR "Failed to enabled required OpenSL backend")
+    message(FATAL_ERROR "Failed to enable required OpenSL backend")
 endif()
 
 # Check PortAudio backend
@@ -1163,7 +1171,7 @@ if(ALSOFT_BACKEND_PORTAUDIO)
     endif()
 endif()
 if(ALSOFT_REQUIRE_PORTAUDIO AND NOT HAVE_PORTAUDIO)
-    message(FATAL_ERROR "Failed to enabled required PortAudio backend")
+    message(FATAL_ERROR "Failed to enable required PortAudio backend")
 endif()
 
 # Check for SDL2 backend
@@ -1181,7 +1189,7 @@ if(ALSOFT_BACKEND_SDL2)
     endif()
 endif()
 if(ALSOFT_REQUIRE_SDL2 AND NOT SDL2_FOUND)
-    message(FATAL_ERROR "Failed to enabled required SDL2 backend")
+    message(FATAL_ERROR "Failed to enable required SDL2 backend")
 endif()
 
 # Optionally enable the Wave Writer backend
@@ -1309,11 +1317,12 @@ configure_file(
     @ONLY)
 
 
-add_library(common STATIC EXCLUDE_FROM_ALL ${COMMON_OBJS})
-target_include_directories(common PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/include)
-target_compile_definitions(common PRIVATE ${CPP_DEFS})
-target_compile_options(common PRIVATE ${C_FLAGS})
-set_target_properties(common PROPERTIES ${DEFAULT_TARGET_PROPS} POSITION_INDEPENDENT_CODE TRUE)
+add_library(alcommon STATIC EXCLUDE_FROM_ALL ${COMMON_OBJS})
+target_include_directories(alcommon PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/include
+    PUBLIC ${OpenAL_SOURCE_DIR}/common)
+target_compile_definitions(alcommon PRIVATE ${CPP_DEFS})
+target_compile_options(alcommon PRIVATE ${C_FLAGS})
+set_target_properties(alcommon PROPERTIES ${DEFAULT_TARGET_PROPS} POSITION_INDEPENDENT_CODE TRUE)
 
 
 unset(HAS_ROUTER)
@@ -1348,7 +1357,7 @@ else()
             PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}"
             "AL_API=${EXPORT_DECL}" ${CPP_DEFS})
         target_compile_options(OpenAL PRIVATE ${C_FLAGS})
-        target_link_libraries(OpenAL PRIVATE common ${LINKER_FLAGS})
+        target_link_libraries(OpenAL PRIVATE alcommon ${LINKER_FLAGS})
         target_include_directories(OpenAL
           PUBLIC
             $<BUILD_INTERFACE:${OpenAL_SOURCE_DIR}/include>
@@ -1379,13 +1388,31 @@ else()
     if(WIN32)
         set_target_properties(${IMPL_TARGET} PROPERTIES PREFIX "")
     endif()
-    target_link_libraries(${IMPL_TARGET} PRIVATE common ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB})
+    target_link_libraries(${IMPL_TARGET} PRIVATE alcommon ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB})
+
+    if(ALSOFT_UWP)
+        set(ALSOFT_CPPWINRT_VERSION "2.0.230706.1" CACHE STRING "The soft-oal default cppwinrt version")
+        
+        find_program(NUGET_EXE NAMES nuget)
+        if(NOT NUGET_EXE)
+            message("NUGET.EXE not found.")
+            message(FATAL_ERROR "Please install this executable, and run CMake again.")
+        endif()
+
+        exec_program(${NUGET_EXE}
+            ARGS install "Microsoft.Windows.CppWinRT" -Version ${ALSOFT_CPPWINRT_VERSION} -ExcludeVersion -OutputDirectory "\"${CMAKE_BINARY_DIR}/packages\"")
+
+        set_target_properties(${IMPL_TARGET} PROPERTIES
+            VS_PROJECT_IMPORT ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT/build/native/Microsoft.Windows.CppWinRT.props
+        )
+        target_link_libraries(${IMPL_TARGET} PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT/build/native/Microsoft.Windows.CppWinRT.targets)
+    endif()
 
     if(NOT WIN32 AND NOT APPLE)
         # FIXME: This doesn't put a dependency on the version script. Changing
         # the version script will not cause a relink as it should.
-        set_property(TARGET ${IMPL_TARGET} APPEND_STRING PROPERTY
-            LINK_FLAGS " -Wl,--version-script=${OpenAL_SOURCE_DIR}/libopenal.version")
+        target_link_options(${IMPL_TARGET} PRIVATE
+            "-Wl,--version-script=${OpenAL_SOURCE_DIR}/libopenal.version")
     endif()
 
     if(APPLE AND ALSOFT_OSX_FRAMEWORK)
@@ -1443,8 +1470,8 @@ set_target_properties(${IMPL_TARGET} PROPERTIES ${DEFAULT_TARGET_PROPS}
     SOVERSION ${LIB_MAJOR_VERSION}
 )
 target_compile_definitions(${IMPL_TARGET}
-    PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}" "AL_API=${EXPORT_DECL}"
-    ${CPP_DEFS})
+    PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES $<$<BOOL:${ALSOFT_EAX}>:ALSOFT_EAX>
+    "ALC_API=${EXPORT_DECL}" "AL_API=${EXPORT_DECL}" ${CPP_DEFS})
 target_compile_options(${IMPL_TARGET} PRIVATE ${C_FLAGS})
 
 if(TARGET build_version)
@@ -1462,8 +1489,7 @@ if(WIN32 AND MINGW AND ALSOFT_BUILD_IMPORT_LIB AND NOT LIBTYPE STREQUAL "STATIC"
             message(STATUS "WARNING: Cannot find dlltool, disabling .def/.lib generation")
         endif()
     else()
-        set_property(TARGET OpenAL APPEND_STRING PROPERTY
-            LINK_FLAGS " -Wl,--output-def,OpenAL32.def")
+        target_link_options(OpenAL PRIVATE "-Wl,--output-def,${PROJECT_BINARY_DIR}/OpenAL32.def")
         add_custom_command(TARGET OpenAL POST_BUILD
             COMMAND "${SED_EXECUTABLE}" -i -e "s/ @[^ ]*//" OpenAL32.def
             COMMAND "${CMAKE_DLLTOOL}" -d OpenAL32.def -l OpenAL32.lib -D OpenAL32.dll
@@ -1494,7 +1520,10 @@ if(FPMATH_SET)
     message(STATUS "Building with SSE${FPMATH_SET} codegen")
     message(STATUS "")
 endif()
-
+if(ALSOFT_UWP)
+    message(STATUS "Building with UWP support")
+    message(STATUS "")
+endif()
 if(ALSOFT_EAX)
     message(STATUS "Building with legacy EAX extension support")
     message(STATUS "")
@@ -1580,7 +1609,7 @@ if(ALSOFT_UTILS)
         target_include_directories(uhjdecoder
             PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common)
         target_compile_options(uhjdecoder PRIVATE ${C_FLAGS})
-        target_link_libraries(uhjdecoder PUBLIC common
+        target_link_libraries(uhjdecoder PUBLIC alcommon
             PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG})
         set_target_properties(uhjdecoder PROPERTIES ${DEFAULT_TARGET_PROPS})
 
@@ -1589,7 +1618,7 @@ if(ALSOFT_UTILS)
         target_include_directories(uhjencoder
             PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common)
         target_compile_options(uhjencoder PRIVATE ${C_FLAGS})
-        target_link_libraries(uhjencoder PUBLIC common
+        target_link_libraries(uhjencoder PUBLIC alcommon
             PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG})
         set_target_properties(uhjencoder PROPERTIES ${DEFAULT_TARGET_PROPS})
     endif()
@@ -1602,7 +1631,7 @@ if(ALSOFT_UTILS)
         target_compile_definitions(sofa-support PRIVATE ${CPP_DEFS})
         target_include_directories(sofa-support PUBLIC ${OpenAL_SOURCE_DIR}/common)
         target_compile_options(sofa-support PRIVATE ${C_FLAGS})
-        target_link_libraries(sofa-support PUBLIC common MySOFA::MySOFA PRIVATE ${LINKER_FLAGS})
+        target_link_libraries(sofa-support PUBLIC alcommon MySOFA::MySOFA PRIVATE ${LINKER_FLAGS})
         set_target_properties(sofa-support PROPERTIES ${DEFAULT_TARGET_PROPS})
 
         set(MAKEMHR_SRCS
@@ -1612,9 +1641,6 @@ if(ALSOFT_UTILS)
             utils/makemhr/loadsofa.h
             utils/makemhr/makemhr.cpp
             utils/makemhr/makemhr.h)
-        if(NOT HAVE_GETOPT)
-            set(MAKEMHR_SRCS  ${MAKEMHR_SRCS} utils/getopt.c utils/getopt.h)
-        endif()
         add_executable(makemhr ${MAKEMHR_SRCS})
         target_compile_definitions(makemhr PRIVATE ${CPP_DEFS})
         target_include_directories(makemhr
@@ -1644,22 +1670,22 @@ endif()
 
 
 # Add a static library with common functions used by multiple example targets
-add_library(ex-common STATIC EXCLUDE_FROM_ALL
+add_library(al-excommon STATIC EXCLUDE_FROM_ALL
     examples/common/alhelpers.c
     examples/common/alhelpers.h)
-target_compile_definitions(ex-common PUBLIC ${CPP_DEFS})
-target_include_directories(ex-common PUBLIC ${OpenAL_SOURCE_DIR}/common)
-target_compile_options(ex-common PUBLIC ${C_FLAGS})
-target_link_libraries(ex-common PUBLIC OpenAL PRIVATE ${RT_LIB})
-set_target_properties(ex-common PROPERTIES ${DEFAULT_TARGET_PROPS})
+target_compile_definitions(al-excommon PUBLIC ${CPP_DEFS})
+target_include_directories(al-excommon PUBLIC ${OpenAL_SOURCE_DIR}/common)
+target_compile_options(al-excommon PUBLIC ${C_FLAGS})
+target_link_libraries(al-excommon PUBLIC OpenAL PRIVATE ${RT_LIB})
+set_target_properties(al-excommon PROPERTIES ${DEFAULT_TARGET_PROPS})
 
 if(ALSOFT_EXAMPLES)
     add_executable(altonegen examples/altonegen.c)
-    target_link_libraries(altonegen PRIVATE ${LINKER_FLAGS} ${MATH_LIB} ex-common ${UNICODE_FLAG})
+    target_link_libraries(altonegen PRIVATE ${LINKER_FLAGS} ${MATH_LIB} al-excommon ${UNICODE_FLAG})
     set_target_properties(altonegen PROPERTIES ${DEFAULT_TARGET_PROPS})
 
     add_executable(alrecord examples/alrecord.c)
-    target_link_libraries(alrecord PRIVATE ${LINKER_FLAGS} ex-common ${UNICODE_FLAG})
+    target_link_libraries(alrecord PRIVATE ${LINKER_FLAGS} al-excommon ${UNICODE_FLAG})
     set_target_properties(alrecord PROPERTIES ${DEFAULT_TARGET_PROPS})
 
     if(ALSOFT_INSTALL_EXAMPLES)
@@ -1670,48 +1696,53 @@ if(ALSOFT_EXAMPLES)
 
     if(SNDFILE_FOUND)
         add_executable(alplay examples/alplay.c)
-        target_link_libraries(alplay PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common
+        target_link_libraries(alplay PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
             ${UNICODE_FLAG})
         set_target_properties(alplay PROPERTIES ${DEFAULT_TARGET_PROPS})
 
         add_executable(alstream examples/alstream.c)
-        target_link_libraries(alstream PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common
+        target_link_libraries(alstream PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
             ${UNICODE_FLAG})
         set_target_properties(alstream PROPERTIES ${DEFAULT_TARGET_PROPS})
 
         add_executable(alreverb examples/alreverb.c)
-        target_link_libraries(alreverb PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common
+        target_link_libraries(alreverb PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
             ${UNICODE_FLAG})
         set_target_properties(alreverb PROPERTIES ${DEFAULT_TARGET_PROPS})
 
         add_executable(almultireverb examples/almultireverb.c)
         target_link_libraries(almultireverb
-            PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common ${MATH_LIB} ${UNICODE_FLAG})
+            PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon ${MATH_LIB} ${UNICODE_FLAG})
         set_target_properties(almultireverb PROPERTIES ${DEFAULT_TARGET_PROPS})
 
         add_executable(allatency examples/allatency.c)
-        target_link_libraries(allatency PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common
+        target_link_libraries(allatency PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
             ${UNICODE_FLAG})
         set_target_properties(allatency PROPERTIES ${DEFAULT_TARGET_PROPS})
 
         add_executable(alhrtf examples/alhrtf.c)
         target_link_libraries(alhrtf
-            PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common ${MATH_LIB} ${UNICODE_FLAG})
+            PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon ${MATH_LIB} ${UNICODE_FLAG})
         set_target_properties(alhrtf PROPERTIES ${DEFAULT_TARGET_PROPS})
 
         add_executable(alstreamcb examples/alstreamcb.cpp)
-        target_link_libraries(alstreamcb PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common
+        target_link_libraries(alstreamcb PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
             ${UNICODE_FLAG})
         set_target_properties(alstreamcb PROPERTIES ${DEFAULT_TARGET_PROPS})
 
-        add_executable(alconvolve examples/alconvolve.c)
-        target_link_libraries(alconvolve PRIVATE ${LINKER_FLAGS} common SndFile::SndFile ex-common
+        add_executable(aldirect examples/aldirect.cpp)
+        target_link_libraries(aldirect PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
             ${UNICODE_FLAG})
+        set_target_properties(aldirect PROPERTIES ${DEFAULT_TARGET_PROPS})
+
+        add_executable(alconvolve examples/alconvolve.c)
+        target_link_libraries(alconvolve PRIVATE ${LINKER_FLAGS} alcommon SndFile::SndFile
+            al-excommon ${UNICODE_FLAG})
         set_target_properties(alconvolve PROPERTIES ${DEFAULT_TARGET_PROPS})
 
         if(ALSOFT_INSTALL_EXAMPLES)
             set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alplay alstream alreverb almultireverb allatency
-                alhrtf)
+                alhrtf aldirect)
         endif()
 
         message(STATUS "Building SndFile example programs")
@@ -1720,7 +1751,7 @@ if(ALSOFT_EXAMPLES)
     if(SDL2_FOUND)
         add_executable(alloopback examples/alloopback.c)
         target_link_libraries(alloopback
-            PRIVATE ${LINKER_FLAGS} SDL2::SDL2 ex-common ${MATH_LIB})
+            PRIVATE ${LINKER_FLAGS} SDL2::SDL2 al-excommon ${MATH_LIB})
         set_target_properties(alloopback PROPERTIES ${DEFAULT_TARGET_PROPS})
 
         if(ALSOFT_INSTALL_EXAMPLES)
@@ -1757,7 +1788,7 @@ if(ALSOFT_EXAMPLES)
             add_executable(alffplay examples/alffplay.cpp)
             target_include_directories(alffplay PRIVATE ${FFMPEG_INCLUDE_DIRS})
             target_link_libraries(alffplay
-                PRIVATE ${LINKER_FLAGS} SDL2::SDL2 ${FFMPEG_LIBRARIES} ex-common)
+                PRIVATE ${LINKER_FLAGS} SDL2::SDL2 ${FFMPEG_LIBRARIES} al-excommon)
             set_target_properties(alffplay PROPERTIES ${DEFAULT_TARGET_PROPS})
 
             if(ALSOFT_INSTALL_EXAMPLES)
@@ -1769,6 +1800,10 @@ if(ALSOFT_EXAMPLES)
     message(STATUS "")
 endif()
 
+if (ALSOFT_TESTS)
+add_subdirectory(tests)
+endif()
+
 if(EXTRA_INSTALLS)
     install(TARGETS ${EXTRA_INSTALLS}
         RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}

+ 38 - 0
Engine/lib/openal-soft/LICENSE-pffft

@@ -0,0 +1,38 @@
+A modified PFFFT is included, with the following license.
+
+Copyright (c) 2023  Christopher Robinson
+
+Copyright (c) 2013  Julien Pommier ( [email protected] )
+
+Copyright (c) 2004 the University Corporation for Atmospheric
+Research ("UCAR"). All rights reserved. Developed by NCAR's
+Computational and Information Systems Laboratory, UCAR,
+www.cisl.ucar.edu.
+
+Redistribution and use of the Software in source and binary forms,
+with or without modification, is permitted provided that the
+following conditions are met:
+
+- Neither the names of NCAR's Computational and Information Systems
+Laboratory, the University Corporation for Atmospheric Research,
+nor the names of its sponsors or contributors may be used to
+endorse or promote products derived from this Software without
+specific prior written permission.
+
+- Redistributions of source code must retain the above copyright
+notices, this list of conditions, and the disclaimer below.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions, and the disclaimer below in the
+documentation and/or other materials provided with the
+distribution.
+
+THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+SOFTWARE.

+ 1 - 1
Engine/lib/openal-soft/OpenALConfig.cmake.in

@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.1...3.18)
 
 include("${CMAKE_CURRENT_LIST_DIR}/OpenALTargets.cmake")
 

+ 27 - 0
Engine/lib/openal-soft/README.md

@@ -64,6 +64,33 @@ as application-agnostic behavior of the library. See alsoftrc.sample for
 available settings.
 
 
+Language Bindings
+-----------------
+
+As a C API, OpenAL Soft can be used directly by any language that can use
+functions with C linkage. For languages that can't directly use C-style
+headers, bindings may be developed to allow code written in that language to
+call into the library. Some bindings for some languages are listed here.
+
+C# Bindings:
+* [OpenTK](https://opentk.net/) includes low-level C# bindings for the OpenAL
+API, including some extensions. It also includes utility libraries for math and
+linear algebra, which can be useful for 3D calculations.
+
+Java Bindings:
+* [JOAL](https://jogamp.org/joal/www/), part of the JogAmp project, includes
+Java bindings for the OpenAL API, usable with OpenAL Soft. It also includes a
+higher level Sound3D Toolkit API and utility functions to make easier use of
+OpenAL features and capabilities.
+
+Python Bindings:
+* [PyOpenAL](https://pypi.org/project/PyOpenAL/). Also includes methods to play
+wave files and, with PyOgg, also Vorbis, Opus, and FLAC.
+
+Other bindings for these and other languages also exist. This list will grow as
+more bindings are found.
+
+
 Acknowledgements
 ----------------
 

File diff suppressed because it is too large
+ 366 - 403
Engine/lib/openal-soft/al/auxeffectslot.cpp


+ 59 - 41
Engine/lib/openal-soft/al/auxeffectslot.h

@@ -1,23 +1,24 @@
 #ifndef AL_AUXEFFECTSLOT_H
 #define AL_AUXEFFECTSLOT_H
 
+#include <array>
 #include <atomic>
-#include <cstddef>
+#include <cstdint>
+#include <string_view>
+#include <utility>
 
 #include "AL/al.h"
 #include "AL/alc.h"
-#include "AL/efx.h"
 
-#include "alc/device.h"
-#include "alc/effects/base.h"
 #include "almalloc.h"
-#include "atomic.h"
+#include "alnumeric.h"
+#include "core/effects/base.h"
 #include "core/effectslot.h"
 #include "intrusive_ptr.h"
-#include "vector.h"
 
 #ifdef ALSOFT_EAX
 #include <memory>
+#include "eax/api.h"
 #include "eax/call.h"
 #include "eax/effect.h"
 #include "eax/exception.h"
@@ -26,8 +27,6 @@
 #endif // ALSOFT_EAX
 
 struct ALbuffer;
-struct ALeffect;
-struct WetBuffer;
 
 #ifdef ALSOFT_EAX
 class EaxFxSlotException : public EaxException {
@@ -38,30 +37,30 @@ public:
 };
 #endif // ALSOFT_EAX
 
-enum class SlotState : ALenum {
-    Initial = AL_INITIAL,
-    Playing = AL_PLAYING,
-    Stopped = AL_STOPPED,
+enum class SlotState : bool {
+    Initial, Playing,
 };
 
 struct ALeffectslot {
+    ALuint EffectId{};
     float Gain{1.0f};
     bool  AuxSendAuto{true};
     ALeffectslot *Target{nullptr};
     ALbuffer *Buffer{nullptr};
 
-    struct {
+    struct EffectData {
         EffectSlotType Type{EffectSlotType::None};
         EffectProps Props{};
 
         al::intrusive_ptr<EffectState> State;
-    } Effect;
+    };
+    EffectData Effect;
 
     bool mPropsDirty{true};
 
     SlotState mState{SlotState::Initial};
 
-    RefCount ref{0u};
+    std::atomic<ALuint> ref{0u};
 
     EffectSlot *mSlot{nullptr};
 
@@ -73,23 +72,23 @@ struct ALeffectslot {
     ALeffectslot& operator=(const ALeffectslot&) = delete;
     ~ALeffectslot();
 
-    ALenum initEffect(ALenum effectType, const EffectProps &effectProps, ALCcontext *context);
-    void updateProps(ALCcontext *context);
+    ALenum initEffect(ALuint effectId, ALenum effectType, const EffectProps &effectProps,
+        ALCcontext *context);
+    void updateProps(ALCcontext *context) const;
 
-    /* This can be new'd for the context's default effect slot. */
-    DEF_NEWDEL(ALeffectslot)
+    static void SetName(ALCcontext *context, ALuint id, std::string_view name);
 
 
 #ifdef ALSOFT_EAX
 public:
     void eax_initialize(ALCcontext& al_context, EaxFxSlotIndexValue index);
 
-    EaxFxSlotIndexValue eax_get_index() const noexcept { return eax_fx_slot_index_; }
-    const EAX50FXSLOTPROPERTIES& eax_get_eax_fx_slot() const noexcept
+    [[nodiscard]] auto eax_get_index() const noexcept -> EaxFxSlotIndexValue { return eax_fx_slot_index_; }
+    [[nodiscard]] auto eax_get_eax_fx_slot() const noexcept -> const EAX50FXSLOTPROPERTIES&
     { return eax_; }
 
     // Returns `true` if all sources should be updated, or `false` otherwise.
-    bool eax_dispatch(const EaxCall& call)
+    [[nodiscard]] auto eax_dispatch(const EaxCall& call) -> bool
     { return call.is_get() ? eax_get(call) : eax_set(call); }
 
     void eax_commit();
@@ -193,6 +192,17 @@ private:
         }
     };
 
+    struct Eax5FlagsValidator {
+        void operator()(unsigned long ulFlags) const
+        {
+            EaxRangeValidator{}(
+                "Flags",
+                ulFlags,
+                0UL,
+                ~EAX50FXSLOTFLAGS_RESERVED);
+        }
+    };
+
     struct Eax5OcclusionValidator {
         void operator()(long lOcclusion) const
         {
@@ -215,21 +225,13 @@ private:
         }
     };
 
-    struct Eax5FlagsValidator {
-        void operator()(unsigned long ulFlags) const
-        {
-            EaxRangeValidator{}(
-                "Flags",
-                ulFlags,
-                0UL,
-                ~EAX50FXSLOTFLAGS_RESERVED);
-        }
-    };
-
     struct Eax5AllValidator {
         void operator()(const EAX50FXSLOTPROPERTIES& all) const
         {
-            Eax4AllValidator{}(static_cast<const EAX40FXSLOTPROPERTIES&>(all));
+            Eax4GuidLoadEffectValidator{}(all.guidLoadEffect);
+            Eax4VolumeValidator{}(all.lVolume);
+            Eax4LockValidator{}(all.lLock);
+            Eax5FlagsValidator{}(all.ulFlags);
             Eax5OcclusionValidator{}(all.lOcclusion);
             Eax5OcclusionLfRatioValidator{}(all.flOcclusionLFRatio);
         }
@@ -277,14 +279,14 @@ private:
         dst = src;
     }
 
-    constexpr bool eax4_fx_slot_is_legacy() const noexcept
+    [[nodiscard]] constexpr auto eax4_fx_slot_is_legacy() const noexcept -> bool
     { return eax_fx_slot_index_ < 2; }
 
     void eax4_fx_slot_ensure_unlocked() const;
 
-    static ALenum eax_get_efx_effect_type(const GUID& guid);
-    const GUID& eax_get_eax_default_effect_guid() const noexcept;
-    long eax_get_eax_default_lock() const noexcept;
+    [[nodiscard]] static auto eax_get_efx_effect_type(const GUID& guid) -> ALenum;
+    [[nodiscard]] auto eax_get_eax_default_effect_guid() const noexcept -> const GUID&;
+    [[nodiscard]] auto eax_get_eax_default_lock() const noexcept -> long;
 
     void eax4_fx_slot_set_defaults(Eax4Props& props) noexcept;
     void eax5_fx_slot_set_defaults(Eax5Props& props) noexcept;
@@ -293,8 +295,8 @@ private:
     void eax_fx_slot_set_current_defaults();
     void eax_fx_slot_set_defaults();
 
-    void eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props) const;
-    void eax5_fx_slot_get(const EaxCall& call, const Eax5Props& props) const;
+    static void eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props);
+    static void eax5_fx_slot_get(const EaxCall& call, const Eax5Props& props);
     void eax_fx_slot_get(const EaxCall& call) const;
     // Returns `true` if all sources should be updated, or `false` otherwise.
     bool eax_get(const EaxCall& call);
@@ -307,7 +309,7 @@ private:
     void eax4_fx_slot_set_all(const EaxCall& call);
     void eax5_fx_slot_set_all(const EaxCall& call);
 
-    bool eax_fx_slot_should_update_sources() const noexcept;
+    [[nodiscard]] auto eax_fx_slot_should_update_sources() const noexcept -> bool;
 
     // Returns `true` if all sources should be updated, or `false` otherwise.
     bool eax4_fx_slot_set(const EaxCall& call);
@@ -365,4 +367,20 @@ EaxAlEffectSlotUPtr eax_create_al_effect_slot(ALCcontext& context);
 void eax_delete_al_effect_slot(ALCcontext& context, ALeffectslot& effect_slot);
 #endif // ALSOFT_EAX
 
+struct EffectSlotSubList {
+    uint64_t FreeMask{~0_u64};
+    gsl::owner<std::array<ALeffectslot,64>*> EffectSlots{nullptr};
+
+    EffectSlotSubList() noexcept = default;
+    EffectSlotSubList(const EffectSlotSubList&) = delete;
+    EffectSlotSubList(EffectSlotSubList&& rhs) noexcept
+      : FreeMask{rhs.FreeMask}, EffectSlots{rhs.EffectSlots}
+    { rhs.FreeMask = ~0_u64; rhs.EffectSlots = nullptr; }
+    ~EffectSlotSubList();
+
+    EffectSlotSubList& operator=(const EffectSlotSubList&) = delete;
+    EffectSlotSubList& operator=(EffectSlotSubList&& rhs) noexcept
+    { std::swap(FreeMask, rhs.FreeMask); std::swap(EffectSlots, rhs.EffectSlots); return *this; }
+};
+
 #endif

File diff suppressed because it is too large
+ 410 - 428
Engine/lib/openal-soft/al/buffer.cpp


+ 27 - 7
Engine/lib/openal-soft/al/buffer.h

@@ -1,20 +1,23 @@
 #ifndef AL_BUFFER_H
 #define AL_BUFFER_H
 
+#include <array>
 #include <atomic>
+#include <cstddef>
+#include <cstdint>
+#include <string_view>
+#include <utility>
 
 #include "AL/al.h"
+#include "AL/alc.h"
 
-#include "albyte.h"
 #include "alc/inprogext.h"
 #include "almalloc.h"
-#include "atomic.h"
+#include "alnumeric.h"
 #include "core/buffer_storage.h"
 #include "vector.h"
 
 #ifdef ALSOFT_EAX
-#include "eax/x_ram.h"
-
 enum class EaxStorage : uint8_t {
     Automatic,
     Accessible,
@@ -26,7 +29,7 @@ enum class EaxStorage : uint8_t {
 struct ALbuffer : public BufferStorage {
     ALbitfieldSOFT Access{0u};
 
-    al::vector<al::byte,16> mDataStorage;
+    al::vector<std::byte,16> mDataStorage;
 
     ALuint OriginalSize{0};
 
@@ -42,12 +45,14 @@ struct ALbuffer : public BufferStorage {
     ALuint mLoopEnd{0u};
 
     /* Number of times buffer was attached to a source (deletion can only occur when 0) */
-    RefCount ref{0u};
+    std::atomic<ALuint> ref{0u};
 
     /* Self ID */
     ALuint id{0};
 
-    DISABLE_ALLOC()
+    static void SetName(ALCcontext *context, ALuint id, std::string_view name);
+
+    DISABLE_ALLOC
 
 #ifdef ALSOFT_EAX
     EaxStorage eax_x_ram_mode{EaxStorage::Automatic};
@@ -55,4 +60,19 @@ struct ALbuffer : public BufferStorage {
 #endif // ALSOFT_EAX
 };
 
+struct BufferSubList {
+    uint64_t FreeMask{~0_u64};
+    gsl::owner<std::array<ALbuffer,64>*> Buffers{nullptr};
+
+    BufferSubList() noexcept = default;
+    BufferSubList(const BufferSubList&) = delete;
+    BufferSubList(BufferSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Buffers{rhs.Buffers}
+    { rhs.FreeMask = ~0_u64; rhs.Buffers = nullptr; }
+    ~BufferSubList();
+
+    BufferSubList& operator=(const BufferSubList&) = delete;
+    BufferSubList& operator=(BufferSubList&& rhs) noexcept
+    { std::swap(FreeMask, rhs.FreeMask); std::swap(Buffers, rhs.Buffers); return *this; }
+};
+
 #endif

+ 618 - 0
Engine/lib/openal-soft/al/debug.cpp

@@ -0,0 +1,618 @@
+#include "config.h"
+
+#include "debug.h"
+
+#include <algorithm>
+#include <array>
+#include <atomic>
+#include <cstring>
+#include <deque>
+#include <mutex>
+#include <optional>
+#include <stdexcept>
+#include <string>
+#include <string_view>
+#include <unordered_map>
+#include <utility>
+
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "AL/alext.h"
+
+#include "alc/context.h"
+#include "alc/device.h"
+#include "alc/inprogext.h"
+#include "alnumeric.h"
+#include "alspan.h"
+#include "alstring.h"
+#include "auxeffectslot.h"
+#include "buffer.h"
+#include "core/logging.h"
+#include "core/voice.h"
+#include "direct_defs.h"
+#include "effect.h"
+#include "error.h"
+#include "filter.h"
+#include "intrusive_ptr.h"
+#include "opthelpers.h"
+#include "source.h"
+
+
+/* Declared here to prevent compilers from thinking it should be inlined, which
+ * GCC warns about increasing code size.
+ */
+DebugGroup::~DebugGroup() = default;
+
+namespace {
+
+static_assert(DebugSeverityBase+DebugSeverityCount <= 32, "Too many debug bits");
+
+template<typename T, T ...Vals>
+constexpr auto make_array_sequence(std::integer_sequence<T, Vals...>)
+{ return std::array<T,sizeof...(Vals)>{Vals...}; }
+
+template<typename T, size_t N>
+constexpr auto make_array_sequence()
+{ return make_array_sequence(std::make_integer_sequence<T,N>{}); }
+
+
+constexpr auto GetDebugSource(ALenum source) noexcept -> std::optional<DebugSource>
+{
+    switch(source)
+    {
+    case AL_DEBUG_SOURCE_API_EXT: return DebugSource::API;
+    case AL_DEBUG_SOURCE_AUDIO_SYSTEM_EXT: return DebugSource::System;
+    case AL_DEBUG_SOURCE_THIRD_PARTY_EXT: return DebugSource::ThirdParty;
+    case AL_DEBUG_SOURCE_APPLICATION_EXT: return DebugSource::Application;
+    case AL_DEBUG_SOURCE_OTHER_EXT: return DebugSource::Other;
+    }
+    return std::nullopt;
+}
+
+constexpr auto GetDebugType(ALenum type) noexcept -> std::optional<DebugType>
+{
+    switch(type)
+    {
+    case AL_DEBUG_TYPE_ERROR_EXT: return DebugType::Error;
+    case AL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_EXT: return DebugType::DeprecatedBehavior;
+    case AL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_EXT: return DebugType::UndefinedBehavior;
+    case AL_DEBUG_TYPE_PORTABILITY_EXT: return DebugType::Portability;
+    case AL_DEBUG_TYPE_PERFORMANCE_EXT: return DebugType::Performance;
+    case AL_DEBUG_TYPE_MARKER_EXT: return DebugType::Marker;
+    case AL_DEBUG_TYPE_PUSH_GROUP_EXT: return DebugType::PushGroup;
+    case AL_DEBUG_TYPE_POP_GROUP_EXT: return DebugType::PopGroup;
+    case AL_DEBUG_TYPE_OTHER_EXT: return DebugType::Other;
+    }
+    return std::nullopt;
+}
+
+constexpr auto GetDebugSeverity(ALenum severity) noexcept -> std::optional<DebugSeverity>
+{
+    switch(severity)
+    {
+    case AL_DEBUG_SEVERITY_HIGH_EXT: return DebugSeverity::High;
+    case AL_DEBUG_SEVERITY_MEDIUM_EXT: return DebugSeverity::Medium;
+    case AL_DEBUG_SEVERITY_LOW_EXT: return DebugSeverity::Low;
+    case AL_DEBUG_SEVERITY_NOTIFICATION_EXT: return DebugSeverity::Notification;
+    }
+    return std::nullopt;
+}
+
+
+constexpr auto GetDebugSourceEnum(DebugSource source) -> ALenum
+{
+    switch(source)
+    {
+    case DebugSource::API: return AL_DEBUG_SOURCE_API_EXT;
+    case DebugSource::System: return AL_DEBUG_SOURCE_AUDIO_SYSTEM_EXT;
+    case DebugSource::ThirdParty: return AL_DEBUG_SOURCE_THIRD_PARTY_EXT;
+    case DebugSource::Application: return AL_DEBUG_SOURCE_APPLICATION_EXT;
+    case DebugSource::Other: return AL_DEBUG_SOURCE_OTHER_EXT;
+    }
+    throw std::runtime_error{"Unexpected debug source value "+std::to_string(al::to_underlying(source))};
+}
+
+constexpr auto GetDebugTypeEnum(DebugType type) -> ALenum
+{
+    switch(type)
+    {
+    case DebugType::Error: return AL_DEBUG_TYPE_ERROR_EXT;
+    case DebugType::DeprecatedBehavior: return AL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_EXT;
+    case DebugType::UndefinedBehavior: return AL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_EXT;
+    case DebugType::Portability: return AL_DEBUG_TYPE_PORTABILITY_EXT;
+    case DebugType::Performance: return AL_DEBUG_TYPE_PERFORMANCE_EXT;
+    case DebugType::Marker: return AL_DEBUG_TYPE_MARKER_EXT;
+    case DebugType::PushGroup: return AL_DEBUG_TYPE_PUSH_GROUP_EXT;
+    case DebugType::PopGroup: return AL_DEBUG_TYPE_POP_GROUP_EXT;
+    case DebugType::Other: return AL_DEBUG_TYPE_OTHER_EXT;
+    }
+    throw std::runtime_error{"Unexpected debug type value "+std::to_string(al::to_underlying(type))};
+}
+
+constexpr auto GetDebugSeverityEnum(DebugSeverity severity) -> ALenum
+{
+    switch(severity)
+    {
+    case DebugSeverity::High: return AL_DEBUG_SEVERITY_HIGH_EXT;
+    case DebugSeverity::Medium: return AL_DEBUG_SEVERITY_MEDIUM_EXT;
+    case DebugSeverity::Low: return AL_DEBUG_SEVERITY_LOW_EXT;
+    case DebugSeverity::Notification: return AL_DEBUG_SEVERITY_NOTIFICATION_EXT;
+    }
+    throw std::runtime_error{"Unexpected debug severity value "+std::to_string(al::to_underlying(severity))};
+}
+
+
+constexpr auto GetDebugSourceName(DebugSource source) noexcept -> const char*
+{
+    switch(source)
+    {
+    case DebugSource::API: return "API";
+    case DebugSource::System: return "Audio System";
+    case DebugSource::ThirdParty: return "Third Party";
+    case DebugSource::Application: return "Application";
+    case DebugSource::Other: return "Other";
+    }
+    return "<invalid source>";
+}
+
+constexpr auto GetDebugTypeName(DebugType type) noexcept -> const char*
+{
+    switch(type)
+    {
+    case DebugType::Error: return "Error";
+    case DebugType::DeprecatedBehavior: return "Deprecated Behavior";
+    case DebugType::UndefinedBehavior: return "Undefined Behavior";
+    case DebugType::Portability: return "Portability";
+    case DebugType::Performance: return "Performance";
+    case DebugType::Marker: return "Marker";
+    case DebugType::PushGroup: return "Push Group";
+    case DebugType::PopGroup: return "Pop Group";
+    case DebugType::Other: return "Other";
+    }
+    return "<invalid type>";
+}
+
+constexpr auto GetDebugSeverityName(DebugSeverity severity) noexcept -> const char*
+{
+    switch(severity)
+    {
+    case DebugSeverity::High: return "High";
+    case DebugSeverity::Medium: return "Medium";
+    case DebugSeverity::Low: return "Low";
+    case DebugSeverity::Notification: return "Notification";
+    }
+    return "<invalid severity>";
+}
+
+} // namespace
+
+
+void ALCcontext::sendDebugMessage(std::unique_lock<std::mutex> &debuglock, DebugSource source,
+    DebugType type, ALuint id, DebugSeverity severity, std::string_view message)
+{
+    if(!mDebugEnabled.load(std::memory_order_relaxed)) UNLIKELY
+        return;
+
+    if(message.length() >= MaxDebugMessageLength) UNLIKELY
+    {
+        ERR("Debug message too long (%zu >= %d):\n-> %.*s\n", message.length(),
+            MaxDebugMessageLength, al::sizei(message), message.data());
+        return;
+    }
+
+    DebugGroup &debug = mDebugGroups.back();
+
+    const uint64_t idfilter{(1_u64 << (DebugSourceBase+al::to_underlying(source)))
+        | (1_u64 << (DebugTypeBase+al::to_underlying(type)))
+        | (uint64_t{id} << 32)};
+    auto iditer = std::lower_bound(debug.mIdFilters.cbegin(), debug.mIdFilters.cend(), idfilter);
+    if(iditer != debug.mIdFilters.cend() && *iditer == idfilter)
+        return;
+
+    const uint filter{(1u << (DebugSourceBase+al::to_underlying(source)))
+        | (1u << (DebugTypeBase+al::to_underlying(type)))
+        | (1u << (DebugSeverityBase+al::to_underlying(severity)))};
+    auto iter = std::lower_bound(debug.mFilters.cbegin(), debug.mFilters.cend(), filter);
+    if(iter != debug.mFilters.cend() && *iter == filter)
+        return;
+
+    if(mDebugCb)
+    {
+        auto callback = mDebugCb;
+        auto param = mDebugParam;
+        debuglock.unlock();
+        callback(GetDebugSourceEnum(source), GetDebugTypeEnum(type), id,
+            GetDebugSeverityEnum(severity), static_cast<ALsizei>(message.length()), message.data(),
+            param);
+    }
+    else
+    {
+        if(mDebugLog.size() < MaxDebugLoggedMessages)
+            mDebugLog.emplace_back(source, type, id, severity, message);
+        else UNLIKELY
+            ERR("Debug message log overflow. Lost message:\n"
+                "  Source: %s\n"
+                "  Type: %s\n"
+                "  ID: %u\n"
+                "  Severity: %s\n"
+                "  Message: \"%.*s\"\n",
+                GetDebugSourceName(source), GetDebugTypeName(type), id,
+                GetDebugSeverityName(severity), al::sizei(message), message.data());
+    }
+}
+
+
+FORCE_ALIGN DECL_FUNCEXT2(void, alDebugMessageCallback,EXT, ALDEBUGPROCEXT,callback, void*,userParam)
+FORCE_ALIGN void AL_APIENTRY alDebugMessageCallbackDirectEXT(ALCcontext *context,
+    ALDEBUGPROCEXT callback, void *userParam) noexcept
+{
+    std::lock_guard<std::mutex> debuglock{context->mDebugCbLock};
+    context->mDebugCb = callback;
+    context->mDebugParam = userParam;
+}
+
+
+FORCE_ALIGN DECL_FUNCEXT6(void, alDebugMessageInsert,EXT, ALenum,source, ALenum,type, ALuint,id, ALenum,severity, ALsizei,length, const ALchar*,message)
+FORCE_ALIGN void AL_APIENTRY alDebugMessageInsertDirectEXT(ALCcontext *context, ALenum source,
+    ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) noexcept
+try {
+    if(!context->mContextFlags.test(ContextFlags::DebugBit))
+        return;
+
+    if(!message)
+        throw al::context_error{AL_INVALID_VALUE, "Null message pointer"};
+
+    auto msgview = (length < 0) ? std::string_view{message}
+        : std::string_view{message, static_cast<uint>(length)};
+    if(msgview.size() >= MaxDebugMessageLength)
+        throw al::context_error{AL_INVALID_VALUE, "Debug message too long (%zu >= %d)",
+            msgview.size(), MaxDebugMessageLength};
+
+    auto dsource = GetDebugSource(source);
+    if(!dsource)
+        throw al::context_error{AL_INVALID_ENUM, "Invalid debug source 0x%04x", source};
+    if(*dsource != DebugSource::ThirdParty && *dsource != DebugSource::Application)
+        throw al::context_error{AL_INVALID_ENUM, "Debug source 0x%04x not allowed", source};
+
+    auto dtype = GetDebugType(type);
+    if(!dtype)
+        throw al::context_error{AL_INVALID_ENUM, "Invalid debug type 0x%04x", type};
+
+    auto dseverity = GetDebugSeverity(severity);
+    if(!dseverity)
+        throw al::context_error{AL_INVALID_ENUM, "Invalid debug severity 0x%04x", severity};
+
+    context->debugMessage(*dsource, *dtype, id, *dseverity, msgview);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
+}
+
+
+FORCE_ALIGN DECL_FUNCEXT6(void, alDebugMessageControl,EXT, ALenum,source, ALenum,type, ALenum,severity, ALsizei,count, const ALuint*,ids, ALboolean,enable)
+FORCE_ALIGN void AL_APIENTRY alDebugMessageControlDirectEXT(ALCcontext *context, ALenum source,
+    ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) noexcept
+try {
+    if(count > 0)
+    {
+        if(!ids)
+            throw al::context_error{AL_INVALID_VALUE, "IDs is null with non-0 count"};
+        if(source == AL_DONT_CARE_EXT)
+            throw al::context_error{AL_INVALID_OPERATION,
+                "Debug source cannot be AL_DONT_CARE_EXT with IDs"};
+        if(type == AL_DONT_CARE_EXT)
+            throw al::context_error{AL_INVALID_OPERATION,
+                "Debug type cannot be AL_DONT_CARE_EXT with IDs"};
+        if(severity != AL_DONT_CARE_EXT)
+            throw al::context_error{AL_INVALID_OPERATION,
+                "Debug severity must be AL_DONT_CARE_EXT with IDs"};
+    }
+
+    if(enable != AL_TRUE && enable != AL_FALSE)
+        throw al::context_error{AL_INVALID_ENUM, "Invalid debug enable %d", enable};
+
+    static constexpr size_t ElemCount{DebugSourceCount + DebugTypeCount + DebugSeverityCount};
+    static constexpr auto Values = make_array_sequence<uint8_t,ElemCount>();
+
+    auto srcIndices = al::span{Values}.subspan(DebugSourceBase,DebugSourceCount);
+    if(source != AL_DONT_CARE_EXT)
+    {
+        auto dsource = GetDebugSource(source);
+        if(!dsource)
+            throw al::context_error{AL_INVALID_ENUM, "Invalid debug source 0x%04x", source};
+        srcIndices = srcIndices.subspan(al::to_underlying(*dsource), 1);
+    }
+
+    auto typeIndices = al::span{Values}.subspan(DebugTypeBase,DebugTypeCount);
+    if(type != AL_DONT_CARE_EXT)
+    {
+        auto dtype = GetDebugType(type);
+        if(!dtype)
+            throw al::context_error{AL_INVALID_ENUM, "Invalid debug type 0x%04x", type};
+        typeIndices = typeIndices.subspan(al::to_underlying(*dtype), 1);
+    }
+
+    auto svrIndices = al::span{Values}.subspan(DebugSeverityBase,DebugSeverityCount);
+    if(severity != AL_DONT_CARE_EXT)
+    {
+        auto dseverity = GetDebugSeverity(severity);
+        if(!dseverity)
+            throw al::context_error{AL_INVALID_ENUM, "Invalid debug severity 0x%04x", severity};
+        svrIndices = svrIndices.subspan(al::to_underlying(*dseverity), 1);
+    }
+
+    std::lock_guard<std::mutex> debuglock{context->mDebugCbLock};
+    DebugGroup &debug = context->mDebugGroups.back();
+    if(count > 0)
+    {
+        const uint filterbase{(1u<<srcIndices[0]) | (1u<<typeIndices[0])};
+
+        for(const uint id : al::span{ids, static_cast<uint>(count)})
+        {
+            const uint64_t filter{filterbase | (uint64_t{id} << 32)};
+
+            auto iter = std::lower_bound(debug.mIdFilters.cbegin(), debug.mIdFilters.cend(),
+                filter);
+            if(!enable && (iter == debug.mIdFilters.cend() || *iter != filter))
+                debug.mIdFilters.insert(iter, filter);
+            else if(enable && iter != debug.mIdFilters.cend() && *iter == filter)
+                debug.mIdFilters.erase(iter);
+        }
+    }
+    else
+    {
+        auto apply_filter = [enable,&debug](const uint filter)
+        {
+            auto iter = std::lower_bound(debug.mFilters.cbegin(), debug.mFilters.cend(), filter);
+            if(!enable && (iter == debug.mFilters.cend() || *iter != filter))
+                debug.mFilters.insert(iter, filter);
+            else if(enable && iter != debug.mFilters.cend() && *iter == filter)
+                debug.mFilters.erase(iter);
+        };
+        auto apply_severity = [apply_filter,svrIndices](const uint filter)
+        {
+            std::for_each(svrIndices.cbegin(), svrIndices.cend(),
+                [apply_filter,filter](const uint idx){ apply_filter(filter | (1<<idx)); });
+        };
+        auto apply_type = [apply_severity,typeIndices](const uint filter)
+        {
+            std::for_each(typeIndices.cbegin(), typeIndices.cend(),
+                [apply_severity,filter](const uint idx){ apply_severity(filter | (1<<idx)); });
+        };
+        std::for_each(srcIndices.cbegin(), srcIndices.cend(),
+            [apply_type](const uint idx){ apply_type(1<<idx); });
+    }
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
+}
+
+
+FORCE_ALIGN DECL_FUNCEXT4(void, alPushDebugGroup,EXT, ALenum,source, ALuint,id, ALsizei,length, const ALchar*,message)
+FORCE_ALIGN void AL_APIENTRY alPushDebugGroupDirectEXT(ALCcontext *context, ALenum source,
+    ALuint id, ALsizei length, const ALchar *message) noexcept
+try {
+    if(length < 0)
+    {
+        size_t newlen{std::strlen(message)};
+        if(newlen >= MaxDebugMessageLength)
+            throw al::context_error{AL_INVALID_VALUE, "Debug message too long (%zu >= %d)", newlen,
+                MaxDebugMessageLength};
+        length = static_cast<ALsizei>(newlen);
+    }
+    else if(length >= MaxDebugMessageLength)
+        throw al::context_error{AL_INVALID_VALUE, "Debug message too long (%d >= %d)", length,
+            MaxDebugMessageLength};
+
+    auto dsource = GetDebugSource(source);
+    if(!dsource)
+        throw al::context_error{AL_INVALID_ENUM, "Invalid debug source 0x%04x", source};
+    if(*dsource != DebugSource::ThirdParty && *dsource != DebugSource::Application)
+        throw al::context_error{AL_INVALID_ENUM, "Debug source 0x%04x not allowed", source};
+
+    std::unique_lock<std::mutex> debuglock{context->mDebugCbLock};
+    if(context->mDebugGroups.size() >= MaxDebugGroupDepth)
+        throw al::context_error{AL_STACK_OVERFLOW_EXT, "Pushing too many debug groups"};
+
+    context->mDebugGroups.emplace_back(*dsource, id,
+        std::string_view{message, static_cast<uint>(length)});
+    auto &oldback = *(context->mDebugGroups.end()-2);
+    auto &newback = context->mDebugGroups.back();
+
+    newback.mFilters = oldback.mFilters;
+    newback.mIdFilters = oldback.mIdFilters;
+
+    if(context->mContextFlags.test(ContextFlags::DebugBit))
+        context->sendDebugMessage(debuglock, newback.mSource, DebugType::PushGroup, newback.mId,
+            DebugSeverity::Notification, newback.mMessage);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
+}
+
+FORCE_ALIGN DECL_FUNCEXT(void, alPopDebugGroup,EXT)
+FORCE_ALIGN void AL_APIENTRY alPopDebugGroupDirectEXT(ALCcontext *context) noexcept
+try {
+    std::unique_lock<std::mutex> debuglock{context->mDebugCbLock};
+    if(context->mDebugGroups.size() <= 1)
+        throw al::context_error{AL_STACK_UNDERFLOW_EXT,
+            "Attempting to pop the default debug group"};
+
+    DebugGroup &debug = context->mDebugGroups.back();
+    const auto source = debug.mSource;
+    const auto id = debug.mId;
+    std::string message{std::move(debug.mMessage)};
+
+    context->mDebugGroups.pop_back();
+    if(context->mContextFlags.test(ContextFlags::DebugBit))
+        context->sendDebugMessage(debuglock, source, DebugType::PopGroup, id,
+            DebugSeverity::Notification, message);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
+}
+
+
+FORCE_ALIGN DECL_FUNCEXT8(ALuint, alGetDebugMessageLog,EXT, ALuint,count, ALsizei,logBufSize, ALenum*,sources, ALenum*,types, ALuint*,ids, ALenum*,severities, ALsizei*,lengths, ALchar*,logBuf)
+FORCE_ALIGN ALuint AL_APIENTRY alGetDebugMessageLogDirectEXT(ALCcontext *context, ALuint count,
+    ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities,
+    ALsizei *lengths, ALchar *logBuf) noexcept
+try {
+    if(logBufSize < 0)
+        throw al::context_error{AL_INVALID_VALUE, "Negative debug log buffer size"};
+
+    auto sourcesOut = al::span{sources, sources ? count : 0u};
+    auto typesOut = al::span{types, types ? count : 0u};
+    auto idsOut = al::span{ids, ids ? count : 0u};
+    auto severitiesOut = al::span{severities, severities ? count : 0u};
+    auto lengthsOut = al::span{lengths, lengths ? count : 0u};
+    auto logOut = al::span{logBuf, logBuf ? static_cast<ALuint>(logBufSize) : 0u};
+
+    std::lock_guard<std::mutex> debuglock{context->mDebugCbLock};
+    for(ALuint i{0};i < count;++i)
+    {
+        if(context->mDebugLog.empty())
+            return i;
+
+        auto &entry = context->mDebugLog.front();
+        const size_t tocopy{entry.mMessage.size() + 1};
+        if(logOut.data() != nullptr)
+        {
+            if(logOut.size() < tocopy)
+                return i;
+            auto oiter = std::copy(entry.mMessage.cbegin(), entry.mMessage.cend(), logOut.begin());
+            *oiter = '\0';
+            logOut = {oiter+1, logOut.end()};
+        }
+
+        if(!sourcesOut.empty())
+        {
+            sourcesOut.front() = GetDebugSourceEnum(entry.mSource);
+            sourcesOut = sourcesOut.subspan<1>();
+        }
+        if(!typesOut.empty())
+        {
+            typesOut.front() = GetDebugTypeEnum(entry.mType);
+            typesOut = typesOut.subspan<1>();
+        }
+        if(!idsOut.empty())
+        {
+            idsOut.front() = entry.mId;
+            idsOut = idsOut.subspan<1>();
+        }
+        if(!severitiesOut.empty())
+        {
+            severitiesOut.front() = GetDebugSeverityEnum(entry.mSeverity);
+            severitiesOut = severitiesOut.subspan<1>();
+        }
+        if(!lengthsOut.empty())
+        {
+            lengthsOut.front() = static_cast<ALsizei>(tocopy);
+            lengthsOut = lengthsOut.subspan<1>();
+        }
+
+        context->mDebugLog.pop_front();
+    }
+
+    return count;
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
+    return 0;
+}
+
+FORCE_ALIGN DECL_FUNCEXT4(void, alObjectLabel,EXT, ALenum,identifier, ALuint,name, ALsizei,length, const ALchar*,label)
+FORCE_ALIGN void AL_APIENTRY alObjectLabelDirectEXT(ALCcontext *context, ALenum identifier,
+    ALuint name, ALsizei length, const ALchar *label) noexcept
+try {
+    if(!label && length != 0)
+        throw al::context_error{AL_INVALID_VALUE, "Null label pointer"};
+
+    auto objname = (length < 0) ? std::string_view{label}
+        : std::string_view{label, static_cast<uint>(length)};
+    if(objname.size() >= MaxObjectLabelLength)
+        throw al::context_error{AL_INVALID_VALUE, "Object label length too long (%zu >= %d)",
+            objname.size(), MaxObjectLabelLength};
+
+    switch(identifier)
+    {
+    case AL_SOURCE_EXT: ALsource::SetName(context, name, objname); return;
+    case AL_BUFFER: ALbuffer::SetName(context, name, objname); return;
+    case AL_FILTER_EXT: ALfilter::SetName(context, name, objname); return;
+    case AL_EFFECT_EXT: ALeffect::SetName(context, name, objname); return;
+    case AL_AUXILIARY_EFFECT_SLOT_EXT: ALeffectslot::SetName(context, name, objname); return;
+    }
+
+    throw al::context_error{AL_INVALID_ENUM, "Invalid name identifier 0x%04x", identifier};
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
+}
+
+FORCE_ALIGN DECL_FUNCEXT5(void, alGetObjectLabel,EXT, ALenum,identifier, ALuint,name, ALsizei,bufSize, ALsizei*,length, ALchar*,label)
+FORCE_ALIGN void AL_APIENTRY alGetObjectLabelDirectEXT(ALCcontext *context, ALenum identifier,
+    ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) noexcept
+try {
+    if(bufSize < 0)
+        throw al::context_error{AL_INVALID_VALUE, "Negative label bufSize"};
+
+    if(!label && !length)
+        throw al::context_error{AL_INVALID_VALUE, "Null length and label"};
+    if(label && bufSize == 0)
+        throw al::context_error{AL_INVALID_VALUE, "Zero label bufSize"};
+
+    const auto labelOut = al::span{label, label ? static_cast<ALuint>(bufSize) : 0u};
+    auto copy_name = [name,length,labelOut](std::unordered_map<ALuint,std::string> &names)
+    {
+        std::string_view objname;
+
+        auto iter = names.find(name);
+        if(iter != names.end())
+            objname = iter->second;
+
+        if(labelOut.empty())
+            *length = static_cast<ALsizei>(objname.size());
+        else
+        {
+            const size_t tocopy{std::min(objname.size(), labelOut.size()-1)};
+            auto oiter = std::copy_n(objname.cbegin(), tocopy, labelOut.begin());
+            *oiter = '\0';
+            if(length)
+                *length = static_cast<ALsizei>(tocopy);
+        }
+    };
+
+    if(identifier == AL_SOURCE_EXT)
+    {
+        std::lock_guard srclock{context->mSourceLock};
+        copy_name(context->mSourceNames);
+    }
+    else if(identifier == AL_BUFFER)
+    {
+        ALCdevice *device{context->mALDevice.get()};
+        std::lock_guard buflock{device->BufferLock};
+        copy_name(device->mBufferNames);
+    }
+    else if(identifier == AL_FILTER_EXT)
+    {
+        ALCdevice *device{context->mALDevice.get()};
+        std::lock_guard filterlock{device->FilterLock};
+        copy_name(device->mFilterNames);
+    }
+    else if(identifier == AL_EFFECT_EXT)
+    {
+        ALCdevice *device{context->mALDevice.get()};
+        std::lock_guard effectlock{device->EffectLock};
+        copy_name(device->mEffectNames);
+    }
+    else if(identifier == AL_AUXILIARY_EFFECT_SLOT_EXT)
+    {
+        std::lock_guard slotlock{context->mEffectSlotLock};
+        copy_name(context->mEffectSlotNames);
+    }
+    else
+        throw al::context_error{AL_INVALID_ENUM, "Invalid name identifier 0x%04x", identifier};
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
+}

+ 70 - 0
Engine/lib/openal-soft/al/debug.h

@@ -0,0 +1,70 @@
+#ifndef AL_DEBUG_H
+#define AL_DEBUG_H
+
+#include <cstdint>
+#include <string>
+#include <utility>
+#include <vector>
+
+using uint = unsigned int;
+
+
+/* Somewhat arbitrary. Avoid letting it get out of control if the app enables
+ * logging but never reads it.
+ */
+inline constexpr std::uint8_t MaxDebugLoggedMessages{64};
+inline constexpr std::uint16_t MaxDebugMessageLength{1024};
+inline constexpr std::uint8_t MaxDebugGroupDepth{64};
+inline constexpr std::uint16_t MaxObjectLabelLength{1024};
+
+
+inline constexpr uint DebugSourceBase{0};
+enum class DebugSource : std::uint8_t {
+    API = 0,
+    System,
+    ThirdParty,
+    Application,
+    Other,
+};
+inline constexpr uint DebugSourceCount{5};
+
+inline constexpr uint DebugTypeBase{DebugSourceBase + DebugSourceCount};
+enum class DebugType : std::uint8_t {
+    Error = 0,
+    DeprecatedBehavior,
+    UndefinedBehavior,
+    Portability,
+    Performance,
+    Marker,
+    PushGroup,
+    PopGroup,
+    Other,
+};
+inline constexpr uint DebugTypeCount{9};
+
+inline constexpr uint DebugSeverityBase{DebugTypeBase + DebugTypeCount};
+enum class DebugSeverity : std::uint8_t {
+    High = 0,
+    Medium,
+    Low,
+    Notification,
+};
+inline constexpr uint DebugSeverityCount{4};
+
+struct DebugGroup {
+    const uint mId;
+    const DebugSource mSource;
+    std::string mMessage;
+    std::vector<uint> mFilters;
+    std::vector<std::uint64_t> mIdFilters;
+
+    template<typename T>
+    DebugGroup(DebugSource source, uint id, T&& message)
+        : mId{id}, mSource{source}, mMessage{std::forward<T>(message)}
+    { }
+    DebugGroup(const DebugGroup&) = default;
+    DebugGroup(DebugGroup&&) = default;
+    ~DebugGroup();
+};
+
+#endif /* AL_DEBUG_H */

+ 127 - 0
Engine/lib/openal-soft/al/direct_defs.h

@@ -0,0 +1,127 @@
+#ifndef AL_DIRECT_DEFS_H
+#define AL_DIRECT_DEFS_H
+
+namespace detail_ {
+
+template<typename T>
+constexpr T DefaultVal() noexcept { return T{}; }
+
+template<>
+constexpr void DefaultVal() noexcept { }
+
+} // namespace detail_
+
+#define DECL_FUNC(R, Name)                                                    \
+auto AL_APIENTRY Name() noexcept -> R                                         \
+{                                                                             \
+    auto context = GetContextRef();                                           \
+    if(!context) UNLIKELY return detail_::DefaultVal<R>();                    \
+    return Name##Direct(context.get());                                       \
+}
+
+#define DECL_FUNC1(R, Name, T1,n1)                                            \
+auto AL_APIENTRY Name(T1 n1) noexcept -> R                                    \
+{                                                                             \
+    auto context = GetContextRef();                                           \
+    if(!context) UNLIKELY return detail_::DefaultVal<R>();                    \
+    return Name##Direct(context.get(), n1);                                   \
+}
+
+#define DECL_FUNC2(R, Name, T1,n1, T2,n2)                                     \
+auto AL_APIENTRY Name(T1 n1, T2 n2) noexcept -> R                             \
+{                                                                             \
+    auto context = GetContextRef();                                           \
+    if(!context) UNLIKELY return detail_::DefaultVal<R>();                    \
+    return Name##Direct(context.get(), n1, n2);                               \
+}
+
+#define DECL_FUNC3(R, Name, T1,n1, T2,n2, T3,n3)                              \
+auto AL_APIENTRY Name(T1 n1, T2 n2, T3 n3) noexcept -> R                      \
+{                                                                             \
+    auto context = GetContextRef();                                           \
+    if(!context) UNLIKELY return detail_::DefaultVal<R>();                    \
+    return Name##Direct(context.get(), n1, n2, n3);                           \
+}
+
+#define DECL_FUNC4(R, Name, T1,n1, T2,n2, T3,n3, T4,n4)                       \
+auto AL_APIENTRY Name(T1 n1, T2 n2, T3 n3, T4 n4) noexcept -> R               \
+{                                                                             \
+    auto context = GetContextRef();                                           \
+    if(!context) UNLIKELY return detail_::DefaultVal<R>();                    \
+    return Name##Direct(context.get(), n1, n2, n3, n4);                       \
+}
+
+#define DECL_FUNC5(R, Name, T1,n1, T2,n2, T3,n3, T4,n4, T5,n5)                \
+auto AL_APIENTRY Name(T1 n1, T2 n2, T3 n3, T4 n4, T5 n5) noexcept -> R        \
+{                                                                             \
+    auto context = GetContextRef();                                           \
+    if(!context) UNLIKELY return detail_::DefaultVal<R>();                    \
+    return Name##Direct(context.get(), n1, n2, n3, n4, n5);                   \
+}
+
+
+#define DECL_FUNCEXT(R, Name,Ext)                                             \
+auto AL_APIENTRY Name##Ext() noexcept -> R                                    \
+{                                                                             \
+    auto context = GetContextRef();                                           \
+    if(!context) UNLIKELY return detail_::DefaultVal<R>();                    \
+    return Name##Direct##Ext(context.get());                                  \
+}
+
+#define DECL_FUNCEXT1(R, Name,Ext, T1,n1)                                     \
+auto AL_APIENTRY Name##Ext(T1 n1) noexcept -> R                               \
+{                                                                             \
+    auto context = GetContextRef();                                           \
+    if(!context) UNLIKELY return detail_::DefaultVal<R>();                    \
+    return Name##Direct##Ext(context.get(), n1);                              \
+}
+
+#define DECL_FUNCEXT2(R, Name,Ext, T1,n1, T2,n2)                              \
+auto AL_APIENTRY Name##Ext(T1 n1, T2 n2) noexcept -> R                        \
+{                                                                             \
+    auto context = GetContextRef();                                           \
+    if(!context) UNLIKELY return detail_::DefaultVal<R>();                    \
+    return Name##Direct##Ext(context.get(), n1, n2);                          \
+}
+
+#define DECL_FUNCEXT3(R, Name,Ext, T1,n1, T2,n2, T3,n3)                       \
+auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3) noexcept -> R                 \
+{                                                                             \
+    auto context = GetContextRef();                                           \
+    if(!context) UNLIKELY return detail_::DefaultVal<R>();                    \
+    return Name##Direct##Ext(context.get(), n1, n2, n3);                      \
+}
+
+#define DECL_FUNCEXT4(R, Name,Ext, T1,n1, T2,n2, T3,n3, T4,n4)                \
+auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3, T4 n4) noexcept -> R          \
+{                                                                             \
+    auto context = GetContextRef();                                           \
+    if(!context) UNLIKELY return detail_::DefaultVal<R>();                    \
+    return Name##Direct##Ext(context.get(), n1, n2, n3, n4);                  \
+}
+
+#define DECL_FUNCEXT5(R, Name,Ext, T1,n1, T2,n2, T3,n3, T4,n4, T5,n5)         \
+auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3, T4 n4, T5 n5) noexcept -> R   \
+{                                                                             \
+    auto context = GetContextRef();                                           \
+    if(!context) UNLIKELY return detail_::DefaultVal<R>();                    \
+    return Name##Direct##Ext(context.get(), n1, n2, n3, n4, n5);              \
+}
+
+#define DECL_FUNCEXT6(R, Name,Ext, T1,n1, T2,n2, T3,n3, T4,n4, T5,n5, T6,n6)  \
+auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3, T4 n4, T5 n5, T6 n6) noexcept -> R \
+{                                                                             \
+    auto context = GetContextRef();                                           \
+    if(!context) UNLIKELY return detail_::DefaultVal<R>();                    \
+    return Name##Direct##Ext(context.get(), n1, n2, n3, n4, n5, n6);          \
+}
+
+#define DECL_FUNCEXT8(R, Name,Ext, T1,n1, T2,n2, T3,n3, T4,n4, T5,n5, T6,n6, T7,n7, T8,n8) \
+auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3, T4 n4, T5 n5, T6 n6, T7 n7, T8 n8) noexcept -> R \
+{                                                                             \
+    auto context = GetContextRef();                                           \
+    if(!context) UNLIKELY return detail_::DefaultVal<R>();                    \
+    return Name##Direct##Ext(context.get(), n1, n2, n3, n4, n5, n6, n7, n8);  \
+}
+
+#endif /* AL_DIRECT_DEFS_H */

+ 44 - 15
Engine/lib/openal-soft/al/eax/api.h

@@ -10,34 +10,39 @@
 //
 
 
+#include <array>
 #include <cfloat>
 #include <cstdint>
 #include <cstring>
-
-#include <array>
+#include <tuple>
+#ifdef _WIN32
+#include <guiddef.h>
+#endif
 
 #include "AL/al.h"
 
 
-#ifndef GUID_DEFINED
-#define GUID_DEFINED
-typedef struct _GUID {
+#ifndef _WIN32
+using GUID = struct _GUID { /* NOLINT(*-reserved-identifier) */
     std::uint32_t Data1;
     std::uint16_t Data2;
     std::uint16_t Data3;
-    std::uint8_t Data4[8];
-} GUID;
+    std::array<std::uint8_t,8> Data4;
+};
 
-#ifndef _SYS_GUID_OPERATOR_EQ_
-#define _SYS_GUID_OPERATOR_EQ_
 inline bool operator==(const GUID& lhs, const GUID& rhs) noexcept
 { return std::memcmp(&lhs, &rhs, sizeof(GUID)) == 0; }
 
 inline bool operator!=(const GUID& lhs, const GUID& rhs) noexcept
 { return !(lhs == rhs); }
-#endif  // _SYS_GUID_OPERATOR_EQ_
-#endif // GUID_DEFINED
+#endif // _WIN32
 
+#define DECL_EQOP(T, ...) \
+[[nodiscard]] auto get_members() const noexcept { return std::forward_as_tuple(__VA_ARGS__); } \
+[[nodiscard]] friend bool operator==(const T &lhs, const T &rhs) noexcept \
+{ return lhs.get_members() == rhs.get_members();  } \
+[[nodiscard]] friend bool operator!=(const T &lhs, const T &rhs) noexcept \
+{ return !(lhs == rhs);  }
 
 extern const GUID DSPROPSETID_EAX_ReverbProperties;
 
@@ -276,11 +281,15 @@ struct EAXVECTOR {
     float x;
     float y;
     float z;
+    [[nodiscard]]
+    auto get_members() const noexcept { return std::forward_as_tuple(x, y, z); }
 }; // EAXVECTOR
 
+[[nodiscard]]
 inline bool operator==(const EAXVECTOR& lhs, const EAXVECTOR& rhs) noexcept
-{ return std::memcmp(&lhs, &rhs, sizeof(EAXVECTOR)) == 0; }
+{ return lhs.get_members() == rhs.get_members(); }
 
+[[nodiscard]]
 inline bool operator!=(const EAXVECTOR& lhs, const EAXVECTOR& rhs) noexcept
 { return !(lhs == rhs); }
 
@@ -361,6 +370,7 @@ constexpr auto EAXCONTEXT_MINMACROFXFACTOR = 0.0F;
 constexpr auto EAXCONTEXT_MAXMACROFXFACTOR = 1.0F;
 constexpr auto EAXCONTEXT_DEFAULTMACROFXFACTOR = 0.0F;
 
+constexpr auto EAXCONTEXT_DEFAULTLASTERROR = EAX_OK;
 
 extern const GUID EAXPROPERTYID_EAX40_FXSlot0;
 extern const GUID EAXPROPERTYID_EAX50_FXSlot0;
@@ -613,7 +623,7 @@ struct EAX30SOURCEPROPERTIES {
     float flOcclusionLFRatio; // occlusion low-frequency level re. main control
     float flOcclusionRoomRatio; // relative occlusion control for room effect
     float flOcclusionDirectRatio; // relative occlusion control for direct path
-    long lExclusion; // main exlusion control (attenuation at high frequencies)
+    long lExclusion; // main exclusion control (attenuation at high frequencies)
     float flExclusionLFRatio; // exclusion low-frequency level re. main control
     long lOutsideVolumeHF; // outside sound cone level at high frequencies
     float flDopplerFactor; // like DS3D flDopplerFactor but per source
@@ -653,11 +663,11 @@ struct EAXSPEAKERLEVELPROPERTIES {
 }; // EAXSPEAKERLEVELPROPERTIES
 
 struct EAX40ACTIVEFXSLOTS {
-    GUID guidActiveFXSlots[EAX40_MAX_ACTIVE_FXSLOTS];
+    std::array<GUID,EAX40_MAX_ACTIVE_FXSLOTS> guidActiveFXSlots;
 }; // EAX40ACTIVEFXSLOTS
 
 struct EAX50ACTIVEFXSLOTS {
-    GUID guidActiveFXSlots[EAX50_MAX_ACTIVE_FXSLOTS];
+    std::array<GUID,EAX50_MAX_ACTIVE_FXSLOTS> guidActiveFXSlots;
 }; // EAX50ACTIVEFXSLOTS
 
 // Use this structure for EAXSOURCE_OBSTRUCTIONPARAMETERS property.
@@ -836,6 +846,11 @@ struct EAXREVERBPROPERTIES {
     float flLFReference; // reference low frequency 
     float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect
     unsigned long ulFlags; // modifies the behavior of properties
+    DECL_EQOP(EAXREVERBPROPERTIES, ulEnvironment, flEnvironmentSize, flEnvironmentDiffusion, lRoom,
+        lRoomHF, lRoomLF, flDecayTime, flDecayHFRatio, flDecayLFRatio, lReflections,
+        flReflectionsDelay, vReflectionsPan, lReverb, flReverbDelay, vReverbPan, flEchoTime,
+        flEchoDepth, flModulationTime, flModulationDepth, flAirAbsorptionHF, flHFReference,
+        flLFReference, flRoomRolloffFactor, ulFlags)
 }; // EAXREVERBPROPERTIES
 
 
@@ -965,6 +980,7 @@ enum EAXAGCCOMPRESSOR_PROPERTY : unsigned int {
 
 struct EAXAGCCOMPRESSORPROPERTIES {
     unsigned long ulOnOff; // Switch Compressor on or off
+    DECL_EQOP(EAXAGCCOMPRESSORPROPERTIES, ulOnOff)
 }; // EAXAGCCOMPRESSORPROPERTIES
 
 
@@ -991,6 +1007,7 @@ struct EAXAUTOWAHPROPERTIES {
     float flReleaseTime; // Release time (seconds)
     long lResonance; // Resonance (mB)
     long lPeakLevel; // Peak level (mB)
+    DECL_EQOP(EAXAUTOWAHPROPERTIES, flAttackTime, flReleaseTime, lResonance, lPeakLevel)
 }; // EAXAUTOWAHPROPERTIES
 
 
@@ -1038,6 +1055,7 @@ struct EAXCHORUSPROPERTIES {
     float flDepth; // Depth (0 to 1)
     float flFeedback; // Feedback (-1 to 1)
     float flDelay; // Delay (seconds)
+    DECL_EQOP(EAXCHORUSPROPERTIES, ulWaveform, lPhase, flRate, flDepth, flFeedback, flDelay)
 }; // EAXCHORUSPROPERTIES
 
 
@@ -1086,6 +1104,7 @@ struct EAXDISTORTIONPROPERTIES {
     float flLowPassCutOff; // Controls the cut-off of the filter pre-distortion (Hz)
     float flEQCenter; // Controls the center frequency of the EQ post-distortion (Hz)
     float flEQBandwidth; // Controls the bandwidth of the EQ post-distortion (Hz)
+    DECL_EQOP(EAXDISTORTIONPROPERTIES, flEdge, lGain, flLowPassCutOff, flEQCenter, flEQBandwidth)
 }; // EAXDISTORTIONPROPERTIES
 
 
@@ -1130,6 +1149,7 @@ struct EAXECHOPROPERTIES {
     float flDamping; // Controls a low-pass filter that dampens the echoes (0 to 1)
     float flFeedback; // Controls the duration of echo repetition (0 to 1)
     float flSpread; // Controls the left-right spread of the echoes
+    DECL_EQOP(EAXECHOPROPERTIES, flDelay, flLRDelay, flDamping, flFeedback, flSpread)
 }; // EAXECHOPROPERTIES
 
 
@@ -1184,6 +1204,8 @@ struct EAXEQUALIZERPROPERTIES {
     float flMid2Width; // (octaves)
     long lHighGain; // (mB)
     float flHighCutOff; // (Hz)
+    DECL_EQOP(EAXEQUALIZERPROPERTIES, lLowGain, flLowCutOff, lMid1Gain, flMid1Center, flMid1Width,
+        lMid2Gain, flMid2Center, flMid2Width, lHighGain, flHighCutOff)
 }; // EAXEQUALIZERPROPERTIES
 
 
@@ -1255,6 +1277,7 @@ struct EAXFLANGERPROPERTIES {
     float flDepth; // Depth (0 to 1)
     float flFeedback; // Feedback (0 to 1)
     float flDelay; // Delay (seconds)
+    DECL_EQOP(EAXFLANGERPROPERTIES, ulWaveform, lPhase, flRate, flDepth, flFeedback, flDelay)
 }; // EAXFLANGERPROPERTIES
 
 
@@ -1305,6 +1328,7 @@ struct EAXFREQUENCYSHIFTERPROPERTIES {
     float flFrequency; // (Hz)
     unsigned long ulLeftDirection; // see enum above
     unsigned long ulRightDirection; // see enum above
+    DECL_EQOP(EAXFREQUENCYSHIFTERPROPERTIES, flFrequency, ulLeftDirection, ulRightDirection)
 }; // EAXFREQUENCYSHIFTERPROPERTIES
 
 
@@ -1383,6 +1407,8 @@ struct EAXVOCALMORPHERPROPERTIES {
     long lPhonemeBCoarseTuning; // (semitones)
     unsigned long ulWaveform; // Waveform selector - see enum above
     float flRate; // (Hz)
+    DECL_EQOP(EAXVOCALMORPHERPROPERTIES, ulPhonemeA, lPhonemeACoarseTuning, ulPhonemeB,
+        lPhonemeBCoarseTuning, ulWaveform, flRate)
 }; // EAXVOCALMORPHERPROPERTIES
 
 
@@ -1425,6 +1451,7 @@ enum EAXPITCHSHIFTER_PROPERTY : unsigned int {
 struct EAXPITCHSHIFTERPROPERTIES {
     long lCoarseTune; // Amount of pitch shift (semitones)
     long lFineTune; // Amount of pitch shift (cents)
+    DECL_EQOP(EAXPITCHSHIFTERPROPERTIES, lCoarseTune, lFineTune)
 }; // EAXPITCHSHIFTERPROPERTIES
 
 
@@ -1460,6 +1487,7 @@ struct EAXRINGMODULATORPROPERTIES {
     float flFrequency; // Frequency of modulation (Hz)
     float flHighPassCutOff; // Cut-off frequency of high-pass filter (Hz)
     unsigned long ulWaveform; // Waveform selector - see enum above
+    DECL_EQOP(EAXRINGMODULATORPROPERTIES, flFrequency, flHighPassCutOff, ulWaveform)
 }; // EAXRINGMODULATORPROPERTIES
 
 
@@ -1490,4 +1518,5 @@ using LPEAXGET = ALenum(AL_APIENTRY*)(
     ALvoid* property_buffer,
     ALuint property_size);
 
+#undef DECL_EQOP
 #endif // !EAX_API_INCLUDED

+ 1 - 2
Engine/lib/openal-soft/al/eax/call.cpp

@@ -22,8 +22,7 @@ EaxCall::EaxCall(
     ALuint property_source_id,
     ALvoid* property_buffer,
     ALuint property_size)
-    : mCallType{type}, mVersion{0}, mPropertySetId{EaxCallPropertySetId::none}
-    , mIsDeferred{(property_id & deferred_flag) != 0}
+    : mCallType{type}, mIsDeferred{(property_id & deferred_flag) != 0}
     , mPropertyId{property_id & ~deferred_flag}, mPropertySourceId{property_source_id}
     , mPropertyBuffer{property_buffer}, mPropertyBufferSize{property_size}
 {

+ 17 - 17
Engine/lib/openal-soft/al/eax/call.h

@@ -31,16 +31,16 @@ public:
         ALvoid* property_buffer,
         ALuint property_size);
 
-    bool is_get() const noexcept { return mCallType == EaxCallType::get; }
-    bool is_deferred() const noexcept { return mIsDeferred; }
-    int get_version() const noexcept { return mVersion; }
-    EaxCallPropertySetId get_property_set_id() const noexcept { return mPropertySetId; }
-    ALuint get_property_id() const noexcept { return mPropertyId; }
-    ALuint get_property_al_name() const noexcept { return mPropertySourceId; }
-    EaxFxSlotIndex get_fx_slot_index() const noexcept { return mFxSlotIndex; }
+    [[nodiscard]] auto is_get() const noexcept -> bool { return mCallType == EaxCallType::get; }
+    [[nodiscard]] auto is_deferred() const noexcept -> bool { return mIsDeferred; }
+    [[nodiscard]] auto get_version() const noexcept -> int { return mVersion; }
+    [[nodiscard]] auto get_property_set_id() const noexcept -> EaxCallPropertySetId { return mPropertySetId; }
+    [[nodiscard]] auto get_property_id() const noexcept -> ALuint { return mPropertyId; }
+    [[nodiscard]] auto get_property_al_name() const noexcept -> ALuint { return mPropertySourceId; }
+    [[nodiscard]] auto get_fx_slot_index() const noexcept -> EaxFxSlotIndex { return mFxSlotIndex; }
 
     template<typename TException, typename TValue>
-    TValue& get_value() const
+    [[nodiscard]] auto get_value() const -> TValue&
     {
         if(mPropertyBufferSize < sizeof(TValue))
             fail_too_small();
@@ -49,32 +49,32 @@ public:
     }
 
     template<typename TValue>
-    al::span<TValue> get_values(size_t max_count) const
+    [[nodiscard]] auto get_values(size_t max_count) const -> al::span<TValue>
     {
         if(max_count == 0 || mPropertyBufferSize < sizeof(TValue))
             fail_too_small();
 
-        const auto count = minz(mPropertyBufferSize / sizeof(TValue), max_count);
-        return al::as_span(static_cast<TValue*>(mPropertyBuffer), count);
+        const auto count = std::min(mPropertyBufferSize/sizeof(TValue), max_count);
+        return {static_cast<TValue*>(mPropertyBuffer), count};
     }
 
     template<typename TValue>
-    al::span<TValue> get_values() const
+    [[nodiscard]] auto get_values() const -> al::span<TValue>
     {
-        return get_values<TValue>(~size_t{});
+        return get_values<TValue>(~0_uz);
     }
 
     template<typename TException, typename TValue>
-    void set_value(const TValue& value) const
+    auto set_value(const TValue& value) const -> void
     {
         get_value<TException, TValue>() = value;
     }
 
 private:
     const EaxCallType mCallType;
-    int mVersion;
-    EaxFxSlotIndex mFxSlotIndex;
-    EaxCallPropertySetId mPropertySetId;
+    int mVersion{};
+    EaxFxSlotIndex mFxSlotIndex{};
+    EaxCallPropertySetId mPropertySetId{EaxCallPropertySetId::none};
     bool mIsDeferred;
 
     const ALuint mPropertyId;

+ 182 - 143
Engine/lib/openal-soft/al/eax/effect.h

@@ -4,60 +4,56 @@
 
 #include <cassert>
 #include <memory>
+#include <variant>
 
 #include "alnumeric.h"
 #include "AL/al.h"
+#include "AL/alext.h"
 #include "core/effects/base.h"
 #include "call.h"
 
-struct EaxEffectErrorMessages
-{
+struct EaxEffectErrorMessages {
     static constexpr auto unknown_property_id() noexcept { return "Unknown property id."; }
     static constexpr auto unknown_version() noexcept { return "Unknown version."; }
 }; // EaxEffectErrorMessages
 
-/* TODO: Use std::variant (C++17). */
-enum class EaxEffectType {
-    None, Reverb, Chorus, Autowah, Compressor, Distortion, Echo, Equalizer, Flanger,
-    FrequencyShifter, Modulator, PitchShifter, VocalMorpher
-};
-struct EaxEffectProps {
-    EaxEffectType mType;
-    union {
-        EAXREVERBPROPERTIES mReverb;
-        EAXCHORUSPROPERTIES mChorus;
-        EAXAUTOWAHPROPERTIES mAutowah;
-        EAXAGCCOMPRESSORPROPERTIES mCompressor;
-        EAXDISTORTIONPROPERTIES mDistortion;
-        EAXECHOPROPERTIES mEcho;
-        EAXEQUALIZERPROPERTIES mEqualizer;
-        EAXFLANGERPROPERTIES mFlanger;
-        EAXFREQUENCYSHIFTERPROPERTIES mFrequencyShifter;
-        EAXRINGMODULATORPROPERTIES mModulator;
-        EAXPITCHSHIFTERPROPERTIES mPitchShifter;
-        EAXVOCALMORPHERPROPERTIES mVocalMorpher;
-    };
-};
+using EaxEffectProps = std::variant<std::monostate,
+    EAXREVERBPROPERTIES,
+    EAXCHORUSPROPERTIES,
+    EAXAUTOWAHPROPERTIES,
+    EAXAGCCOMPRESSORPROPERTIES,
+    EAXDISTORTIONPROPERTIES,
+    EAXECHOPROPERTIES,
+    EAXEQUALIZERPROPERTIES,
+    EAXFLANGERPROPERTIES,
+    EAXFREQUENCYSHIFTERPROPERTIES,
+    EAXRINGMODULATORPROPERTIES,
+    EAXPITCHSHIFTERPROPERTIES,
+    EAXVOCALMORPHERPROPERTIES>;
+
+template<typename... Ts>
+struct overloaded : Ts... { using Ts::operator()...; };
+
+template<typename... Ts>
+overloaded(Ts...) -> overloaded<Ts...>;
 
 constexpr ALenum EnumFromEaxEffectType(const EaxEffectProps &props)
 {
-    switch(props.mType)
-    {
-    case EaxEffectType::None: break;
-    case EaxEffectType::Reverb: return AL_EFFECT_EAXREVERB;
-    case EaxEffectType::Chorus: return AL_EFFECT_CHORUS;
-    case EaxEffectType::Autowah: return AL_EFFECT_AUTOWAH;
-    case EaxEffectType::Compressor: return AL_EFFECT_COMPRESSOR;
-    case EaxEffectType::Distortion: return AL_EFFECT_DISTORTION;
-    case EaxEffectType::Echo: return AL_EFFECT_ECHO;
-    case EaxEffectType::Equalizer: return AL_EFFECT_EQUALIZER;
-    case EaxEffectType::Flanger: return AL_EFFECT_FLANGER;
-    case EaxEffectType::FrequencyShifter: return AL_EFFECT_FREQUENCY_SHIFTER;
-    case EaxEffectType::Modulator: return AL_EFFECT_RING_MODULATOR;
-    case EaxEffectType::PitchShifter: return AL_EFFECT_PITCH_SHIFTER;
-    case EaxEffectType::VocalMorpher: return AL_EFFECT_VOCAL_MORPHER;
-    }
-    return AL_EFFECT_NULL;
+    return std::visit(overloaded{
+        [](const std::monostate&) noexcept { return AL_EFFECT_NULL; },
+        [](const EAXREVERBPROPERTIES&) noexcept { return AL_EFFECT_EAXREVERB; },
+        [](const EAXCHORUSPROPERTIES&) noexcept { return AL_EFFECT_CHORUS; },
+        [](const EAXAUTOWAHPROPERTIES&) noexcept { return AL_EFFECT_AUTOWAH; },
+        [](const EAXAGCCOMPRESSORPROPERTIES&) noexcept { return AL_EFFECT_COMPRESSOR; },
+        [](const EAXDISTORTIONPROPERTIES&) noexcept { return AL_EFFECT_DISTORTION; },
+        [](const EAXECHOPROPERTIES&) noexcept { return AL_EFFECT_ECHO; },
+        [](const EAXEQUALIZERPROPERTIES&) noexcept { return AL_EFFECT_EQUALIZER; },
+        [](const EAXFLANGERPROPERTIES&) noexcept { return AL_EFFECT_FLANGER; },
+        [](const EAXFREQUENCYSHIFTERPROPERTIES&) noexcept { return AL_EFFECT_FREQUENCY_SHIFTER; },
+        [](const EAXRINGMODULATORPROPERTIES&) noexcept { return AL_EFFECT_RING_MODULATOR; },
+        [](const EAXPITCHSHIFTERPROPERTIES&) noexcept { return AL_EFFECT_PITCH_SHIFTER; },
+        [](const EAXVOCALMORPHERPROPERTIES&) noexcept { return AL_EFFECT_VOCAL_MORPHER; }
+    }, props);
 }
 
 struct EaxReverbCommitter {
@@ -105,7 +101,6 @@ struct EaxReverbCommitter {
     bool commit(const EAX_REVERBPROPERTIES &props);
     bool commit(const EAX20LISTENERPROPERTIES &props);
     bool commit(const EAXREVERBPROPERTIES &props);
-    bool commit(const EaxEffectProps &props);
 
     static void SetDefaults(EAX_REVERBPROPERTIES &props);
     static void SetDefaults(EAX20LISTENERPROPERTIES &props);
@@ -115,16 +110,13 @@ struct EaxReverbCommitter {
     static void Get(const EaxCall &call, const EAX_REVERBPROPERTIES &props);
     static void Get(const EaxCall &call, const EAX20LISTENERPROPERTIES &props);
     static void Get(const EaxCall &call, const EAXREVERBPROPERTIES &props);
-    static void Get(const EaxCall &call, const EaxEffectProps &props);
 
     static void Set(const EaxCall &call, EAX_REVERBPROPERTIES &props);
     static void Set(const EaxCall &call, EAX20LISTENERPROPERTIES &props);
     static void Set(const EaxCall &call, EAXREVERBPROPERTIES &props);
-    static void Set(const EaxCall &call, EaxEffectProps &props);
 
-    static void translate(const EAX_REVERBPROPERTIES& src, EaxEffectProps& dst) noexcept;
-    static void translate(const EAX20LISTENERPROPERTIES& src, EaxEffectProps& dst) noexcept;
-    static void translate(const EAXREVERBPROPERTIES& src, EaxEffectProps& dst) noexcept;
+    static void translate(const EAX_REVERBPROPERTIES& src, EAXREVERBPROPERTIES& dst) noexcept;
+    static void translate(const EAX20LISTENERPROPERTIES& src, EAXREVERBPROPERTIES& dst) noexcept;
 };
 
 template<typename T>
@@ -149,51 +141,137 @@ struct EaxCommitter {
     [[noreturn]] static void fail(const char *message);
     [[noreturn]] static void fail_unknown_property_id()
     { fail(EaxEffectErrorMessages::unknown_property_id()); }
-
-    bool commit(const EaxEffectProps &props);
-
-    static void SetDefaults(EaxEffectProps &props);
-    static void Get(const EaxCall &call, const EaxEffectProps &props);
-    static void Set(const EaxCall &call, EaxEffectProps &props);
 };
 
 struct EaxAutowahCommitter : public EaxCommitter<EaxAutowahCommitter> {
     using EaxCommitter<EaxAutowahCommitter>::EaxCommitter;
+
+    bool commit(const EAXAUTOWAHPROPERTIES &props);
+
+    static void SetDefaults(EaxEffectProps &props);
+    static void Get(const EaxCall &call, const EAXAUTOWAHPROPERTIES &props);
+    static void Set(const EaxCall &call, EAXAUTOWAHPROPERTIES &props);
 };
 struct EaxChorusCommitter : public EaxCommitter<EaxChorusCommitter> {
     using EaxCommitter<EaxChorusCommitter>::EaxCommitter;
+
+    bool commit(const EAXCHORUSPROPERTIES &props);
+
+    static void SetDefaults(EaxEffectProps &props);
+    static void Get(const EaxCall &call, const EAXCHORUSPROPERTIES &props);
+    static void Set(const EaxCall &call, EAXCHORUSPROPERTIES &props);
 };
 struct EaxCompressorCommitter : public EaxCommitter<EaxCompressorCommitter> {
     using EaxCommitter<EaxCompressorCommitter>::EaxCommitter;
+
+    bool commit(const EAXAGCCOMPRESSORPROPERTIES &props);
+
+    static void SetDefaults(EaxEffectProps &props);
+    static void Get(const EaxCall &call, const EAXAGCCOMPRESSORPROPERTIES &props);
+    static void Set(const EaxCall &call, EAXAGCCOMPRESSORPROPERTIES &props);
 };
 struct EaxDistortionCommitter : public EaxCommitter<EaxDistortionCommitter> {
     using EaxCommitter<EaxDistortionCommitter>::EaxCommitter;
+
+    bool commit(const EAXDISTORTIONPROPERTIES &props);
+
+    static void SetDefaults(EaxEffectProps &props);
+    static void Get(const EaxCall &call, const EAXDISTORTIONPROPERTIES &props);
+    static void Set(const EaxCall &call, EAXDISTORTIONPROPERTIES &props);
 };
 struct EaxEchoCommitter : public EaxCommitter<EaxEchoCommitter> {
     using EaxCommitter<EaxEchoCommitter>::EaxCommitter;
+
+    bool commit(const EAXECHOPROPERTIES &props);
+
+    static void SetDefaults(EaxEffectProps &props);
+    static void Get(const EaxCall &call, const EAXECHOPROPERTIES &props);
+    static void Set(const EaxCall &call, EAXECHOPROPERTIES &props);
 };
 struct EaxEqualizerCommitter : public EaxCommitter<EaxEqualizerCommitter> {
     using EaxCommitter<EaxEqualizerCommitter>::EaxCommitter;
+
+    bool commit(const EAXEQUALIZERPROPERTIES &props);
+
+    static void SetDefaults(EaxEffectProps &props);
+    static void Get(const EaxCall &call, const EAXEQUALIZERPROPERTIES &props);
+    static void Set(const EaxCall &call, EAXEQUALIZERPROPERTIES &props);
 };
 struct EaxFlangerCommitter : public EaxCommitter<EaxFlangerCommitter> {
     using EaxCommitter<EaxFlangerCommitter>::EaxCommitter;
+
+    bool commit(const EAXFLANGERPROPERTIES &props);
+
+    static void SetDefaults(EaxEffectProps &props);
+    static void Get(const EaxCall &call, const EAXFLANGERPROPERTIES &props);
+    static void Set(const EaxCall &call, EAXFLANGERPROPERTIES &props);
 };
 struct EaxFrequencyShifterCommitter : public EaxCommitter<EaxFrequencyShifterCommitter> {
     using EaxCommitter<EaxFrequencyShifterCommitter>::EaxCommitter;
+
+    bool commit(const EAXFREQUENCYSHIFTERPROPERTIES &props);
+
+    static void SetDefaults(EaxEffectProps &props);
+    static void Get(const EaxCall &call, const EAXFREQUENCYSHIFTERPROPERTIES &props);
+    static void Set(const EaxCall &call, EAXFREQUENCYSHIFTERPROPERTIES &props);
 };
 struct EaxModulatorCommitter : public EaxCommitter<EaxModulatorCommitter> {
     using EaxCommitter<EaxModulatorCommitter>::EaxCommitter;
+
+    bool commit(const EAXRINGMODULATORPROPERTIES &props);
+
+    static void SetDefaults(EaxEffectProps &props);
+    static void Get(const EaxCall &call, const EAXRINGMODULATORPROPERTIES &props);
+    static void Set(const EaxCall &call, EAXRINGMODULATORPROPERTIES &props);
 };
 struct EaxPitchShifterCommitter : public EaxCommitter<EaxPitchShifterCommitter> {
     using EaxCommitter<EaxPitchShifterCommitter>::EaxCommitter;
+
+    bool commit(const EAXPITCHSHIFTERPROPERTIES &props);
+
+    static void SetDefaults(EaxEffectProps &props);
+    static void Get(const EaxCall &call, const EAXPITCHSHIFTERPROPERTIES &props);
+    static void Set(const EaxCall &call, EAXPITCHSHIFTERPROPERTIES &props);
 };
 struct EaxVocalMorpherCommitter : public EaxCommitter<EaxVocalMorpherCommitter> {
     using EaxCommitter<EaxVocalMorpherCommitter>::EaxCommitter;
+
+    bool commit(const EAXVOCALMORPHERPROPERTIES &props);
+
+    static void SetDefaults(EaxEffectProps &props);
+    static void Get(const EaxCall &call, const EAXVOCALMORPHERPROPERTIES &props);
+    static void Set(const EaxCall &call, EAXVOCALMORPHERPROPERTIES &props);
 };
 struct EaxNullCommitter : public EaxCommitter<EaxNullCommitter> {
     using EaxCommitter<EaxNullCommitter>::EaxCommitter;
+
+    bool commit(const std::monostate &props);
+
+    static void SetDefaults(EaxEffectProps &props);
+    static void Get(const EaxCall &call, const std::monostate &props);
+    static void Set(const EaxCall &call, std::monostate &props);
 };
 
+template<typename T>
+struct CommitterFromProps { };
+
+template<> struct CommitterFromProps<std::monostate> { using type = EaxNullCommitter; };
+template<> struct CommitterFromProps<EAXREVERBPROPERTIES> { using type = EaxReverbCommitter; };
+template<> struct CommitterFromProps<EAXCHORUSPROPERTIES> { using type = EaxChorusCommitter; };
+template<> struct CommitterFromProps<EAXAGCCOMPRESSORPROPERTIES> { using type = EaxCompressorCommitter; };
+template<> struct CommitterFromProps<EAXAUTOWAHPROPERTIES> { using type = EaxAutowahCommitter; };
+template<> struct CommitterFromProps<EAXDISTORTIONPROPERTIES> { using type = EaxDistortionCommitter; };
+template<> struct CommitterFromProps<EAXECHOPROPERTIES> { using type = EaxEchoCommitter; };
+template<> struct CommitterFromProps<EAXEQUALIZERPROPERTIES> { using type = EaxEqualizerCommitter; };
+template<> struct CommitterFromProps<EAXFLANGERPROPERTIES> { using type = EaxFlangerCommitter; };
+template<> struct CommitterFromProps<EAXFREQUENCYSHIFTERPROPERTIES> { using type = EaxFrequencyShifterCommitter; };
+template<> struct CommitterFromProps<EAXRINGMODULATORPROPERTIES> { using type = EaxModulatorCommitter; };
+template<> struct CommitterFromProps<EAXPITCHSHIFTERPROPERTIES> { using type = EaxPitchShifterCommitter; };
+template<> struct CommitterFromProps<EAXVOCALMORPHERPROPERTIES> { using type = EaxVocalMorpherCommitter; };
+
+template<typename T>
+using CommitterFor = typename CommitterFromProps<std::remove_cv_t<std::remove_reference_t<T>>>::type;
+
 
 class EaxEffect {
 public:
@@ -238,51 +316,39 @@ public:
     State4 state5_{};
 
 
-    template<typename T, typename ...Args>
-    void call_set_defaults(Args&& ...args)
-    { return T::SetDefaults(std::forward<Args>(args)...); }
-
-    void call_set_defaults(const ALenum altype, EaxEffectProps &props)
+    static void call_set_defaults(const ALenum altype, EaxEffectProps &props)
     {
-        if(altype == AL_EFFECT_EAXREVERB)
-            return call_set_defaults<EaxReverbCommitter>(props);
-        if(altype == AL_EFFECT_CHORUS)
-            return call_set_defaults<EaxChorusCommitter>(props);
-        if(altype == AL_EFFECT_AUTOWAH)
-            return call_set_defaults<EaxAutowahCommitter>(props);
-        if(altype == AL_EFFECT_COMPRESSOR)
-            return call_set_defaults<EaxCompressorCommitter>(props);
-        if(altype == AL_EFFECT_DISTORTION)
-            return call_set_defaults<EaxDistortionCommitter>(props);
-        if(altype == AL_EFFECT_ECHO)
-            return call_set_defaults<EaxEchoCommitter>(props);
-        if(altype == AL_EFFECT_EQUALIZER)
-            return call_set_defaults<EaxEqualizerCommitter>(props);
-        if(altype == AL_EFFECT_FLANGER)
-            return call_set_defaults<EaxFlangerCommitter>(props);
-        if(altype == AL_EFFECT_FREQUENCY_SHIFTER)
-            return call_set_defaults<EaxFrequencyShifterCommitter>(props);
-        if(altype == AL_EFFECT_RING_MODULATOR)
-            return call_set_defaults<EaxModulatorCommitter>(props);
-        if(altype == AL_EFFECT_PITCH_SHIFTER)
-            return call_set_defaults<EaxPitchShifterCommitter>(props);
-        if(altype == AL_EFFECT_VOCAL_MORPHER)
-            return call_set_defaults<EaxVocalMorpherCommitter>(props);
-        return call_set_defaults<EaxNullCommitter>(props);
+        switch(altype)
+        {
+        case AL_EFFECT_EAXREVERB: return EaxReverbCommitter::SetDefaults(props);
+        case AL_EFFECT_CHORUS: return EaxChorusCommitter::SetDefaults(props);
+        case AL_EFFECT_AUTOWAH: return EaxAutowahCommitter::SetDefaults(props);
+        case AL_EFFECT_COMPRESSOR: return EaxCompressorCommitter::SetDefaults(props);
+        case AL_EFFECT_DISTORTION: return EaxDistortionCommitter::SetDefaults(props);
+        case AL_EFFECT_ECHO: return EaxEchoCommitter::SetDefaults(props);
+        case AL_EFFECT_EQUALIZER: return EaxEqualizerCommitter::SetDefaults(props);
+        case AL_EFFECT_FLANGER: return EaxFlangerCommitter::SetDefaults(props);
+        case AL_EFFECT_FREQUENCY_SHIFTER: return EaxFrequencyShifterCommitter::SetDefaults(props);
+        case AL_EFFECT_RING_MODULATOR: return EaxModulatorCommitter::SetDefaults(props);
+        case AL_EFFECT_PITCH_SHIFTER: return EaxPitchShifterCommitter::SetDefaults(props);
+        case AL_EFFECT_VOCAL_MORPHER: return EaxVocalMorpherCommitter::SetDefaults(props);
+        case AL_EFFECT_NULL: break;
+        }
+        return EaxNullCommitter::SetDefaults(props);
     }
 
     template<typename T>
     void init()
     {
-        call_set_defaults<EaxReverbCommitter>(state1_.d);
+        EaxReverbCommitter::SetDefaults(state1_.d);
         state1_.i = state1_.d;
-        call_set_defaults<EaxReverbCommitter>(state2_.d);
+        EaxReverbCommitter::SetDefaults(state2_.d);
         state2_.i = state2_.d;
-        call_set_defaults<EaxReverbCommitter>(state3_.d);
+        EaxReverbCommitter::SetDefaults(state3_.d);
         state3_.i = state3_.d;
-        call_set_defaults<T>(state4_.d);
+        T::SetDefaults(state4_.d);
         state4_.i = state4_.d;
-        call_set_defaults<T>(state5_.d);
+        T::SetDefaults(state5_.d);
         state5_.i = state5_.d;
     }
 
@@ -290,9 +356,9 @@ public:
     {
         switch(eax_version)
         {
-        case 1: call_set_defaults<EaxReverbCommitter>(state1_.d); break;
-        case 2: call_set_defaults<EaxReverbCommitter>(state2_.d); break;
-        case 3: call_set_defaults<EaxReverbCommitter>(state3_.d); break;
+        case 1: EaxReverbCommitter::SetDefaults(state1_.d); break;
+        case 2: EaxReverbCommitter::SetDefaults(state2_.d); break;
+        case 3: EaxReverbCommitter::SetDefaults(state3_.d); break;
         case 4: call_set_defaults(altype, state4_.d); break;
         case 5: call_set_defaults(altype, state5_.d); break;
         }
@@ -300,47 +366,20 @@ public:
     }
 
 
-#define EAXCALL(T, Callable, ...)                                             \
-    if(T == EaxEffectType::Reverb)                                            \
-        return Callable<EaxReverbCommitter>(__VA_ARGS__);                     \
-    if(T == EaxEffectType::Chorus)                                            \
-        return Callable<EaxChorusCommitter>(__VA_ARGS__);                     \
-    if(T == EaxEffectType::Autowah)                                           \
-        return Callable<EaxAutowahCommitter>(__VA_ARGS__);                    \
-    if(T == EaxEffectType::Compressor)                                        \
-        return Callable<EaxCompressorCommitter>(__VA_ARGS__);                 \
-    if(T == EaxEffectType::Distortion)                                        \
-        return Callable<EaxDistortionCommitter>(__VA_ARGS__);                 \
-    if(T == EaxEffectType::Echo)                                              \
-        return Callable<EaxEchoCommitter>(__VA_ARGS__);                       \
-    if(T == EaxEffectType::Equalizer)                                         \
-        return Callable<EaxEqualizerCommitter>(__VA_ARGS__);                  \
-    if(T == EaxEffectType::Flanger)                                           \
-        return Callable<EaxFlangerCommitter>(__VA_ARGS__);                    \
-    if(T == EaxEffectType::FrequencyShifter)                                  \
-        return Callable<EaxFrequencyShifterCommitter>(__VA_ARGS__);           \
-    if(T == EaxEffectType::Modulator)                                         \
-        return Callable<EaxModulatorCommitter>(__VA_ARGS__);                  \
-    if(T == EaxEffectType::PitchShifter)                                      \
-        return Callable<EaxPitchShifterCommitter>(__VA_ARGS__);               \
-    if(T == EaxEffectType::VocalMorpher)                                      \
-        return Callable<EaxVocalMorpherCommitter>(__VA_ARGS__);               \
-    return Callable<EaxNullCommitter>(__VA_ARGS__)
-
-    template<typename T, typename ...Args>
-    static void call_set(Args&& ...args)
-    { return T::Set(std::forward<Args>(args)...); }
-
     static void call_set(const EaxCall &call, EaxEffectProps &props)
-    { EAXCALL(props.mType, call_set, call, props); }
+    {
+        return std::visit([&](auto &arg)
+        { return CommitterFor<decltype(arg)>::Set(call, arg); },
+        props);
+    }
 
     void set(const EaxCall &call)
     {
         switch(call.get_version())
         {
-        case 1: call_set<EaxReverbCommitter>(call, state1_.d); break;
-        case 2: call_set<EaxReverbCommitter>(call, state2_.d); break;
-        case 3: call_set<EaxReverbCommitter>(call, state3_.d); break;
+        case 1: EaxReverbCommitter::Set(call, state1_.d); break;
+        case 2: EaxReverbCommitter::Set(call, state2_.d); break;
+        case 3: EaxReverbCommitter::Set(call, state3_.d); break;
         case 4: call_set(call, state4_.d); break;
         case 5: call_set(call, state5_.d); break;
         }
@@ -348,32 +387,32 @@ public:
     }
 
 
-    template<typename T, typename ...Args>
-    static void call_get(Args&& ...args)
-    { return T::Get(std::forward<Args>(args)...); }
-
     static void call_get(const EaxCall &call, const EaxEffectProps &props)
-    { EAXCALL(props.mType, call_get, call, props); }
+    {
+        return std::visit([&](auto &arg)
+        { return CommitterFor<decltype(arg)>::Get(call, arg); },
+        props);
+    }
 
-    void get(const EaxCall &call)
+    void get(const EaxCall &call) const
     {
         switch(call.get_version())
         {
-        case 1: call_get<EaxReverbCommitter>(call, state1_.d); break;
-        case 2: call_get<EaxReverbCommitter>(call, state2_.d); break;
-        case 3: call_get<EaxReverbCommitter>(call, state3_.d); break;
+        case 1: EaxReverbCommitter::Get(call, state1_.d); break;
+        case 2: EaxReverbCommitter::Get(call, state2_.d); break;
+        case 3: EaxReverbCommitter::Get(call, state3_.d); break;
         case 4: call_get(call, state4_.d); break;
         case 5: call_get(call, state5_.d); break;
         }
     }
 
 
-    template<typename T, typename ...Args>
-    bool call_commit(Args&& ...args)
-    { return T{props_, al_effect_props_}.commit(std::forward<Args>(args)...); }
-
     bool call_commit(const EaxEffectProps &props)
-    { EAXCALL(props.mType, call_commit, props); }
+    {
+        return std::visit([&](auto &arg)
+        { return CommitterFor<decltype(arg)>{props_, al_effect_props_}.commit(arg); },
+        props);
+    }
 
     bool commit(int eax_version)
     {
@@ -388,15 +427,15 @@ public:
         {
         case 1:
             state1_.i = state1_.d;
-            ret |= call_commit<EaxReverbCommitter>(state1_.d);
+            ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state1_.d);
             break;
         case 2:
             state2_.i = state2_.d;
-            ret |= call_commit<EaxReverbCommitter>(state2_.d);
+            ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state2_.d);
             break;
         case 3:
             state3_.i = state3_.d;
-            ret |= call_commit<EaxReverbCommitter>(state3_.d);
+            ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state3_.d);
             break;
         case 4:
             state4_.i = state4_.d;

+ 10 - 37
Engine/lib/openal-soft/al/eax/exception.cpp

@@ -6,54 +6,27 @@
 #include <string>
 
 
-EaxException::EaxException(const char *context, const char *message)
+EaxException::EaxException(std::string_view context, std::string_view message)
     : std::runtime_error{make_message(context, message)}
 {
 }
 EaxException::~EaxException() = default;
 
 
-std::string EaxException::make_message(const char *context, const char *message)
+std::string EaxException::make_message(std::string_view context, std::string_view message)
 {
-    const auto context_size = (context ? std::string::traits_type::length(context) : 0);
-    const auto has_contex = (context_size > 0);
-
-    const auto message_size = (message ? std::string::traits_type::length(message) : 0);
-    const auto has_message = (message_size > 0);
-
-    if (!has_contex && !has_message)
-    {
-        return std::string{};
-    }
-
-    static constexpr char left_prefix[] = "[";
-    const auto left_prefix_size = std::string::traits_type::length(left_prefix);
-
-    static constexpr char right_prefix[] = "] ";
-    const auto right_prefix_size = std::string::traits_type::length(right_prefix);
-
-    const auto what_size =
-        (
-            has_contex ?
-            left_prefix_size + context_size + right_prefix_size :
-            0) +
-        message_size +
-        1;
-
     auto what = std::string{};
-    what.reserve(what_size);
-
-    if (has_contex)
-    {
-        what.append(left_prefix, left_prefix_size);
-        what.append(context, context_size);
-        what.append(right_prefix, right_prefix_size);
-    }
+    if(context.empty() && message.empty())
+        return what;
 
-    if (has_message)
+    what.reserve((!context.empty() ? context.size() + 3 : 0) + message.length() + 1);
+    if(!context.empty())
     {
-        what.append(message, message_size);
+        what += "[";
+        what += context;
+        what += "] ";
     }
+    what += message;
 
     return what;
 }

+ 10 - 5
Engine/lib/openal-soft/al/eax/exception.h

@@ -1,18 +1,23 @@
 #ifndef EAX_EXCEPTION_INCLUDED
 #define EAX_EXCEPTION_INCLUDED
 
-
 #include <stdexcept>
 #include <string>
+#include <string_view>
 
 
 class EaxException : public std::runtime_error {
-    static std::string make_message(const char *context, const char *message);
+    static std::string make_message(std::string_view context, std::string_view message);
 
 public:
-    EaxException(const char *context, const char *message);
+    EaxException() = delete;
+    EaxException(const EaxException&) = default;
+    EaxException(EaxException&&) = default;
+    EaxException(std::string_view context, std::string_view message);
     ~EaxException() override;
-}; // EaxException
 
+    auto operator=(const EaxException&) -> EaxException& = default;
+    auto operator=(EaxException&&) -> EaxException& = default;
+};
 
-#endif // !EAX_EXCEPTION_INCLUDED
+#endif /* EAX_EXCEPTION_INCLUDED */

+ 3 - 4
Engine/lib/openal-soft/al/eax/fx_slot_index.h

@@ -3,17 +3,16 @@
 
 
 #include <cstddef>
+#include <optional>
 
-#include "aloptional.h"
 #include "api.h"
 
 
 using EaxFxSlotIndexValue = std::size_t;
 
-class EaxFxSlotIndex : public al::optional<EaxFxSlotIndexValue>
-{
+class EaxFxSlotIndex : public std::optional<EaxFxSlotIndexValue> {
 public:
-    using al::optional<EaxFxSlotIndexValue>::optional;
+    using std::optional<EaxFxSlotIndexValue>::optional;
 
     EaxFxSlotIndex& operator=(const EaxFxSlotIndexValue &value) { set(value); return *this; }
     EaxFxSlotIndex& operator=(const GUID &guid) { set(guid); return *this; }

+ 4 - 10
Engine/lib/openal-soft/al/eax/fx_slots.h

@@ -6,13 +6,10 @@
 
 #include "al/auxeffectslot.h"
 
-#include "api.h"
-#include "call.h"
 #include "fx_slot_index.h"
 
 
-class EaxFxSlots
-{
+class EaxFxSlots {
 public:
     void initialize(ALCcontext& al_context);
 
@@ -25,11 +22,9 @@ public:
     }
 
 
-    const ALeffectslot& get(
-        EaxFxSlotIndex index) const;
+    [[nodiscard]] auto get(EaxFxSlotIndex index) const -> const ALeffectslot&;
 
-    ALeffectslot& get(
-        EaxFxSlotIndex index);
+    [[nodiscard]] auto get(EaxFxSlotIndex index) -> ALeffectslot&;
 
 private:
     using Items = std::array<EaxAlEffectSlotUPtr, EAX_MAX_FXSLOTS>;
@@ -39,8 +34,7 @@ private:
 
 
     [[noreturn]]
-    static void fail(
-        const char* message);
+    static void fail(const char* message);
 
     void initialize_fx_slots(ALCcontext& al_context);
 }; // EaxFxSlots

+ 0 - 21
Engine/lib/openal-soft/al/eax/globals.cpp

@@ -1,21 +0,0 @@
-#include "config.h"
-
-#include "globals.h"
-
-
-bool eax_g_is_enabled = true;
-
-
-const char eax1_ext_name[] = "EAX";
-const char eax2_ext_name[] = "EAX2.0";
-const char eax3_ext_name[] = "EAX3.0";
-const char eax4_ext_name[] = "EAX4.0";
-const char eax5_ext_name[] = "EAX5.0";
-
-const char eax_x_ram_ext_name[] = "EAX-RAM";
-
-const char eax_eax_set_func_name[] = "EAXSet";
-const char eax_eax_get_func_name[] = "EAXGet";
-
-const char eax_eax_set_buffer_mode_func_name[] = "EAXSetBufferMode";
-const char eax_eax_get_buffer_mode_func_name[] = "EAXGetBufferMode";

+ 2 - 18
Engine/lib/openal-soft/al/eax/globals.h

@@ -1,22 +1,6 @@
 #ifndef EAX_GLOBALS_INCLUDED
 #define EAX_GLOBALS_INCLUDED
 
+inline bool eax_g_is_enabled{true};
 
-extern bool eax_g_is_enabled;
-
-
-extern const char eax1_ext_name[];
-extern const char eax2_ext_name[];
-extern const char eax3_ext_name[];
-extern const char eax4_ext_name[];
-extern const char eax5_ext_name[];
-
-extern const char eax_x_ram_ext_name[];
-
-extern const char eax_eax_set_func_name[];
-extern const char eax_eax_get_func_name[];
-
-extern const char eax_eax_set_buffer_mode_func_name[];
-extern const char eax_eax_get_buffer_mode_func_name[];
-
-#endif // !EAX_GLOBALS_INCLUDED
+#endif /* EAX_GLOBALS_INCLUDED */

+ 4 - 4
Engine/lib/openal-soft/al/eax/utils.cpp

@@ -5,10 +5,11 @@
 #include <cassert>
 #include <exception>
 
+#include "alstring.h"
 #include "core/logging.h"
 
 
-void eax_log_exception(const char *message) noexcept
+void eax_log_exception(std::string_view message) noexcept
 {
     const auto exception_ptr = std::current_exception();
     assert(exception_ptr);
@@ -17,10 +18,9 @@ void eax_log_exception(const char *message) noexcept
         std::rethrow_exception(exception_ptr);
     }
     catch(const std::exception& ex) {
-        const auto ex_message = ex.what();
-        ERR("%s %s\n", message ? message : "", ex_message);
+        ERR("%.*s %s\n", al::sizei(message), message.data(), ex.what());
     }
     catch(...) {
-        ERR("%s %s\n", message ? message : "", "Generic exception.");
+        ERR("%.*s %s\n", al::sizei(message), message.data(), "Generic exception.");
     }
 }

+ 6 - 6
Engine/lib/openal-soft/al/eax/utils.h

@@ -4,8 +4,11 @@
 #include <algorithm>
 #include <cstdint>
 #include <string>
+#include <string_view>
 #include <type_traits>
 
+#include "opthelpers.h"
+
 using EaxDirtyFlags = unsigned int;
 
 struct EaxAlLowPassParam {
@@ -13,16 +16,13 @@ struct EaxAlLowPassParam {
     float gain_hf;
 };
 
-void eax_log_exception(const char *message) noexcept;
+void eax_log_exception(std::string_view message) noexcept;
 
 template<typename TException, typename TValue>
-void eax_validate_range(
-    const char* value_name,
-    const TValue& value,
-    const TValue& min_value,
+void eax_validate_range(std::string_view value_name, const TValue& value, const TValue& min_value,
     const TValue& max_value)
 {
-    if (value >= min_value && value <= max_value)
+    if(value >= min_value && value <= max_value) LIKELY
         return;
 
     const auto message =

+ 2 - 10
Engine/lib/openal-soft/al/eax/x_ram.h

@@ -24,15 +24,7 @@ constexpr auto AL_STORAGE_AUTOMATIC_NAME = "AL_STORAGE_AUTOMATIC";
 constexpr auto AL_STORAGE_HARDWARE_NAME = "AL_STORAGE_HARDWARE";
 constexpr auto AL_STORAGE_ACCESSIBLE_NAME = "AL_STORAGE_ACCESSIBLE";
 
-
-ALboolean AL_APIENTRY EAXSetBufferMode(
-    ALsizei n,
-    const ALuint* buffers,
-    ALint value);
-
-ALenum AL_APIENTRY EAXGetBufferMode(
-    ALuint buffer,
-    ALint* pReserved);
-
+ALboolean AL_APIENTRY EAXSetBufferMode(ALsizei n, const ALuint *buffers, ALint value) noexcept;
+ALenum AL_APIENTRY EAXGetBufferMode(ALuint buffer, ALint *pReserved) noexcept;
 
 #endif // !EAX_X_RAM_INCLUDED

+ 336 - 367
Engine/lib/openal-soft/al/effect.cpp

@@ -28,9 +28,13 @@
 #include <iterator>
 #include <memory>
 #include <mutex>
-#include <new>
 #include <numeric>
+#include <string>
+#include <type_traits>
+#include <unordered_map>
 #include <utility>
+#include <variant>
+#include <vector>
 
 #include "AL/al.h"
 #include "AL/alc.h"
@@ -38,26 +42,23 @@
 #include "AL/efx-presets.h"
 #include "AL/efx.h"
 
+#include "al/effects/effects.h"
 #include "albit.h"
 #include "alc/context.h"
 #include "alc/device.h"
-#include "alc/effects/base.h"
 #include "alc/inprogext.h"
 #include "almalloc.h"
 #include "alnumeric.h"
+#include "alspan.h"
 #include "alstring.h"
-#include "core/except.h"
 #include "core/logging.h"
+#include "direct_defs.h"
+#include "error.h"
+#include "intrusive_ptr.h"
 #include "opthelpers.h"
-#include "vector.h"
 
-#ifdef ALSOFT_EAX
-#include <cassert>
 
-#include "eax/exception.h"
-#endif // ALSOFT_EAX
-
-const EffectList gEffectList[16]{
+const std::array<EffectList,16> gEffectList{{
     { "eaxreverb",   EAXREVERB_EFFECT,   AL_EFFECT_EAXREVERB },
     { "reverb",      REVERB_EFFECT,      AL_EFFECT_REVERB },
     { "autowah",     AUTOWAH_EFFECT,     AL_EFFECT_AUTOWAH },
@@ -73,95 +74,74 @@ const EffectList gEffectList[16]{
     { "vmorpher",    VMORPHER_EFFECT,    AL_EFFECT_VOCAL_MORPHER },
     { "dedicated",   DEDICATED_EFFECT,   AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT },
     { "dedicated",   DEDICATED_EFFECT,   AL_EFFECT_DEDICATED_DIALOGUE },
-    { "convolution", CONVOLUTION_EFFECT, AL_EFFECT_CONVOLUTION_REVERB_SOFT },
-};
-
-bool DisabledEffects[MAX_EFFECTS];
-
+    { "convolution", CONVOLUTION_EFFECT, AL_EFFECT_CONVOLUTION_SOFT },
+}};
 
-effect_exception::effect_exception(ALenum code, const char *msg, ...) : mErrorCode{code}
-{
-    std::va_list args;
-    va_start(args, msg);
-    setMessage(msg, args);
-    va_end(args);
-}
-effect_exception::~effect_exception() = default;
 
 namespace {
 
-struct EffectPropsItem {
-    ALenum Type;
-    const EffectProps &DefaultProps;
-    const EffectVtable &Vtable;
-};
-constexpr EffectPropsItem EffectPropsList[] = {
-    { AL_EFFECT_NULL, NullEffectProps, NullEffectVtable },
-    { AL_EFFECT_EAXREVERB, ReverbEffectProps, ReverbEffectVtable },
-    { AL_EFFECT_REVERB, StdReverbEffectProps, StdReverbEffectVtable },
-    { AL_EFFECT_AUTOWAH, AutowahEffectProps, AutowahEffectVtable },
-    { AL_EFFECT_CHORUS, ChorusEffectProps, ChorusEffectVtable },
-    { AL_EFFECT_COMPRESSOR, CompressorEffectProps, CompressorEffectVtable },
-    { AL_EFFECT_DISTORTION, DistortionEffectProps, DistortionEffectVtable },
-    { AL_EFFECT_ECHO, EchoEffectProps, EchoEffectVtable },
-    { AL_EFFECT_EQUALIZER, EqualizerEffectProps, EqualizerEffectVtable },
-    { AL_EFFECT_FLANGER, FlangerEffectProps, FlangerEffectVtable },
-    { AL_EFFECT_FREQUENCY_SHIFTER, FshifterEffectProps, FshifterEffectVtable },
-    { AL_EFFECT_RING_MODULATOR, ModulatorEffectProps, ModulatorEffectVtable },
-    { AL_EFFECT_PITCH_SHIFTER, PshifterEffectProps, PshifterEffectVtable },
-    { AL_EFFECT_VOCAL_MORPHER, VmorpherEffectProps, VmorpherEffectVtable },
-    { AL_EFFECT_DEDICATED_DIALOGUE, DedicatedEffectProps, DedicatedEffectVtable },
-    { AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT, DedicatedEffectProps, DedicatedEffectVtable },
-    { AL_EFFECT_CONVOLUTION_REVERB_SOFT, ConvolutionEffectProps, ConvolutionEffectVtable },
-};
-
-
-void ALeffect_setParami(ALeffect *effect, ALenum param, int value)
-{ effect->vtab->setParami(&effect->Props, param, value); }
-void ALeffect_setParamiv(ALeffect *effect, ALenum param, const int *values)
-{ effect->vtab->setParamiv(&effect->Props, param, values); }
-void ALeffect_setParamf(ALeffect *effect, ALenum param, float value)
-{ effect->vtab->setParamf(&effect->Props, param, value); }
-void ALeffect_setParamfv(ALeffect *effect, ALenum param, const float *values)
-{ effect->vtab->setParamfv(&effect->Props, param, values); }
-
-void ALeffect_getParami(const ALeffect *effect, ALenum param, int *value)
-{ effect->vtab->getParami(&effect->Props, param, value); }
-void ALeffect_getParamiv(const ALeffect *effect, ALenum param, int *values)
-{ effect->vtab->getParamiv(&effect->Props, param, values); }
-void ALeffect_getParamf(const ALeffect *effect, ALenum param, float *value)
-{ effect->vtab->getParamf(&effect->Props, param, value); }
-void ALeffect_getParamfv(const ALeffect *effect, ALenum param, float *values)
-{ effect->vtab->getParamfv(&effect->Props, param, values); }
-
+using SubListAllocator = al::allocator<std::array<ALeffect,64>>;
 
-const EffectPropsItem *getEffectPropsItemByType(ALenum type)
+constexpr auto GetDefaultProps(ALenum type) noexcept -> const EffectProps&
 {
-    auto iter = std::find_if(std::begin(EffectPropsList), std::end(EffectPropsList),
-        [type](const EffectPropsItem &item) noexcept -> bool
-        { return item.Type == type; });
-    return (iter != std::end(EffectPropsList)) ? al::to_address(iter) : nullptr;
+    switch(type)
+    {
+    case AL_EFFECT_NULL: return NullEffectProps;
+    case AL_EFFECT_EAXREVERB: return ReverbEffectProps;
+    case AL_EFFECT_REVERB: return StdReverbEffectProps;
+    case AL_EFFECT_AUTOWAH: return AutowahEffectProps;
+    case AL_EFFECT_CHORUS: return ChorusEffectProps;
+    case AL_EFFECT_COMPRESSOR: return CompressorEffectProps;
+    case AL_EFFECT_DISTORTION: return DistortionEffectProps;
+    case AL_EFFECT_ECHO: return EchoEffectProps;
+    case AL_EFFECT_EQUALIZER: return EqualizerEffectProps;
+    case AL_EFFECT_FLANGER: return FlangerEffectProps;
+    case AL_EFFECT_FREQUENCY_SHIFTER: return FshifterEffectProps;
+    case AL_EFFECT_RING_MODULATOR: return ModulatorEffectProps;
+    case AL_EFFECT_PITCH_SHIFTER: return PshifterEffectProps;
+    case AL_EFFECT_VOCAL_MORPHER: return VmorpherEffectProps;
+    case AL_EFFECT_DEDICATED_DIALOGUE: return DedicatedDialogEffectProps;
+    case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: return DedicatedLfeEffectProps;
+    case AL_EFFECT_CONVOLUTION_SOFT: return ConvolutionEffectProps;
+    }
+    return NullEffectProps;
 }
 
-void InitEffectParams(ALeffect *effect, ALenum type)
+void InitEffectParams(ALeffect *effect, ALenum type) noexcept
 {
-    const EffectPropsItem *item{getEffectPropsItemByType(type)};
-    if(item)
-    {
-        effect->Props = item->DefaultProps;
-        effect->vtab = &item->Vtable;
-    }
-    else
+    switch(type)
     {
-        effect->Props = EffectProps{};
-        effect->vtab = &NullEffectVtable;
+    case AL_EFFECT_NULL: effect->PropsVariant.emplace<NullEffectHandler>(); break;
+    case AL_EFFECT_EAXREVERB: effect->PropsVariant.emplace<ReverbEffectHandler>(); break;
+    case AL_EFFECT_REVERB: effect->PropsVariant.emplace<StdReverbEffectHandler>(); break;
+    case AL_EFFECT_AUTOWAH: effect->PropsVariant.emplace<AutowahEffectHandler>(); break;
+    case AL_EFFECT_CHORUS: effect->PropsVariant.emplace<ChorusEffectHandler>(); break;
+    case AL_EFFECT_COMPRESSOR: effect->PropsVariant.emplace<CompressorEffectHandler>(); break;
+    case AL_EFFECT_DISTORTION: effect->PropsVariant.emplace<DistortionEffectHandler>(); break;
+    case AL_EFFECT_ECHO: effect->PropsVariant.emplace<EchoEffectHandler>(); break;
+    case AL_EFFECT_EQUALIZER: effect->PropsVariant.emplace<EqualizerEffectHandler>(); break;
+    case AL_EFFECT_FLANGER: effect->PropsVariant.emplace<ChorusEffectHandler>(); break;
+    case AL_EFFECT_FREQUENCY_SHIFTER: effect->PropsVariant.emplace<FshifterEffectHandler>(); break;
+    case AL_EFFECT_RING_MODULATOR: effect->PropsVariant.emplace<ModulatorEffectHandler>(); break;
+    case AL_EFFECT_PITCH_SHIFTER: effect->PropsVariant.emplace<PshifterEffectHandler>(); break;
+    case AL_EFFECT_VOCAL_MORPHER: effect->PropsVariant.emplace<VmorpherEffectHandler>(); break;
+    case AL_EFFECT_DEDICATED_DIALOGUE:
+        effect->PropsVariant.emplace<DedicatedDialogEffectHandler>();
+        break;
+    case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT:
+        effect->PropsVariant.emplace<DedicatedLfeEffectHandler>();
+        break;
+    case AL_EFFECT_CONVOLUTION_SOFT:
+        effect->PropsVariant.emplace<ConvolutionEffectHandler>();
+        break;
     }
+    effect->Props = GetDefaultProps(type);
     effect->type = type;
 }
 
-bool EnsureEffects(ALCdevice *device, size_t needed)
-{
-    size_t count{std::accumulate(device->EffectList.cbegin(), device->EffectList.cend(), size_t{0},
+auto EnsureEffects(ALCdevice *device, size_t needed) noexcept -> bool
+try {
+    size_t count{std::accumulate(device->EffectList.cbegin(), device->EffectList.cend(), 0_uz,
         [](size_t cur, const EffectSubList &sublist) noexcept -> size_t
         { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
 
@@ -170,21 +150,19 @@ bool EnsureEffects(ALCdevice *device, size_t needed)
         if(device->EffectList.size() >= 1<<25) UNLIKELY
             return false;
 
-        device->EffectList.emplace_back();
-        auto sublist = device->EffectList.end() - 1;
-        sublist->FreeMask = ~0_u64;
-        sublist->Effects = static_cast<ALeffect*>(al_calloc(alignof(ALeffect), sizeof(ALeffect)*64));
-        if(!sublist->Effects) UNLIKELY
-        {
-            device->EffectList.pop_back();
-            return false;
-        }
-        count += 64;
+        EffectSubList sublist{};
+        sublist.FreeMask = ~0_u64;
+        sublist.Effects = SubListAllocator{}.allocate(1);
+        device->EffectList.emplace_back(std::move(sublist));
+        count += std::tuple_size_v<SubListAllocator::value_type>;
     }
     return true;
 }
+catch(...) {
+    return false;
+}
 
-ALeffect *AllocEffect(ALCdevice *device)
+ALeffect *AllocEffect(ALCdevice *device) noexcept
 {
     auto sublist = std::find_if(device->EffectList.begin(), device->EffectList.end(),
         [](const EffectSubList &entry) noexcept -> bool
@@ -193,7 +171,7 @@ ALeffect *AllocEffect(ALCdevice *device)
     auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
     ASSUME(slidx < 64);
 
-    ALeffect *effect{al::construct_at(sublist->Effects + slidx)};
+    ALeffect *effect{al::construct_at(al::to_address(sublist->Effects->begin() + slidx))};
     InitEffectParams(effect, AL_EFFECT_NULL);
 
     /* Add 1 to avoid effect ID 0. */
@@ -206,16 +184,18 @@ ALeffect *AllocEffect(ALCdevice *device)
 
 void FreeEffect(ALCdevice *device, ALeffect *effect)
 {
+    device->mEffectNames.erase(effect->id);
+
     const ALuint id{effect->id - 1};
     const size_t lidx{id >> 6};
     const ALuint slidx{id & 0x3f};
 
-    al::destroy_at(effect);
+    std::destroy_at(effect);
 
     device->EffectList[lidx].FreeMask |= 1_u64 << slidx;
 }
 
-inline ALeffect *LookupEffect(ALCdevice *device, ALuint id)
+inline auto LookupEffect(ALCdevice *device, ALuint id) noexcept -> ALeffect*
 {
     const size_t lidx{(id-1) >> 6};
     const ALuint slidx{(id-1) & 0x3f};
@@ -225,320 +205,294 @@ inline ALeffect *LookupEffect(ALCdevice *device, ALuint id)
     EffectSubList &sublist = device->EffectList[lidx];
     if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
         return nullptr;
-    return sublist.Effects + slidx;
+    return al::to_address(sublist.Effects->begin() + slidx);
 }
 
 } // namespace
 
-AL_API void AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    if(n < 0) UNLIKELY
-        context->setError(AL_INVALID_VALUE, "Generating %d effects", n);
+AL_API DECL_FUNC2(void, alGenEffects, ALsizei,n, ALuint*,effects)
+FORCE_ALIGN void AL_APIENTRY alGenEffectsDirect(ALCcontext *context, ALsizei n, ALuint *effects) noexcept
+try {
+    if(n < 0)
+        throw al::context_error{AL_INVALID_VALUE, "Generating %d effects", n};
     if(n <= 0) UNLIKELY return;
 
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->EffectLock};
-    if(!EnsureEffects(device, static_cast<ALuint>(n)))
-    {
-        context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d effect%s", n, (n==1)?"":"s");
-        return;
-    }
+    std::lock_guard<std::mutex> effectlock{device->EffectLock};
 
-    if(n == 1) LIKELY
-    {
-        /* Special handling for the easy and normal case. */
-        ALeffect *effect{AllocEffect(device)};
-        effects[0] = effect->id;
-    }
-    else
-    {
-        /* Store the allocated buffer IDs in a separate local list, to avoid
-         * modifying the user storage in case of failure.
-         */
-        al::vector<ALuint> ids;
-        ids.reserve(static_cast<ALuint>(n));
-        do {
-            ALeffect *effect{AllocEffect(device)};
-            ids.emplace_back(effect->id);
-        } while(--n);
-        std::copy(ids.cbegin(), ids.cend(), effects);
-    }
-}
-END_API_FUNC
+    const al::span eids{effects, static_cast<ALuint>(n)};
+    if(!EnsureEffects(device, eids.size()))
+        throw al::context_error{AL_OUT_OF_MEMORY, "Failed to allocate %d effect%s", n,
+            (n == 1) ? "" : "s"};
 
-AL_API void AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+    std::generate(eids.begin(), eids.end(), [device]{ return AllocEffect(device)->id; });
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
+}
 
-    if(n < 0) UNLIKELY
-        context->setError(AL_INVALID_VALUE, "Deleting %d effects", n);
+AL_API DECL_FUNC2(void, alDeleteEffects, ALsizei,n, const ALuint*,effects)
+FORCE_ALIGN void AL_APIENTRY alDeleteEffectsDirect(ALCcontext *context, ALsizei n,
+    const ALuint *effects) noexcept
+try {
+    if(n < 0)
+        throw al::context_error{AL_INVALID_VALUE, "Deleting %d effects", n};
     if(n <= 0) UNLIKELY return;
 
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->EffectLock};
+    std::lock_guard<std::mutex> effectlock{device->EffectLock};
 
     /* First try to find any effects that are invalid. */
     auto validate_effect = [device](const ALuint eid) -> bool
     { return !eid || LookupEffect(device, eid) != nullptr; };
 
-    const ALuint *effects_end = effects + n;
-    auto inveffect = std::find_if_not(effects, effects_end, validate_effect);
-    if(inveffect != effects_end) UNLIKELY
-    {
-        context->setError(AL_INVALID_NAME, "Invalid effect ID %u", *inveffect);
-        return;
-    }
+    const al::span eids{effects, static_cast<ALuint>(n)};
+    auto inveffect = std::find_if_not(eids.begin(), eids.end(), validate_effect);
+    if(inveffect != eids.end())
+        throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", *inveffect};
 
     /* All good. Delete non-0 effect IDs. */
     auto delete_effect = [device](ALuint eid) -> void
     {
-        ALeffect *effect{eid ? LookupEffect(device, eid) : nullptr};
-        if(effect) FreeEffect(device, effect);
+        if(ALeffect *effect{eid ? LookupEffect(device, eid) : nullptr})
+            FreeEffect(device, effect);
     };
-    std::for_each(effects, effects_end, delete_effect);
+    std::for_each(eids.begin(), eids.end(), delete_effect);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
 
-AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect)
-START_API_FUNC
+AL_API DECL_FUNC1(ALboolean, alIsEffect, ALuint,effect)
+FORCE_ALIGN ALboolean AL_APIENTRY alIsEffectDirect(ALCcontext *context, ALuint effect) noexcept
 {
-    ContextRef context{GetContextRef()};
-    if(context) LIKELY
-    {
-        ALCdevice *device{context->mALDevice.get()};
-        std::lock_guard<std::mutex> _{device->EffectLock};
-        if(!effect || LookupEffect(device, effect))
-            return AL_TRUE;
-    }
+    ALCdevice *device{context->mALDevice.get()};
+    std::lock_guard<std::mutex> effectlock{device->EffectLock};
+    if(!effect || LookupEffect(device, effect))
+        return AL_TRUE;
     return AL_FALSE;
 }
-END_API_FUNC
-
-AL_API void AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint value)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
 
+AL_API DECL_FUNC3(void, alEffecti, ALuint,effect, ALenum,param, ALint,value)
+FORCE_ALIGN void AL_APIENTRY alEffectiDirect(ALCcontext *context, ALuint effect, ALenum param,
+    ALint value) noexcept
+try {
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->EffectLock};
+    std::lock_guard<std::mutex> effectlock{device->EffectLock};
 
     ALeffect *aleffect{LookupEffect(device, effect)};
-    if(!aleffect) UNLIKELY
-        context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
-    else if(param == AL_EFFECT_TYPE)
+    if(!aleffect)
+        throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
+
+    switch(param)
     {
-        bool isOk{value == AL_EFFECT_NULL};
-        if(!isOk)
+    case AL_EFFECT_TYPE:
+        if(value != AL_EFFECT_NULL)
         {
-            for(const EffectList &effectitem : gEffectList)
-            {
-                if(value == effectitem.val && !DisabledEffects[effectitem.type])
-                {
-                    isOk = true;
-                    break;
-                }
-            }
+            auto check_effect = [value](const EffectList &item) -> bool
+            { return value == item.val && !DisabledEffects.test(item.type); };
+            if(!std::any_of(gEffectList.cbegin(), gEffectList.cend(), check_effect))
+                throw al::context_error{AL_INVALID_VALUE, "Effect type 0x%04x not supported",
+                    value};
         }
 
-        if(isOk)
-            InitEffectParams(aleffect, value);
-        else
-            context->setError(AL_INVALID_VALUE, "Effect type 0x%04x not supported", value);
+        InitEffectParams(aleffect, value);
+        return;
     }
-    else try
+
+    /* Call the appropriate handler */
+    std::visit([aleffect,param,value](auto &arg)
     {
-        /* Call the appropriate handler */
-        ALeffect_setParami(aleffect, param, value);
-    }
-    catch(effect_exception &e) {
-        context->setError(e.errorCode(), "%s", e.what());
-    }
+        using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
+        using PropType = typename Type::prop_type;
+        return arg.SetParami(std::get<PropType>(aleffect->Props), param, value);
+    }, aleffect->PropsVariant);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *values)
-START_API_FUNC
-{
+AL_API DECL_FUNC3(void, alEffectiv, ALuint,effect, ALenum,param, const ALint*,values)
+FORCE_ALIGN void AL_APIENTRY alEffectivDirect(ALCcontext *context, ALuint effect, ALenum param,
+    const ALint *values) noexcept
+try {
     switch(param)
     {
     case AL_EFFECT_TYPE:
-        alEffecti(effect, param, values[0]);
+        alEffectiDirect(context, effect, param, *values);
         return;
     }
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->EffectLock};
+    std::lock_guard<std::mutex> effectlock{device->EffectLock};
 
     ALeffect *aleffect{LookupEffect(device, effect)};
-    if(!aleffect) UNLIKELY
-        context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
-    else try
+    if(!aleffect)
+        throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
+
+    /* Call the appropriate handler */
+    std::visit([aleffect,param,values](auto &arg)
     {
-        /* Call the appropriate handler */
-        ALeffect_setParamiv(aleffect, param, values);
-    }
-    catch(effect_exception &e) {
-        context->setError(e.errorCode(), "%s", e.what());
-    }
+        using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
+        using PropType = typename Type::prop_type;
+        return arg.SetParamiv(std::get<PropType>(aleffect->Props), param, values);
+    }, aleffect->PropsVariant);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
-
-AL_API void AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat value)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
 
+AL_API DECL_FUNC3(void, alEffectf, ALuint,effect, ALenum,param, ALfloat,value)
+FORCE_ALIGN void AL_APIENTRY alEffectfDirect(ALCcontext *context, ALuint effect, ALenum param,
+    ALfloat value) noexcept
+try {
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->EffectLock};
+    std::lock_guard<std::mutex> effectlock{device->EffectLock};
 
     ALeffect *aleffect{LookupEffect(device, effect)};
     if(!aleffect) UNLIKELY
-        context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
-    else try
+        throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
+
+    /* Call the appropriate handler */
+    std::visit([aleffect,param,value](auto &arg)
     {
-        /* Call the appropriate handler */
-        ALeffect_setParamf(aleffect, param, value);
-    }
-    catch(effect_exception &e) {
-        context->setError(e.errorCode(), "%s", e.what());
-    }
+        using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
+        using PropType = typename Type::prop_type;
+        return arg.SetParamf(std::get<PropType>(aleffect->Props), param, value);
+    }, aleffect->PropsVariant);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
-
-AL_API void AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *values)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
 
+AL_API DECL_FUNC3(void, alEffectfv, ALuint,effect, ALenum,param, const ALfloat*,values)
+FORCE_ALIGN void AL_APIENTRY alEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param,
+    const ALfloat *values) noexcept
+try {
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->EffectLock};
+    std::lock_guard<std::mutex> effectlock{device->EffectLock};
 
     ALeffect *aleffect{LookupEffect(device, effect)};
-    if(!aleffect) UNLIKELY
-        context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
-    else try
+    if(!aleffect)
+        throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
+
+    /* Call the appropriate handler */
+    std::visit([aleffect,param,values](auto &arg)
     {
-        /* Call the appropriate handler */
-        ALeffect_setParamfv(aleffect, param, values);
-    }
-    catch(effect_exception &e) {
-        context->setError(e.errorCode(), "%s", e.what());
-    }
+        using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
+        using PropType = typename Type::prop_type;
+        return arg.SetParamfv(std::get<PropType>(aleffect->Props), param, values);
+    }, aleffect->PropsVariant);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
-
-AL_API void AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *value)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
 
+AL_API DECL_FUNC3(void, alGetEffecti, ALuint,effect, ALenum,param, ALint*,value)
+FORCE_ALIGN void AL_APIENTRY alGetEffectiDirect(ALCcontext *context, ALuint effect, ALenum param,
+    ALint *value) noexcept
+try {
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->EffectLock};
+    std::lock_guard<std::mutex> effectlock{device->EffectLock};
 
     const ALeffect *aleffect{LookupEffect(device, effect)};
-    if(!aleffect) UNLIKELY
-        context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
-    else if(param == AL_EFFECT_TYPE)
-        *value = aleffect->type;
-    else try
+    if(!aleffect)
+        throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
+
+    switch(param)
     {
-        /* Call the appropriate handler */
-        ALeffect_getParami(aleffect, param, value);
-    }
-    catch(effect_exception &e) {
-        context->setError(e.errorCode(), "%s", e.what());
+    case AL_EFFECT_TYPE:
+        *value = aleffect->type;
+        return;
     }
+
+    /* Call the appropriate handler */
+    std::visit([aleffect,param,value](auto &arg)
+    {
+        using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
+        using PropType = typename Type::prop_type;
+        return arg.GetParami(std::get<PropType>(aleffect->Props), param, value);
+    }, aleffect->PropsVariant);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *values)
-START_API_FUNC
-{
+AL_API DECL_FUNC3(void, alGetEffectiv, ALuint,effect, ALenum,param, ALint*,values)
+FORCE_ALIGN void AL_APIENTRY alGetEffectivDirect(ALCcontext *context, ALuint effect, ALenum param,
+    ALint *values) noexcept
+try {
     switch(param)
     {
     case AL_EFFECT_TYPE:
-        alGetEffecti(effect, param, values);
+        alGetEffectiDirect(context, effect, param, values);
         return;
     }
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->EffectLock};
+    std::lock_guard<std::mutex> effectlock{device->EffectLock};
 
     const ALeffect *aleffect{LookupEffect(device, effect)};
-    if(!aleffect) UNLIKELY
-        context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
-    else try
+    if(!aleffect)
+        throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
+
+    /* Call the appropriate handler */
+    std::visit([aleffect,param,values](auto &arg)
     {
-        /* Call the appropriate handler */
-        ALeffect_getParamiv(aleffect, param, values);
-    }
-    catch(effect_exception &e) {
-        context->setError(e.errorCode(), "%s", e.what());
-    }
+        using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
+        using PropType = typename Type::prop_type;
+        return arg.GetParamiv(std::get<PropType>(aleffect->Props), param, values);
+    }, aleffect->PropsVariant);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
-
-AL_API void AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *value)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
 
+AL_API DECL_FUNC3(void, alGetEffectf, ALuint,effect, ALenum,param, ALfloat*,value)
+FORCE_ALIGN void AL_APIENTRY alGetEffectfDirect(ALCcontext *context, ALuint effect, ALenum param,
+    ALfloat *value) noexcept
+try {
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->EffectLock};
+    std::lock_guard<std::mutex> effectlock{device->EffectLock};
 
     const ALeffect *aleffect{LookupEffect(device, effect)};
-    if(!aleffect) UNLIKELY
-        context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
-    else try
+    if(!aleffect)
+        throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
+
+    /* Call the appropriate handler */
+    std::visit([aleffect,param,value](auto &arg)
     {
-        /* Call the appropriate handler */
-        ALeffect_getParamf(aleffect, param, value);
-    }
-    catch(effect_exception &e) {
-        context->setError(e.errorCode(), "%s", e.what());
-    }
+        using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
+        using PropType = typename Type::prop_type;
+        return arg.GetParamf(std::get<PropType>(aleffect->Props), param, value);
+    }, aleffect->PropsVariant);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
-
-AL_API void AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *values)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
 
+AL_API DECL_FUNC3(void, alGetEffectfv, ALuint,effect, ALenum,param, ALfloat*,values)
+FORCE_ALIGN void AL_APIENTRY alGetEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param,
+    ALfloat *values) noexcept
+try {
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->EffectLock};
+    std::lock_guard<std::mutex> effectlock{device->EffectLock};
 
     const ALeffect *aleffect{LookupEffect(device, effect)};
-    if(!aleffect) UNLIKELY
-        context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
-    else try
+    if(!aleffect)
+        throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
+
+    /* Call the appropriate handler */
+    std::visit([aleffect,param,values](auto &arg)
     {
-        /* Call the appropriate handler */
-        ALeffect_getParamfv(aleffect, param, values);
-    }
-    catch(effect_exception &e) {
-        context->setError(e.errorCode(), "%s", e.what());
-    }
+        using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
+        using PropType = typename Type::prop_type;
+        return arg.GetParamfv(std::get<PropType>(aleffect->Props), param, values);
+    }, aleffect->PropsVariant);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
 
 
 void InitEffect(ALeffect *effect)
@@ -546,26 +500,43 @@ void InitEffect(ALeffect *effect)
     InitEffectParams(effect, AL_EFFECT_NULL);
 }
 
+void ALeffect::SetName(ALCcontext* context, ALuint id, std::string_view name)
+{
+    ALCdevice *device{context->mALDevice.get()};
+    std::lock_guard<std::mutex> effectlock{device->EffectLock};
+
+    auto effect = LookupEffect(device, id);
+    if(!effect)
+        throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", id};
+
+    device->mEffectNames.insert_or_assign(id, name);
+}
+
+
 EffectSubList::~EffectSubList()
 {
+    if(!Effects)
+        return;
+
     uint64_t usemask{~FreeMask};
     while(usemask)
     {
         const int idx{al::countr_zero(usemask)};
-        al::destroy_at(Effects+idx);
+        std::destroy_at(al::to_address(Effects->begin()+idx));
         usemask &= ~(1_u64 << idx);
     }
     FreeMask = ~usemask;
-    al_free(Effects);
+    SubListAllocator{}.deallocate(Effects, 1);
     Effects = nullptr;
 }
 
 
-#define DECL(x) { #x, EFX_REVERB_PRESET_##x }
-static const struct {
-    const char name[32];
+struct EffectPreset {
+    const char name[32]; /* NOLINT(*-avoid-c-arrays) */
     EFXEAXREVERBPROPERTIES props;
-} reverblist[] = {
+};
+#define DECL(x) EffectPreset{#x, EFX_REVERB_PRESET_##x}
+static constexpr std::array reverblist{
     DECL(GENERIC),
     DECL(PADDEDCELL),
     DECL(ROOM),
@@ -695,61 +666,62 @@ static const struct {
 };
 #undef DECL
 
-void LoadReverbPreset(const char *name, ALeffect *effect)
+void LoadReverbPreset(const std::string_view name, ALeffect *effect)
 {
-    if(al::strcasecmp(name, "NONE") == 0)
+    using namespace std::string_view_literals;
+
+    if(al::case_compare(name, "NONE"sv) == 0)
     {
         InitEffectParams(effect, AL_EFFECT_NULL);
         TRACE("Loading reverb '%s'\n", "NONE");
         return;
     }
 
-    if(!DisabledEffects[EAXREVERB_EFFECT])
+    if(!DisabledEffects.test(EAXREVERB_EFFECT))
         InitEffectParams(effect, AL_EFFECT_EAXREVERB);
-    else if(!DisabledEffects[REVERB_EFFECT])
+    else if(!DisabledEffects.test(REVERB_EFFECT))
         InitEffectParams(effect, AL_EFFECT_REVERB);
     else
         InitEffectParams(effect, AL_EFFECT_NULL);
     for(const auto &reverbitem : reverblist)
     {
-        const EFXEAXREVERBPROPERTIES *props;
-
-        if(al::strcasecmp(name, reverbitem.name) != 0)
+        if(al::case_compare(name, std::data(reverbitem.name)) != 0)
             continue;
 
-        TRACE("Loading reverb '%s'\n", reverbitem.name);
-        props = &reverbitem.props;
-        effect->Props.Reverb.Density   = props->flDensity;
-        effect->Props.Reverb.Diffusion = props->flDiffusion;
-        effect->Props.Reverb.Gain   = props->flGain;
-        effect->Props.Reverb.GainHF = props->flGainHF;
-        effect->Props.Reverb.GainLF = props->flGainLF;
-        effect->Props.Reverb.DecayTime    = props->flDecayTime;
-        effect->Props.Reverb.DecayHFRatio = props->flDecayHFRatio;
-        effect->Props.Reverb.DecayLFRatio = props->flDecayLFRatio;
-        effect->Props.Reverb.ReflectionsGain   = props->flReflectionsGain;
-        effect->Props.Reverb.ReflectionsDelay  = props->flReflectionsDelay;
-        effect->Props.Reverb.ReflectionsPan[0] = props->flReflectionsPan[0];
-        effect->Props.Reverb.ReflectionsPan[1] = props->flReflectionsPan[1];
-        effect->Props.Reverb.ReflectionsPan[2] = props->flReflectionsPan[2];
-        effect->Props.Reverb.LateReverbGain   = props->flLateReverbGain;
-        effect->Props.Reverb.LateReverbDelay  = props->flLateReverbDelay;
-        effect->Props.Reverb.LateReverbPan[0] = props->flLateReverbPan[0];
-        effect->Props.Reverb.LateReverbPan[1] = props->flLateReverbPan[1];
-        effect->Props.Reverb.LateReverbPan[2] = props->flLateReverbPan[2];
-        effect->Props.Reverb.EchoTime  = props->flEchoTime;
-        effect->Props.Reverb.EchoDepth = props->flEchoDepth;
-        effect->Props.Reverb.ModulationTime  = props->flModulationTime;
-        effect->Props.Reverb.ModulationDepth = props->flModulationDepth;
-        effect->Props.Reverb.AirAbsorptionGainHF = props->flAirAbsorptionGainHF;
-        effect->Props.Reverb.HFReference = props->flHFReference;
-        effect->Props.Reverb.LFReference = props->flLFReference;
-        effect->Props.Reverb.RoomRolloffFactor = props->flRoomRolloffFactor;
-        effect->Props.Reverb.DecayHFLimit = props->iDecayHFLimit ? AL_TRUE : AL_FALSE;
+        TRACE("Loading reverb '%s'\n", std::data(reverbitem.name));
+        const auto &props = reverbitem.props;
+        auto &dst = std::get<ReverbProps>(effect->Props);
+        dst.Density   = props.flDensity;
+        dst.Diffusion = props.flDiffusion;
+        dst.Gain   = props.flGain;
+        dst.GainHF = props.flGainHF;
+        dst.GainLF = props.flGainLF;
+        dst.DecayTime    = props.flDecayTime;
+        dst.DecayHFRatio = props.flDecayHFRatio;
+        dst.DecayLFRatio = props.flDecayLFRatio;
+        dst.ReflectionsGain   = props.flReflectionsGain;
+        dst.ReflectionsDelay  = props.flReflectionsDelay;
+        dst.ReflectionsPan[0] = props.flReflectionsPan[0];
+        dst.ReflectionsPan[1] = props.flReflectionsPan[1];
+        dst.ReflectionsPan[2] = props.flReflectionsPan[2];
+        dst.LateReverbGain   = props.flLateReverbGain;
+        dst.LateReverbDelay  = props.flLateReverbDelay;
+        dst.LateReverbPan[0] = props.flLateReverbPan[0];
+        dst.LateReverbPan[1] = props.flLateReverbPan[1];
+        dst.LateReverbPan[2] = props.flLateReverbPan[2];
+        dst.EchoTime  = props.flEchoTime;
+        dst.EchoDepth = props.flEchoDepth;
+        dst.ModulationTime  = props.flModulationTime;
+        dst.ModulationDepth = props.flModulationDepth;
+        dst.AirAbsorptionGainHF = props.flAirAbsorptionGainHF;
+        dst.HFReference = props.flHFReference;
+        dst.LFReference = props.flLFReference;
+        dst.RoomRolloffFactor = props.flRoomRolloffFactor;
+        dst.DecayHFLimit = props.iDecayHFLimit ? AL_TRUE : AL_FALSE;
         return;
     }
 
-    WARN("Reverb preset '%s' not found\n", name);
+    WARN("Reverb preset '%.*s' not found\n", al::sizei(name), name.data());
 }
 
 bool IsValidEffectType(ALenum type) noexcept
@@ -757,10 +729,7 @@ bool IsValidEffectType(ALenum type) noexcept
     if(type == AL_EFFECT_NULL)
         return true;
 
-    for(const auto &effect_item : gEffectList)
-    {
-        if(type == effect_item.val && !DisabledEffects[effect_item.type])
-            return true;
-    }
-    return false;
+    auto check_effect = [type](const EffectList &item) noexcept -> bool
+    { return type == item.val && !DisabledEffects.test(item.type); };
+    return std::any_of(gEffectList.cbegin(), gEffectList.cend(), check_effect);
 }

+ 40 - 12
Engine/lib/openal-soft/al/effect.h

@@ -1,11 +1,20 @@
 #ifndef AL_EFFECT_H
 #define AL_EFFECT_H
 
+#include <array>
+#include <bitset>
+#include <cstdint>
+#include <string_view>
+#include <utility>
+
 #include "AL/al.h"
+#include "AL/alc.h"
 #include "AL/efx.h"
 
-#include "al/effects/effects.h"
-#include "alc/effects/base.h"
+#include "almalloc.h"
+#include "alnumeric.h"
+#include "core/effects/base.h"
+#include "effects/effects.h"
 
 
 enum {
@@ -27,36 +36,55 @@ enum {
 
     MAX_EFFECTS
 };
-extern bool DisabledEffects[MAX_EFFECTS];
-
-extern float ReverbBoost;
+inline std::bitset<MAX_EFFECTS> DisabledEffects;
 
 struct EffectList {
-    const char name[16];
-    int type;
+    const char name[16]; /* NOLINT(*-avoid-c-arrays) */
+    ALuint type;
     ALenum val;
 };
-extern const EffectList gEffectList[16];
+extern const std::array<EffectList,16> gEffectList;
 
+using EffectHandlerVariant = std::variant<NullEffectHandler,ReverbEffectHandler,
+    StdReverbEffectHandler,AutowahEffectHandler,ChorusEffectHandler,CompressorEffectHandler,
+    DistortionEffectHandler,EchoEffectHandler,EqualizerEffectHandler,FlangerEffectHandler,
+    FshifterEffectHandler,ModulatorEffectHandler,PshifterEffectHandler,VmorpherEffectHandler,
+    DedicatedDialogEffectHandler,DedicatedLfeEffectHandler,ConvolutionEffectHandler>;
 
 struct ALeffect {
     // Effect type (AL_EFFECT_NULL, ...)
     ALenum type{AL_EFFECT_NULL};
 
+    EffectHandlerVariant PropsVariant;
     EffectProps Props{};
 
-    const EffectVtable *vtab{nullptr};
-
     /* Self ID */
     ALuint id{0u};
 
-    DISABLE_ALLOC()
+    static void SetName(ALCcontext *context, ALuint id, std::string_view name);
+
+    DISABLE_ALLOC
 };
 
 void InitEffect(ALeffect *effect);
 
-void LoadReverbPreset(const char *name, ALeffect *effect);
+void LoadReverbPreset(const std::string_view name, ALeffect *effect);
 
 bool IsValidEffectType(ALenum type) noexcept;
 
+struct EffectSubList {
+    uint64_t FreeMask{~0_u64};
+    gsl::owner<std::array<ALeffect,64>*> Effects{nullptr}; /* 64 */
+
+    EffectSubList() noexcept = default;
+    EffectSubList(const EffectSubList&) = delete;
+    EffectSubList(EffectSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Effects{rhs.Effects}
+    { rhs.FreeMask = ~0_u64; rhs.Effects = nullptr; }
+    ~EffectSubList();
+
+    EffectSubList& operator=(const EffectSubList&) = delete;
+    EffectSubList& operator=(EffectSubList&& rhs) noexcept
+    { std::swap(FreeMask, rhs.FreeMask); std::swap(Effects, rhs.Effects); return *this; }
+};
+
 #endif

+ 72 - 84
Engine/lib/openal-soft/al/effects/autowah.cpp

@@ -13,6 +13,7 @@
 
 #ifdef ALSOFT_EAX
 #include "alnumeric.h"
+#include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #endif // ALSOFT_EAX
@@ -20,100 +21,87 @@
 
 namespace {
 
-void Autowah_setParamf(EffectProps *props, ALenum param, float val)
+constexpr EffectProps genDefaultProps() noexcept
+{
+    AutowahProps props{};
+    props.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME;
+    props.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME;
+    props.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE;
+    props.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN;
+    return props;
+}
+
+} // namespace
+
+const EffectProps AutowahEffectProps{genDefaultProps()};
+
+void AutowahEffectHandler::SetParami(AutowahProps&, ALenum param, int)
+{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
+void AutowahEffectHandler::SetParamiv(AutowahProps&, ALenum param, const int*)
+{
+    throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
+        param};
+}
+
+void AutowahEffectHandler::SetParamf(AutowahProps &props, ALenum param, float val)
 {
     switch(param)
     {
     case AL_AUTOWAH_ATTACK_TIME:
         if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
             throw effect_exception{AL_INVALID_VALUE, "Autowah attack time out of range"};
-        props->Autowah.AttackTime = val;
+        props.AttackTime = val;
         break;
 
     case AL_AUTOWAH_RELEASE_TIME:
         if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME))
             throw effect_exception{AL_INVALID_VALUE, "Autowah release time out of range"};
-        props->Autowah.ReleaseTime = val;
+        props.ReleaseTime = val;
         break;
 
     case AL_AUTOWAH_RESONANCE:
         if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
             throw effect_exception{AL_INVALID_VALUE, "Autowah resonance out of range"};
-        props->Autowah.Resonance = val;
+        props.Resonance = val;
         break;
 
     case AL_AUTOWAH_PEAK_GAIN:
         if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN))
             throw effect_exception{AL_INVALID_VALUE, "Autowah peak gain out of range"};
-        props->Autowah.PeakGain = val;
+        props.PeakGain = val;
         break;
 
     default:
         throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param};
     }
 }
-void Autowah_setParamfv(EffectProps *props,  ALenum param, const float *vals)
-{ Autowah_setParamf(props, param, vals[0]); }
+void AutowahEffectHandler::SetParamfv(AutowahProps &props,  ALenum param, const float *vals)
+{ SetParamf(props, param, *vals); }
 
-void Autowah_setParami(EffectProps*, ALenum param, int)
+void AutowahEffectHandler::GetParami(const AutowahProps&, ALenum param, int*)
 { throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
-void Autowah_setParamiv(EffectProps*, ALenum param, const int*)
+void AutowahEffectHandler::GetParamiv(const AutowahProps&, ALenum param, int*)
 {
     throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
         param};
 }
 
-void Autowah_getParamf(const EffectProps *props, ALenum param, float *val)
+void AutowahEffectHandler::GetParamf(const AutowahProps &props, ALenum param, float *val)
 {
     switch(param)
     {
-    case AL_AUTOWAH_ATTACK_TIME:
-        *val = props->Autowah.AttackTime;
-        break;
-
-    case AL_AUTOWAH_RELEASE_TIME:
-        *val = props->Autowah.ReleaseTime;
-        break;
-
-    case AL_AUTOWAH_RESONANCE:
-        *val = props->Autowah.Resonance;
-        break;
-
-    case AL_AUTOWAH_PEAK_GAIN:
-        *val = props->Autowah.PeakGain;
-        break;
+    case AL_AUTOWAH_ATTACK_TIME: *val = props.AttackTime; break;
+    case AL_AUTOWAH_RELEASE_TIME: *val = props.ReleaseTime; break;
+    case AL_AUTOWAH_RESONANCE: *val = props.Resonance; break;
+    case AL_AUTOWAH_PEAK_GAIN: *val = props.PeakGain; break;
 
     default:
         throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param};
     }
 
 }
-void Autowah_getParamfv(const EffectProps *props, ALenum param, float *vals)
-{ Autowah_getParamf(props, param, vals); }
-
-void Autowah_getParami(const EffectProps*, ALenum param, int*)
-{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
-void Autowah_getParamiv(const EffectProps*, ALenum param, int*)
-{
-    throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
-        param};
-}
-
-EffectProps genDefaultProps() noexcept
-{
-    EffectProps props{};
-    props.Autowah.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME;
-    props.Autowah.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME;
-    props.Autowah.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE;
-    props.Autowah.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN;
-    return props;
-}
-
-} // namespace
-
-DEFINE_ALEFFECT_VTABLE(Autowah);
-
-const EffectProps AutowahEffectProps{genDefaultProps()};
+void AutowahEffectHandler::GetParamfv(const AutowahProps &props, ALenum param, float *vals)
+{ GetParamf(props, param, vals); }
 
 #ifdef ALSOFT_EAX
 namespace {
@@ -189,62 +177,62 @@ template<>
     throw Exception{message};
 }
 
-template<>
-bool AutowahCommitter::commit(const EaxEffectProps &props)
+bool EaxAutowahCommitter::commit(const EAXAUTOWAHPROPERTIES &props)
 {
-    if(props.mType == mEaxProps.mType
-        && mEaxProps.mAutowah.flAttackTime == props.mAutowah.flAttackTime
-        && mEaxProps.mAutowah.flReleaseTime == props.mAutowah.flReleaseTime
-        && mEaxProps.mAutowah.lResonance == props.mAutowah.lResonance
-        && mEaxProps.mAutowah.lPeakLevel == props.mAutowah.lPeakLevel)
+    if(auto *cur = std::get_if<EAXAUTOWAHPROPERTIES>(&mEaxProps); cur && *cur == props)
         return false;
 
     mEaxProps = props;
-
-    mAlProps.Autowah.AttackTime = props.mAutowah.flAttackTime;
-    mAlProps.Autowah.ReleaseTime = props.mAutowah.flReleaseTime;
-    mAlProps.Autowah.Resonance = level_mb_to_gain(static_cast<float>(props.mAutowah.lResonance));
-    mAlProps.Autowah.PeakGain = level_mb_to_gain(static_cast<float>(props.mAutowah.lPeakLevel));
+    mAlProps = [&]{
+        AutowahProps ret{};
+        ret.AttackTime = props.flAttackTime;
+        ret.ReleaseTime = props.flReleaseTime;
+        ret.Resonance = level_mb_to_gain(static_cast<float>(props.lResonance));
+        ret.PeakGain = level_mb_to_gain(static_cast<float>(props.lPeakLevel));
+        return ret;
+    }();
 
     return true;
 }
 
-template<>
-void AutowahCommitter::SetDefaults(EaxEffectProps &props)
+void EaxAutowahCommitter::SetDefaults(EaxEffectProps &props)
 {
-    props.mType = EaxEffectType::Autowah;
-    props.mAutowah.flAttackTime = EAXAUTOWAH_DEFAULTATTACKTIME;
-    props.mAutowah.flReleaseTime = EAXAUTOWAH_DEFAULTRELEASETIME;
-    props.mAutowah.lResonance = EAXAUTOWAH_DEFAULTRESONANCE;
-    props.mAutowah.lPeakLevel = EAXAUTOWAH_DEFAULTPEAKLEVEL;
+    static constexpr EAXAUTOWAHPROPERTIES defprops{[]
+    {
+        EAXAUTOWAHPROPERTIES ret{};
+        ret.flAttackTime = EAXAUTOWAH_DEFAULTATTACKTIME;
+        ret.flReleaseTime = EAXAUTOWAH_DEFAULTRELEASETIME;
+        ret.lResonance = EAXAUTOWAH_DEFAULTRESONANCE;
+        ret.lPeakLevel = EAXAUTOWAH_DEFAULTPEAKLEVEL;
+        return ret;
+    }()};
+    props = defprops;
 }
 
-template<>
-void AutowahCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+void EaxAutowahCommitter::Get(const EaxCall &call, const EAXAUTOWAHPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXAUTOWAH_NONE: break;
-    case EAXAUTOWAH_ALLPARAMETERS: call.set_value<Exception>(props.mAutowah); break;
-    case EAXAUTOWAH_ATTACKTIME: call.set_value<Exception>(props.mAutowah.flAttackTime); break;
-    case EAXAUTOWAH_RELEASETIME: call.set_value<Exception>(props.mAutowah.flReleaseTime); break;
-    case EAXAUTOWAH_RESONANCE: call.set_value<Exception>(props.mAutowah.lResonance); break;
-    case EAXAUTOWAH_PEAKLEVEL: call.set_value<Exception>(props.mAutowah.lPeakLevel); break;
+    case EAXAUTOWAH_ALLPARAMETERS: call.set_value<Exception>(props); break;
+    case EAXAUTOWAH_ATTACKTIME: call.set_value<Exception>(props.flAttackTime); break;
+    case EAXAUTOWAH_RELEASETIME: call.set_value<Exception>(props.flReleaseTime); break;
+    case EAXAUTOWAH_RESONANCE: call.set_value<Exception>(props.lResonance); break;
+    case EAXAUTOWAH_PEAKLEVEL: call.set_value<Exception>(props.lPeakLevel); break;
     default: fail_unknown_property_id();
     }
 }
 
-template<>
-void AutowahCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+void EaxAutowahCommitter::Set(const EaxCall &call, EAXAUTOWAHPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXAUTOWAH_NONE: break;
-    case EAXAUTOWAH_ALLPARAMETERS: defer<AllValidator>(call, props.mAutowah); break;
-    case EAXAUTOWAH_ATTACKTIME: defer<AttackTimeValidator>(call, props.mAutowah.flAttackTime); break;
-    case EAXAUTOWAH_RELEASETIME: defer<ReleaseTimeValidator>(call, props.mAutowah.flReleaseTime); break;
-    case EAXAUTOWAH_RESONANCE: defer<ResonanceValidator>(call, props.mAutowah.lResonance); break;
-    case EAXAUTOWAH_PEAKLEVEL: defer<PeakLevelValidator>(call, props.mAutowah.lPeakLevel); break;
+    case EAXAUTOWAH_ALLPARAMETERS: defer<AllValidator>(call, props); break;
+    case EAXAUTOWAH_ATTACKTIME: defer<AttackTimeValidator>(call, props.flAttackTime); break;
+    case EAXAUTOWAH_RELEASETIME: defer<ReleaseTimeValidator>(call, props.flReleaseTime); break;
+    case EAXAUTOWAH_RESONANCE: defer<ResonanceValidator>(call, props.lResonance); break;
+    case EAXAUTOWAH_PEAKLEVEL: defer<PeakLevelValidator>(call, props.lPeakLevel); break;
     default: fail_unknown_property_id();
     }
 }

+ 108 - 186
Engine/lib/openal-soft/al/effects/chorus.cpp

@@ -1,19 +1,17 @@
 
 #include "config.h"
 
+#include <optional>
 #include <stdexcept>
 
 #include "AL/al.h"
 #include "AL/efx.h"
 
-#include "alc/effects/base.h"
-#include "aloptional.h"
-#include "core/logging.h"
 #include "effects.h"
 
 #ifdef ALSOFT_EAX
 #include <cassert>
-#include "alnumeric.h"
+#include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #endif // ALSOFT_EAX
@@ -27,16 +25,16 @@ static_assert(FlangerMaxDelay >= AL_FLANGER_MAX_DELAY, "Flanger max delay too sm
 static_assert(AL_CHORUS_WAVEFORM_SINUSOID == AL_FLANGER_WAVEFORM_SINUSOID, "Chorus/Flanger waveform value mismatch");
 static_assert(AL_CHORUS_WAVEFORM_TRIANGLE == AL_FLANGER_WAVEFORM_TRIANGLE, "Chorus/Flanger waveform value mismatch");
 
-inline al::optional<ChorusWaveform> WaveformFromEnum(ALenum type)
+constexpr std::optional<ChorusWaveform> WaveformFromEnum(ALenum type) noexcept
 {
     switch(type)
     {
     case AL_CHORUS_WAVEFORM_SINUSOID: return ChorusWaveform::Sinusoid;
     case AL_CHORUS_WAVEFORM_TRIANGLE: return ChorusWaveform::Triangle;
     }
-    return al::nullopt;
+    return std::nullopt;
 }
-inline ALenum EnumFromWaveform(ChorusWaveform type)
+constexpr ALenum EnumFromWaveform(ChorusWaveform type)
 {
     switch(type)
     {
@@ -46,13 +44,41 @@ inline ALenum EnumFromWaveform(ChorusWaveform type)
     throw std::runtime_error{"Invalid chorus waveform: "+std::to_string(static_cast<int>(type))};
 }
 
-void Chorus_setParami(EffectProps *props, ALenum param, int val)
+constexpr EffectProps genDefaultChorusProps() noexcept
+{
+    ChorusProps props{};
+    props.Waveform = WaveformFromEnum(AL_CHORUS_DEFAULT_WAVEFORM).value();
+    props.Phase = AL_CHORUS_DEFAULT_PHASE;
+    props.Rate = AL_CHORUS_DEFAULT_RATE;
+    props.Depth = AL_CHORUS_DEFAULT_DEPTH;
+    props.Feedback = AL_CHORUS_DEFAULT_FEEDBACK;
+    props.Delay = AL_CHORUS_DEFAULT_DELAY;
+    return props;
+}
+
+constexpr EffectProps genDefaultFlangerProps() noexcept
+{
+    ChorusProps props{};
+    props.Waveform = WaveformFromEnum(AL_FLANGER_DEFAULT_WAVEFORM).value();
+    props.Phase = AL_FLANGER_DEFAULT_PHASE;
+    props.Rate = AL_FLANGER_DEFAULT_RATE;
+    props.Depth = AL_FLANGER_DEFAULT_DEPTH;
+    props.Feedback = AL_FLANGER_DEFAULT_FEEDBACK;
+    props.Delay = AL_FLANGER_DEFAULT_DELAY;
+    return props;
+}
+
+} // namespace
+
+const EffectProps ChorusEffectProps{genDefaultChorusProps()};
+
+void ChorusEffectHandler::SetParami(ChorusProps &props, ALenum param, int val)
 {
     switch(param)
     {
     case AL_CHORUS_WAVEFORM:
         if(auto formopt = WaveformFromEnum(val))
-            props->Chorus.Waveform = *formopt;
+            props.Waveform = *formopt;
         else
             throw effect_exception{AL_INVALID_VALUE, "Invalid chorus waveform: 0x%04x", val};
         break;
@@ -60,115 +86,89 @@ void Chorus_setParami(EffectProps *props, ALenum param, int val)
     case AL_CHORUS_PHASE:
         if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE))
             throw effect_exception{AL_INVALID_VALUE, "Chorus phase out of range: %d", val};
-        props->Chorus.Phase = val;
+        props.Phase = val;
         break;
 
     default:
         throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param};
     }
 }
-void Chorus_setParamiv(EffectProps *props, ALenum param, const int *vals)
-{ Chorus_setParami(props, param, vals[0]); }
-void Chorus_setParamf(EffectProps *props, ALenum param, float val)
+void ChorusEffectHandler::SetParamiv(ChorusProps &props, ALenum param, const int *vals)
+{ SetParami(props, param, *vals); }
+void ChorusEffectHandler::SetParamf(ChorusProps &props, ALenum param, float val)
 {
     switch(param)
     {
     case AL_CHORUS_RATE:
         if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE))
             throw effect_exception{AL_INVALID_VALUE, "Chorus rate out of range: %f", val};
-        props->Chorus.Rate = val;
+        props.Rate = val;
         break;
 
     case AL_CHORUS_DEPTH:
         if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH))
             throw effect_exception{AL_INVALID_VALUE, "Chorus depth out of range: %f", val};
-        props->Chorus.Depth = val;
+        props.Depth = val;
         break;
 
     case AL_CHORUS_FEEDBACK:
         if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK))
             throw effect_exception{AL_INVALID_VALUE, "Chorus feedback out of range: %f", val};
-        props->Chorus.Feedback = val;
+        props.Feedback = val;
         break;
 
     case AL_CHORUS_DELAY:
         if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY))
             throw effect_exception{AL_INVALID_VALUE, "Chorus delay out of range: %f", val};
-        props->Chorus.Delay = val;
+        props.Delay = val;
         break;
 
     default:
         throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param};
     }
 }
-void Chorus_setParamfv(EffectProps *props, ALenum param, const float *vals)
-{ Chorus_setParamf(props, param, vals[0]); }
+void ChorusEffectHandler::SetParamfv(ChorusProps &props, ALenum param, const float *vals)
+{ SetParamf(props, param, *vals); }
 
-void Chorus_getParami(const EffectProps *props, ALenum param, int *val)
+void ChorusEffectHandler::GetParami(const ChorusProps &props, ALenum param, int *val)
 {
     switch(param)
     {
-    case AL_CHORUS_WAVEFORM:
-        *val = EnumFromWaveform(props->Chorus.Waveform);
-        break;
-
-    case AL_CHORUS_PHASE:
-        *val = props->Chorus.Phase;
-        break;
+    case AL_CHORUS_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break;
+    case AL_CHORUS_PHASE: *val = props.Phase; break;
 
     default:
         throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param};
     }
 }
-void Chorus_getParamiv(const EffectProps *props, ALenum param, int *vals)
-{ Chorus_getParami(props, param, vals); }
-void Chorus_getParamf(const EffectProps *props, ALenum param, float *val)
+void ChorusEffectHandler::GetParamiv(const ChorusProps &props, ALenum param, int *vals)
+{ GetParami(props, param, vals); }
+void ChorusEffectHandler::GetParamf(const ChorusProps &props, ALenum param, float *val)
 {
     switch(param)
     {
-    case AL_CHORUS_RATE:
-        *val = props->Chorus.Rate;
-        break;
-
-    case AL_CHORUS_DEPTH:
-        *val = props->Chorus.Depth;
-        break;
-
-    case AL_CHORUS_FEEDBACK:
-        *val = props->Chorus.Feedback;
-        break;
-
-    case AL_CHORUS_DELAY:
-        *val = props->Chorus.Delay;
-        break;
+    case AL_CHORUS_RATE: *val = props.Rate; break;
+    case AL_CHORUS_DEPTH: *val = props.Depth; break;
+    case AL_CHORUS_FEEDBACK: *val = props.Feedback; break;
+    case AL_CHORUS_DELAY: *val = props.Delay; break;
 
     default:
         throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param};
     }
 }
-void Chorus_getParamfv(const EffectProps *props, ALenum param, float *vals)
-{ Chorus_getParamf(props, param, vals); }
+void ChorusEffectHandler::GetParamfv(const ChorusProps &props, ALenum param, float *vals)
+{ GetParamf(props, param, vals); }
 
-EffectProps genDefaultChorusProps() noexcept
-{
-    EffectProps props{};
-    props.Chorus.Waveform = *WaveformFromEnum(AL_CHORUS_DEFAULT_WAVEFORM);
-    props.Chorus.Phase = AL_CHORUS_DEFAULT_PHASE;
-    props.Chorus.Rate = AL_CHORUS_DEFAULT_RATE;
-    props.Chorus.Depth = AL_CHORUS_DEFAULT_DEPTH;
-    props.Chorus.Feedback = AL_CHORUS_DEFAULT_FEEDBACK;
-    props.Chorus.Delay = AL_CHORUS_DEFAULT_DELAY;
-    return props;
-}
 
+const EffectProps FlangerEffectProps{genDefaultFlangerProps()};
 
-void Flanger_setParami(EffectProps *props, ALenum param, int val)
+void FlangerEffectHandler::SetParami(ChorusProps &props, ALenum param, int val)
 {
     switch(param)
     {
     case AL_FLANGER_WAVEFORM:
         if(auto formopt = WaveformFromEnum(val))
-            props->Chorus.Waveform = *formopt;
+            props.Waveform = *formopt;
         else
             throw effect_exception{AL_INVALID_VALUE, "Invalid flanger waveform: 0x%04x", val};
         break;
@@ -176,127 +176,87 @@ void Flanger_setParami(EffectProps *props, ALenum param, int val)
     case AL_FLANGER_PHASE:
         if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE))
             throw effect_exception{AL_INVALID_VALUE, "Flanger phase out of range: %d", val};
-        props->Chorus.Phase = val;
+        props.Phase = val;
         break;
 
     default:
         throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param};
     }
 }
-void Flanger_setParamiv(EffectProps *props, ALenum param, const int *vals)
-{ Flanger_setParami(props, param, vals[0]); }
-void Flanger_setParamf(EffectProps *props, ALenum param, float val)
+void FlangerEffectHandler::SetParamiv(ChorusProps &props, ALenum param, const int *vals)
+{ SetParami(props, param, *vals); }
+void FlangerEffectHandler::SetParamf(ChorusProps &props, ALenum param, float val)
 {
     switch(param)
     {
     case AL_FLANGER_RATE:
         if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE))
             throw effect_exception{AL_INVALID_VALUE, "Flanger rate out of range: %f", val};
-        props->Chorus.Rate = val;
+        props.Rate = val;
         break;
 
     case AL_FLANGER_DEPTH:
         if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH))
             throw effect_exception{AL_INVALID_VALUE, "Flanger depth out of range: %f", val};
-        props->Chorus.Depth = val;
+        props.Depth = val;
         break;
 
     case AL_FLANGER_FEEDBACK:
         if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK))
             throw effect_exception{AL_INVALID_VALUE, "Flanger feedback out of range: %f", val};
-        props->Chorus.Feedback = val;
+        props.Feedback = val;
         break;
 
     case AL_FLANGER_DELAY:
         if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY))
             throw effect_exception{AL_INVALID_VALUE, "Flanger delay out of range: %f", val};
-        props->Chorus.Delay = val;
+        props.Delay = val;
         break;
 
     default:
         throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param};
     }
 }
-void Flanger_setParamfv(EffectProps *props, ALenum param, const float *vals)
-{ Flanger_setParamf(props, param, vals[0]); }
+void FlangerEffectHandler::SetParamfv(ChorusProps &props, ALenum param, const float *vals)
+{ SetParamf(props, param, *vals); }
 
-void Flanger_getParami(const EffectProps *props, ALenum param, int *val)
+void FlangerEffectHandler::GetParami(const ChorusProps &props, ALenum param, int *val)
 {
     switch(param)
     {
-    case AL_FLANGER_WAVEFORM:
-        *val = EnumFromWaveform(props->Chorus.Waveform);
-        break;
-
-    case AL_FLANGER_PHASE:
-        *val = props->Chorus.Phase;
-        break;
+    case AL_FLANGER_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break;
+    case AL_FLANGER_PHASE: *val = props.Phase; break;
 
     default:
         throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param};
     }
 }
-void Flanger_getParamiv(const EffectProps *props, ALenum param, int *vals)
-{ Flanger_getParami(props, param, vals); }
-void Flanger_getParamf(const EffectProps *props, ALenum param, float *val)
+void FlangerEffectHandler::GetParamiv(const ChorusProps &props, ALenum param, int *vals)
+{ GetParami(props, param, vals); }
+void FlangerEffectHandler::GetParamf(const ChorusProps &props, ALenum param, float *val)
 {
     switch(param)
     {
-    case AL_FLANGER_RATE:
-        *val = props->Chorus.Rate;
-        break;
-
-    case AL_FLANGER_DEPTH:
-        *val = props->Chorus.Depth;
-        break;
-
-    case AL_FLANGER_FEEDBACK:
-        *val = props->Chorus.Feedback;
-        break;
-
-    case AL_FLANGER_DELAY:
-        *val = props->Chorus.Delay;
-        break;
+    case AL_FLANGER_RATE: *val = props.Rate; break;
+    case AL_FLANGER_DEPTH: *val = props.Depth; break;
+    case AL_FLANGER_FEEDBACK: *val = props.Feedback; break;
+    case AL_FLANGER_DELAY: *val = props.Delay; break;
 
     default:
         throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param};
     }
 }
-void Flanger_getParamfv(const EffectProps *props, ALenum param, float *vals)
-{ Flanger_getParamf(props, param, vals); }
-
-EffectProps genDefaultFlangerProps() noexcept
-{
-    EffectProps props{};
-    props.Chorus.Waveform = *WaveformFromEnum(AL_FLANGER_DEFAULT_WAVEFORM);
-    props.Chorus.Phase = AL_FLANGER_DEFAULT_PHASE;
-    props.Chorus.Rate = AL_FLANGER_DEFAULT_RATE;
-    props.Chorus.Depth = AL_FLANGER_DEFAULT_DEPTH;
-    props.Chorus.Feedback = AL_FLANGER_DEFAULT_FEEDBACK;
-    props.Chorus.Delay = AL_FLANGER_DEFAULT_DELAY;
-    return props;
-}
-
-} // namespace
-
-DEFINE_ALEFFECT_VTABLE(Chorus);
-
-const EffectProps ChorusEffectProps{genDefaultChorusProps()};
-
-DEFINE_ALEFFECT_VTABLE(Flanger);
-
-const EffectProps FlangerEffectProps{genDefaultFlangerProps()};
+void FlangerEffectHandler::GetParamfv(const ChorusProps &props, ALenum param, float *vals)
+{ GetParamf(props, param, vals); }
 
 
 #ifdef ALSOFT_EAX
 namespace {
 
 struct EaxChorusTraits {
-    using Props = EAXCHORUSPROPERTIES;
+    using EaxProps = EAXCHORUSPROPERTIES;
     using Committer = EaxChorusCommitter;
-    static constexpr auto Field = &EaxEffectProps::mChorus;
 
-    static constexpr auto eax_effect_type() { return EaxEffectType::Chorus; }
     static constexpr auto efx_effect() { return AL_EFFECT_CHORUS; }
 
     static constexpr auto eax_none_param_id() { return EAXCHORUS_NONE; }
@@ -359,11 +319,9 @@ struct EaxChorusTraits {
 }; // EaxChorusTraits
 
 struct EaxFlangerTraits {
-    using Props = EAXFLANGERPROPERTIES;
+    using EaxProps = EAXFLANGERPROPERTIES;
     using Committer = EaxFlangerCommitter;
-    static constexpr auto Field = &EaxEffectProps::mFlanger;
 
-    static constexpr auto eax_effect_type() { return EaxEffectType::Flanger; }
     static constexpr auto efx_effect() { return AL_EFFECT_FLANGER; }
 
     static constexpr auto eax_none_param_id() { return EAXFLANGER_NONE; }
@@ -428,11 +386,10 @@ struct EaxFlangerTraits {
 template<typename TTraits>
 struct ChorusFlangerEffect {
     using Traits = TTraits;
+    using EaxProps = typename Traits::EaxProps;
     using Committer = typename Traits::Committer;
     using Exception = typename Committer::Exception;
 
-    static constexpr auto Field = Traits::Field;
-
     struct WaveformValidator {
         void operator()(unsigned long ulWaveform) const
         {
@@ -500,7 +457,7 @@ struct ChorusFlangerEffect {
     }; // DelayValidator
 
     struct AllValidator {
-        void operator()(const typename Traits::Props& all) const
+        void operator()(const EaxProps& all) const
         {
             WaveformValidator{}(all.ulWaveform);
             PhaseValidator{}(all.lPhase);
@@ -514,8 +471,7 @@ struct ChorusFlangerEffect {
 public:
     static void SetDefaults(EaxEffectProps &props)
     {
-        auto&& all = props.*Field;
-        props.mType = Traits::eax_effect_type();
+        auto&& all = props.emplace<EaxProps>();
         all.ulWaveform = Traits::eax_default_waveform();
         all.lPhase = Traits::eax_default_phase();
         all.flRate = Traits::eax_default_rate();
@@ -525,109 +481,83 @@ public:
     }
 
 
-    static void Get(const EaxCall &call, const EaxEffectProps &props)
+    static void Get(const EaxCall &call, const EaxProps &all)
     {
-        auto&& all = props.*Field;
         switch(call.get_property_id())
         {
         case Traits::eax_none_param_id():
             break;
-
         case Traits::eax_allparameters_param_id():
             call.template set_value<Exception>(all);
             break;
-
         case Traits::eax_waveform_param_id():
             call.template set_value<Exception>(all.ulWaveform);
             break;
-
         case Traits::eax_phase_param_id():
             call.template set_value<Exception>(all.lPhase);
             break;
-
         case Traits::eax_rate_param_id():
             call.template set_value<Exception>(all.flRate);
             break;
-
         case Traits::eax_depth_param_id():
             call.template set_value<Exception>(all.flDepth);
             break;
-
         case Traits::eax_feedback_param_id():
             call.template set_value<Exception>(all.flFeedback);
             break;
-
         case Traits::eax_delay_param_id():
             call.template set_value<Exception>(all.flDelay);
             break;
-
         default:
             Committer::fail_unknown_property_id();
         }
     }
 
-    static void Set(const EaxCall &call, EaxEffectProps &props)
+    static void Set(const EaxCall &call, EaxProps &all)
     {
-        auto&& all = props.*Field;
         switch(call.get_property_id())
         {
         case Traits::eax_none_param_id():
             break;
-
         case Traits::eax_allparameters_param_id():
             Committer::template defer<AllValidator>(call, all);
             break;
-
         case Traits::eax_waveform_param_id():
             Committer::template defer<WaveformValidator>(call, all.ulWaveform);
             break;
-
         case Traits::eax_phase_param_id():
             Committer::template defer<PhaseValidator>(call, all.lPhase);
             break;
-
         case Traits::eax_rate_param_id():
             Committer::template defer<RateValidator>(call, all.flRate);
             break;
-
         case Traits::eax_depth_param_id():
             Committer::template defer<DepthValidator>(call, all.flDepth);
             break;
-
         case Traits::eax_feedback_param_id():
             Committer::template defer<FeedbackValidator>(call, all.flFeedback);
             break;
-
         case Traits::eax_delay_param_id():
             Committer::template defer<DelayValidator>(call, all.flDelay);
             break;
-
         default:
             Committer::fail_unknown_property_id();
         }
     }
 
-    static bool Commit(const EaxEffectProps &props, EaxEffectProps &props_, EffectProps &al_props_)
+    static bool Commit(const EaxProps &props, EaxEffectProps &props_, ChorusProps &al_props_)
     {
-        if(props.mType == props_.mType)
-        {
-            auto&& src = props_.*Field;
-            auto&& dst = props.*Field;
-            if(dst.ulWaveform == src.ulWaveform && dst.lPhase == src.lPhase
-                && dst.flRate == src.flRate && dst.flDepth == src.flDepth
-                && dst.flFeedback == src.flFeedback && dst.flDelay == src.flDelay)
-                return false;
-        }
+        if(auto *cur = std::get_if<EaxProps>(&props_); cur && *cur == props)
+            return false;
 
         props_ = props;
-        auto&& dst = props.*Field;
 
-        al_props_.Chorus.Waveform = Traits::eax_waveform(dst.ulWaveform);
-        al_props_.Chorus.Phase = static_cast<int>(dst.lPhase);
-        al_props_.Chorus.Rate = dst.flRate;
-        al_props_.Chorus.Depth = dst.flDepth;
-        al_props_.Chorus.Feedback = dst.flFeedback;
-        al_props_.Chorus.Delay = dst.flDelay;
+        al_props_.Waveform = Traits::eax_waveform(props.ulWaveform);
+        al_props_.Phase = static_cast<int>(props.lPhase);
+        al_props_.Rate = props.flRate;
+        al_props_.Depth = props.flDepth;
+        al_props_.Feedback = props.flFeedback;
+        al_props_.Delay = props.flDelay;
 
         return true;
     }
@@ -652,29 +582,25 @@ template<>
     throw Exception{message};
 }
 
-template<>
-bool ChorusCommitter::commit(const EaxEffectProps &props)
+bool EaxChorusCommitter::commit(const EAXCHORUSPROPERTIES &props)
 {
     using Committer = ChorusFlangerEffect<EaxChorusTraits>;
-    return Committer::Commit(props, mEaxProps, mAlProps);
+    return Committer::Commit(props, mEaxProps, mAlProps.emplace<ChorusProps>());
 }
 
-template<>
-void ChorusCommitter::SetDefaults(EaxEffectProps &props)
+void EaxChorusCommitter::SetDefaults(EaxEffectProps &props)
 {
     using Committer = ChorusFlangerEffect<EaxChorusTraits>;
     Committer::SetDefaults(props);
 }
 
-template<>
-void ChorusCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+void EaxChorusCommitter::Get(const EaxCall &call, const EAXCHORUSPROPERTIES &props)
 {
     using Committer = ChorusFlangerEffect<EaxChorusTraits>;
     Committer::Get(call, props);
 }
 
-template<>
-void ChorusCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+void EaxChorusCommitter::Set(const EaxCall &call, EAXCHORUSPROPERTIES &props)
 {
     using Committer = ChorusFlangerEffect<EaxChorusTraits>;
     Committer::Set(call, props);
@@ -693,29 +619,25 @@ template<>
     throw Exception{message};
 }
 
-template<>
-bool FlangerCommitter::commit(const EaxEffectProps &props)
+bool EaxFlangerCommitter::commit(const EAXFLANGERPROPERTIES &props)
 {
     using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
-    return Committer::Commit(props, mEaxProps, mAlProps);
+    return Committer::Commit(props, mEaxProps, mAlProps.emplace<ChorusProps>());
 }
 
-template<>
-void FlangerCommitter::SetDefaults(EaxEffectProps &props)
+void EaxFlangerCommitter::SetDefaults(EaxEffectProps &props)
 {
     using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
     Committer::SetDefaults(props);
 }
 
-template<>
-void FlangerCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+void EaxFlangerCommitter::Get(const EaxCall &call, const EAXFLANGERPROPERTIES &props)
 {
     using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
     Committer::Get(call, props);
 }
 
-template<>
-void FlangerCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+void EaxFlangerCommitter::Set(const EaxCall &call, EAXFLANGERPROPERTIES &props)
 {
     using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
     Committer::Set(call, props);

+ 35 - 44
Engine/lib/openal-soft/al/effects/compressor.cpp

@@ -9,6 +9,7 @@
 
 #ifdef ALSOFT_EAX
 #include "alnumeric.h"
+#include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #endif // ALSOFT_EAX
@@ -16,14 +17,25 @@
 
 namespace {
 
-void Compressor_setParami(EffectProps *props, ALenum param, int val)
+constexpr EffectProps genDefaultProps() noexcept
+{
+    CompressorProps props{};
+    props.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF;
+    return props;
+}
+
+} // namespace
+
+const EffectProps CompressorEffectProps{genDefaultProps()};
+
+void CompressorEffectHandler::SetParami(CompressorProps &props, ALenum param, int val)
 {
     switch(param)
     {
     case AL_COMPRESSOR_ONOFF:
         if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF))
             throw effect_exception{AL_INVALID_VALUE, "Compressor state out of range"};
-        props->Compressor.OnOff = (val != AL_FALSE);
+        props.OnOff = (val != AL_FALSE);
         break;
 
     default:
@@ -31,51 +43,36 @@ void Compressor_setParami(EffectProps *props, ALenum param, int val)
             param};
     }
 }
-void Compressor_setParamiv(EffectProps *props, ALenum param, const int *vals)
-{ Compressor_setParami(props, param, vals[0]); }
-void Compressor_setParamf(EffectProps*, ALenum param, float)
+void CompressorEffectHandler::SetParamiv(CompressorProps &props, ALenum param, const int *vals)
+{ SetParami(props, param, *vals); }
+void CompressorEffectHandler::SetParamf(CompressorProps&, ALenum param, float)
 { throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; }
-void Compressor_setParamfv(EffectProps*, ALenum param, const float*)
+void CompressorEffectHandler::SetParamfv(CompressorProps&, ALenum param, const float*)
 {
     throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x",
         param};
 }
 
-void Compressor_getParami(const EffectProps *props, ALenum param, int *val)
+void CompressorEffectHandler::GetParami(const CompressorProps &props, ALenum param, int *val)
 { 
     switch(param)
     {
-    case AL_COMPRESSOR_ONOFF:
-        *val = props->Compressor.OnOff;
-        break;
-
+    case AL_COMPRESSOR_ONOFF: *val = props.OnOff; break;
     default:
         throw effect_exception{AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
             param};
     }
 }
-void Compressor_getParamiv(const EffectProps *props, ALenum param, int *vals)
-{ Compressor_getParami(props, param, vals); }
-void Compressor_getParamf(const EffectProps*, ALenum param, float*)
+void CompressorEffectHandler::GetParamiv(const CompressorProps &props, ALenum param, int *vals)
+{ GetParami(props, param, vals); }
+void CompressorEffectHandler::GetParamf(const CompressorProps&, ALenum param, float*)
 { throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; }
-void Compressor_getParamfv(const EffectProps*, ALenum param, float*)
+void CompressorEffectHandler::GetParamfv(const CompressorProps&, ALenum param, float*)
 {
     throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x",
         param};
 }
 
-EffectProps genDefaultProps() noexcept
-{
-    EffectProps props{};
-    props.Compressor.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF;
-    return props;
-}
-
-} // namespace
-
-DEFINE_ALEFFECT_VTABLE(Compressor);
-
-const EffectProps CompressorEffectProps{genDefaultProps()};
 
 #ifdef ALSOFT_EAX
 namespace {
@@ -115,46 +112,40 @@ template<>
     throw Exception{message};
 }
 
-template<>
-bool CompressorCommitter::commit(const EaxEffectProps &props)
+bool EaxCompressorCommitter::commit(const EAXAGCCOMPRESSORPROPERTIES &props)
 {
-    if(props.mType == mEaxProps.mType
-        && props.mCompressor.ulOnOff == mEaxProps.mCompressor.ulOnOff)
+    if(auto *cur = std::get_if<EAXAGCCOMPRESSORPROPERTIES>(&mEaxProps); cur && *cur == props)
         return false;
 
     mEaxProps = props;
+    mAlProps = CompressorProps{props.ulOnOff != 0};
 
-    mAlProps.Compressor.OnOff = (props.mCompressor.ulOnOff != 0);
     return true;
 }
 
-template<>
-void CompressorCommitter::SetDefaults(EaxEffectProps &props)
+void EaxCompressorCommitter::SetDefaults(EaxEffectProps &props)
 {
-    props.mType = EaxEffectType::Compressor;
-    props.mCompressor.ulOnOff = EAXAGCCOMPRESSOR_DEFAULTONOFF;
+    props = EAXAGCCOMPRESSORPROPERTIES{EAXAGCCOMPRESSOR_DEFAULTONOFF};
 }
 
-template<>
-void CompressorCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+void EaxCompressorCommitter::Get(const EaxCall &call, const EAXAGCCOMPRESSORPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXAGCCOMPRESSOR_NONE: break;
-    case EAXAGCCOMPRESSOR_ALLPARAMETERS: call.set_value<Exception>(props.mCompressor); break;
-    case EAXAGCCOMPRESSOR_ONOFF: call.set_value<Exception>(props.mCompressor.ulOnOff); break;
+    case EAXAGCCOMPRESSOR_ALLPARAMETERS: call.set_value<Exception>(props); break;
+    case EAXAGCCOMPRESSOR_ONOFF: call.set_value<Exception>(props.ulOnOff); break;
     default: fail_unknown_property_id();
     }
 }
 
-template<>
-void CompressorCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+void EaxCompressorCommitter::Set(const EaxCall &call, EAXAGCCOMPRESSORPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXAGCCOMPRESSOR_NONE: break;
-    case EAXAGCCOMPRESSOR_ALLPARAMETERS: defer<AllValidator>(call, props.mCompressor); break;
-    case EAXAGCCOMPRESSOR_ONOFF: defer<OnOffValidator>(call, props.mCompressor.ulOnOff); break;
+    case EAXAGCCOMPRESSOR_ALLPARAMETERS: defer<AllValidator>(call, props); break;
+    case EAXAGCCOMPRESSOR_ONOFF: defer<OnOffValidator>(call, props.ulOnOff); break;
     default: fail_unknown_property_id();
     }
 }

+ 54 - 30
Engine/lib/openal-soft/al/effects/convolution.cpp

@@ -1,93 +1,117 @@
 
 #include "config.h"
 
+#include <algorithm>
+#include <array>
+#include <cmath>
+
 #include "AL/al.h"
-#include "alc/inprogext.h"
 
-#include "alc/effects/base.h"
+#include "alc/inprogext.h"
+#include "alnumeric.h"
+#include "alspan.h"
+#include "core/effects/base.h"
 #include "effects.h"
 
 
 namespace {
 
-void Convolution_setParami(EffectProps* /*props*/, ALenum param, int /*val*/)
+constexpr EffectProps genDefaultProps() noexcept
+{
+    ConvolutionProps props{};
+    props.OrientAt = {0.0f,  0.0f, -1.0f};
+    props.OrientUp = {0.0f,  1.0f,  0.0f};
+    return props;
+}
+
+} // namespace
+
+const EffectProps ConvolutionEffectProps{genDefaultProps()};
+
+void ConvolutionEffectHandler::SetParami(ConvolutionProps& /*props*/, ALenum param, int /*val*/)
 {
     switch(param)
     {
     default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
+        throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect integer property 0x%04x",
             param};
     }
 }
-void Convolution_setParamiv(EffectProps *props, ALenum param, const int *vals)
+void ConvolutionEffectHandler::SetParamiv(ConvolutionProps &props, ALenum param, const int *vals)
 {
     switch(param)
     {
     default:
-        Convolution_setParami(props, param, vals[0]);
+        SetParami(props, param, *vals);
     }
 }
-void Convolution_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/)
+void ConvolutionEffectHandler::SetParamf(ConvolutionProps& /*props*/, ALenum param, float /*val*/)
 {
     switch(param)
     {
     default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
+        throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect float property 0x%04x",
             param};
     }
 }
-void Convolution_setParamfv(EffectProps *props, ALenum param, const float *vals)
+void ConvolutionEffectHandler::SetParamfv(ConvolutionProps &props, ALenum param, const float *values)
 {
+    static constexpr auto finite_checker = [](float val) -> bool { return std::isfinite(val); };
+    al::span<const float> vals;
     switch(param)
     {
+    case AL_CONVOLUTION_ORIENTATION_SOFT:
+        vals = {values, 6_uz};
+        if(!std::all_of(vals.cbegin(), vals.cend(), finite_checker))
+            throw effect_exception{AL_INVALID_VALUE, "Property 0x%04x value out of range", param};
+
+        std::copy_n(vals.cbegin(), props.OrientAt.size(), props.OrientAt.begin());
+        std::copy_n(vals.cbegin()+3, props.OrientUp.size(), props.OrientUp.begin());
+        break;
+
     default:
-        Convolution_setParamf(props, param, vals[0]);
+        SetParamf(props, param, *values);
     }
 }
 
-void Convolution_getParami(const EffectProps* /*props*/, ALenum param, int* /*val*/)
+void ConvolutionEffectHandler::GetParami(const ConvolutionProps& /*props*/, ALenum param, int* /*val*/)
 {
     switch(param)
     {
     default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
+        throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect integer property 0x%04x",
             param};
     }
 }
-void Convolution_getParamiv(const EffectProps *props, ALenum param, int *vals)
+void ConvolutionEffectHandler::GetParamiv(const ConvolutionProps &props, ALenum param, int *vals)
 {
     switch(param)
     {
     default:
-        Convolution_getParami(props, param, vals);
+        GetParami(props, param, vals);
     }
 }
-void Convolution_getParamf(const EffectProps* /*props*/, ALenum param, float* /*val*/)
+void ConvolutionEffectHandler::GetParamf(const ConvolutionProps& /*props*/, ALenum param, float* /*val*/)
 {
     switch(param)
     {
     default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
+        throw effect_exception{AL_INVALID_ENUM, "Invalid convolution effect float property 0x%04x",
             param};
     }
 }
-void Convolution_getParamfv(const EffectProps *props, ALenum param, float *vals)
+void ConvolutionEffectHandler::GetParamfv(const ConvolutionProps &props, ALenum param, float *values)
 {
+    al::span<float> vals;
     switch(param)
     {
+    case AL_CONVOLUTION_ORIENTATION_SOFT:
+        vals = {values, 6_uz};
+        std::copy(props.OrientAt.cbegin(), props.OrientAt.cend(), vals.begin());
+        std::copy(props.OrientUp.cbegin(), props.OrientUp.cend(), vals.begin()+3);
+        break;
+
     default:
-        Convolution_getParamf(props, param, vals);
+        GetParamf(props, param, values);
     }
 }
-
-EffectProps genDefaultProps() noexcept
-{
-    EffectProps props{};
-    return props;
-}
-
-} // namespace
-
-DEFINE_ALEFFECT_VTABLE(Convolution);
-
-const EffectProps ConvolutionEffectProps{genDefaultProps()};

+ 72 - 22
Engine/lib/openal-soft/al/effects/dedicated.cpp

@@ -12,61 +12,111 @@
 
 namespace {
 
-void Dedicated_setParami(EffectProps*, ALenum param, int)
+constexpr EffectProps genDefaultDialogProps() noexcept
+{
+    DedicatedProps props{};
+    props.Target = DedicatedProps::Dialog;
+    props.Gain = 1.0f;
+    return props;
+}
+
+constexpr EffectProps genDefaultLfeProps() noexcept
+{
+    DedicatedProps props{};
+    props.Target = DedicatedProps::Lfe;
+    props.Gain = 1.0f;
+    return props;
+}
+
+} // namespace
+
+const EffectProps DedicatedDialogEffectProps{genDefaultDialogProps()};
+
+void DedicatedDialogEffectHandler::SetParami(DedicatedProps&, ALenum param, int)
 { throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
-void Dedicated_setParamiv(EffectProps*, ALenum param, const int*)
+void DedicatedDialogEffectHandler::SetParamiv(DedicatedProps&, ALenum param, const int*)
 {
     throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
         param};
 }
-void Dedicated_setParamf(EffectProps *props, ALenum param, float val)
+void DedicatedDialogEffectHandler::SetParamf(DedicatedProps &props, ALenum param, float val)
 {
     switch(param)
     {
     case AL_DEDICATED_GAIN:
         if(!(val >= 0.0f && std::isfinite(val)))
             throw effect_exception{AL_INVALID_VALUE, "Dedicated gain out of range"};
-        props->Dedicated.Gain = val;
+        props.Gain = val;
         break;
 
     default:
         throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
     }
 }
-void Dedicated_setParamfv(EffectProps *props, ALenum param, const float *vals)
-{ Dedicated_setParamf(props, param, vals[0]); }
+void DedicatedDialogEffectHandler::SetParamfv(DedicatedProps &props, ALenum param, const float *vals)
+{ SetParamf(props, param, *vals); }
 
-void Dedicated_getParami(const EffectProps*, ALenum param, int*)
+void DedicatedDialogEffectHandler::GetParami(const DedicatedProps&, ALenum param, int*)
 { throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
-void Dedicated_getParamiv(const EffectProps*, ALenum param, int*)
+void DedicatedDialogEffectHandler::GetParamiv(const DedicatedProps&, ALenum param, int*)
 {
     throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
         param};
 }
-void Dedicated_getParamf(const EffectProps *props, ALenum param, float *val)
+void DedicatedDialogEffectHandler::GetParamf(const DedicatedProps &props, ALenum param, float *val)
+{
+    switch(param)
+    {
+    case AL_DEDICATED_GAIN: *val = props.Gain; break;
+    default:
+        throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
+    }
+}
+void DedicatedDialogEffectHandler::GetParamfv(const DedicatedProps &props, ALenum param, float *vals)
+{ GetParamf(props, param, vals); }
+
+
+const EffectProps DedicatedLfeEffectProps{genDefaultLfeProps()};
+
+void DedicatedLfeEffectHandler::SetParami(DedicatedProps&, ALenum param, int)
+{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
+void DedicatedLfeEffectHandler::SetParamiv(DedicatedProps&, ALenum param, const int*)
+{
+    throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
+        param};
+}
+void DedicatedLfeEffectHandler::SetParamf(DedicatedProps &props, ALenum param, float val)
 {
     switch(param)
     {
     case AL_DEDICATED_GAIN:
-        *val = props->Dedicated.Gain;
+        if(!(val >= 0.0f && std::isfinite(val)))
+            throw effect_exception{AL_INVALID_VALUE, "Dedicated gain out of range"};
+        props.Gain = val;
         break;
 
     default:
         throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
     }
 }
-void Dedicated_getParamfv(const EffectProps *props, ALenum param, float *vals)
-{ Dedicated_getParamf(props, param, vals); }
+void DedicatedLfeEffectHandler::SetParamfv(DedicatedProps &props, ALenum param, const float *vals)
+{ SetParamf(props, param, *vals); }
 
-EffectProps genDefaultProps() noexcept
+void DedicatedLfeEffectHandler::GetParami(const DedicatedProps&, ALenum param, int*)
+{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
+void DedicatedLfeEffectHandler::GetParamiv(const DedicatedProps&, ALenum param, int*)
 {
-    EffectProps props{};
-    props.Dedicated.Gain = 1.0f;
-    return props;
+    throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
+        param};
 }
-
-} // namespace
-
-DEFINE_ALEFFECT_VTABLE(Dedicated);
-
-const EffectProps DedicatedEffectProps{genDefaultProps()};
+void DedicatedLfeEffectHandler::GetParamf(const DedicatedProps &props, ALenum param, float *val)
+{
+    switch(param)
+    {
+    case AL_DEDICATED_GAIN: *val = props.Gain; break;
+    default:
+        throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
+    }
+}
+void DedicatedLfeEffectHandler::GetParamfv(const DedicatedProps &props, ALenum param, float *vals)
+{ GetParamf(props, param, vals); }

+ 73 - 87
Engine/lib/openal-soft/al/effects/distortion.cpp

@@ -9,6 +9,7 @@
 
 #ifdef ALSOFT_EAX
 #include "alnumeric.h"
+#include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #endif // ALSOFT_EAX
@@ -16,108 +17,93 @@
 
 namespace {
 
-void Distortion_setParami(EffectProps*, ALenum param, int)
+constexpr EffectProps genDefaultProps() noexcept
+{
+    DistortionProps props{};
+    props.Edge = AL_DISTORTION_DEFAULT_EDGE;
+    props.Gain = AL_DISTORTION_DEFAULT_GAIN;
+    props.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF;
+    props.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER;
+    props.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH;
+    return props;
+}
+
+} // namespace
+
+const EffectProps DistortionEffectProps{genDefaultProps()};
+
+void DistortionEffectHandler::SetParami(DistortionProps&, ALenum param, int)
 { throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; }
-void Distortion_setParamiv(EffectProps*, ALenum param, const int*)
+void DistortionEffectHandler::SetParamiv(DistortionProps&, ALenum param, const int*)
 {
     throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x",
         param};
 }
-void Distortion_setParamf(EffectProps *props, ALenum param, float val)
+void DistortionEffectHandler::SetParamf(DistortionProps &props, ALenum param, float val)
 {
     switch(param)
     {
     case AL_DISTORTION_EDGE:
         if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE))
             throw effect_exception{AL_INVALID_VALUE, "Distortion edge out of range"};
-        props->Distortion.Edge = val;
+        props.Edge = val;
         break;
 
     case AL_DISTORTION_GAIN:
         if(!(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN))
             throw effect_exception{AL_INVALID_VALUE, "Distortion gain out of range"};
-        props->Distortion.Gain = val;
+        props.Gain = val;
         break;
 
     case AL_DISTORTION_LOWPASS_CUTOFF:
         if(!(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_LOWPASS_CUTOFF))
             throw effect_exception{AL_INVALID_VALUE, "Distortion low-pass cutoff out of range"};
-        props->Distortion.LowpassCutoff = val;
+        props.LowpassCutoff = val;
         break;
 
     case AL_DISTORTION_EQCENTER:
         if(!(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER))
             throw effect_exception{AL_INVALID_VALUE, "Distortion EQ center out of range"};
-        props->Distortion.EQCenter = val;
+        props.EQCenter = val;
         break;
 
     case AL_DISTORTION_EQBANDWIDTH:
         if(!(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH))
             throw effect_exception{AL_INVALID_VALUE, "Distortion EQ bandwidth out of range"};
-        props->Distortion.EQBandwidth = val;
+        props.EQBandwidth = val;
         break;
 
     default:
         throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param};
     }
 }
-void Distortion_setParamfv(EffectProps *props, ALenum param, const float *vals)
-{ Distortion_setParamf(props, param, vals[0]); }
+void DistortionEffectHandler::SetParamfv(DistortionProps &props, ALenum param, const float *vals)
+{ SetParamf(props, param, *vals); }
 
-void Distortion_getParami(const EffectProps*, ALenum param, int*)
+void DistortionEffectHandler::GetParami(const DistortionProps&, ALenum param, int*)
 { throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; }
-void Distortion_getParamiv(const EffectProps*, ALenum param, int*)
+void DistortionEffectHandler::GetParamiv(const DistortionProps&, ALenum param, int*)
 {
     throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x",
         param};
 }
-void Distortion_getParamf(const EffectProps *props, ALenum param, float *val)
+void DistortionEffectHandler::GetParamf(const DistortionProps &props, ALenum param, float *val)
 {
     switch(param)
     {
-    case AL_DISTORTION_EDGE:
-        *val = props->Distortion.Edge;
-        break;
-
-    case AL_DISTORTION_GAIN:
-        *val = props->Distortion.Gain;
-        break;
-
-    case AL_DISTORTION_LOWPASS_CUTOFF:
-        *val = props->Distortion.LowpassCutoff;
-        break;
-
-    case AL_DISTORTION_EQCENTER:
-        *val = props->Distortion.EQCenter;
-        break;
-
-    case AL_DISTORTION_EQBANDWIDTH:
-        *val = props->Distortion.EQBandwidth;
-        break;
+    case AL_DISTORTION_EDGE: *val = props.Edge; break;
+    case AL_DISTORTION_GAIN: *val = props.Gain; break;
+    case AL_DISTORTION_LOWPASS_CUTOFF: *val = props.LowpassCutoff; break;
+    case AL_DISTORTION_EQCENTER: *val = props.EQCenter; break;
+    case AL_DISTORTION_EQBANDWIDTH: *val = props.EQBandwidth; break;
 
     default:
         throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param};
     }
 }
-void Distortion_getParamfv(const EffectProps *props, ALenum param, float *vals)
-{ Distortion_getParamf(props, param, vals); }
-
-EffectProps genDefaultProps() noexcept
-{
-    EffectProps props{};
-    props.Distortion.Edge = AL_DISTORTION_DEFAULT_EDGE;
-    props.Distortion.Gain = AL_DISTORTION_DEFAULT_GAIN;
-    props.Distortion.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF;
-    props.Distortion.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER;
-    props.Distortion.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH;
-    return props;
-}
-
-} // namespace
-
-DEFINE_ALEFFECT_VTABLE(Distortion);
+void DistortionEffectHandler::GetParamfv(const DistortionProps &props, ALenum param, float *vals)
+{ GetParamf(props, param, vals); }
 
-const EffectProps DistortionEffectProps{genDefaultProps()};
 
 #ifdef ALSOFT_EAX
 namespace {
@@ -204,66 +190,66 @@ template<>
     throw Exception{message};
 }
 
-template<>
-bool DistortionCommitter::commit(const EaxEffectProps &props)
+bool EaxDistortionCommitter::commit(const EAXDISTORTIONPROPERTIES &props)
 {
-    if(props.mType == mEaxProps.mType && mEaxProps.mDistortion.flEdge == props.mDistortion.flEdge
-        && mEaxProps.mDistortion.lGain == props.mDistortion.lGain
-        && mEaxProps.mDistortion.flLowPassCutOff == props.mDistortion.flLowPassCutOff
-        && mEaxProps.mDistortion.flEQCenter == props.mDistortion.flEQCenter
-        && mEaxProps.mDistortion.flEQBandwidth == props.mDistortion.flEQBandwidth)
+    if(auto *cur = std::get_if<EAXDISTORTIONPROPERTIES>(&mEaxProps); cur && *cur == props)
         return false;
 
     mEaxProps = props;
-
-    mAlProps.Distortion.Edge = props.mDistortion.flEdge;
-    mAlProps.Distortion.Gain = level_mb_to_gain(static_cast<float>(props.mDistortion.lGain));
-    mAlProps.Distortion.LowpassCutoff = props.mDistortion.flLowPassCutOff;
-    mAlProps.Distortion.EQCenter = props.mDistortion.flEQCenter;
-    mAlProps.Distortion.EQBandwidth = props.mDistortion.flEdge;
+    mAlProps = [&]{
+        DistortionProps ret{};
+        ret.Edge = props.flEdge;
+        ret.Gain = level_mb_to_gain(static_cast<float>(props.lGain));
+        ret.LowpassCutoff = props.flLowPassCutOff;
+        ret.EQCenter = props.flEQCenter;
+        ret.EQBandwidth = props.flEdge;
+        return ret;
+    }();
 
     return true;
 }
 
-template<>
-void DistortionCommitter::SetDefaults(EaxEffectProps &props)
+void EaxDistortionCommitter::SetDefaults(EaxEffectProps &props)
 {
-    props.mType = EaxEffectType::Distortion;
-    props.mDistortion.flEdge = EAXDISTORTION_DEFAULTEDGE;
-    props.mDistortion.lGain = EAXDISTORTION_DEFAULTGAIN;
-    props.mDistortion.flLowPassCutOff = EAXDISTORTION_DEFAULTLOWPASSCUTOFF;
-    props.mDistortion.flEQCenter = EAXDISTORTION_DEFAULTEQCENTER;
-    props.mDistortion.flEQBandwidth = EAXDISTORTION_DEFAULTEQBANDWIDTH;
+    static constexpr EAXDISTORTIONPROPERTIES defprops{[]
+    {
+        EAXDISTORTIONPROPERTIES ret{};
+        ret.flEdge = EAXDISTORTION_DEFAULTEDGE;
+        ret.lGain = EAXDISTORTION_DEFAULTGAIN;
+        ret.flLowPassCutOff = EAXDISTORTION_DEFAULTLOWPASSCUTOFF;
+        ret.flEQCenter = EAXDISTORTION_DEFAULTEQCENTER;
+        ret.flEQBandwidth = EAXDISTORTION_DEFAULTEQBANDWIDTH;
+        return ret;
+    }()};
+    props = defprops;
 }
 
-template<>
-void DistortionCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+void EaxDistortionCommitter::Get(const EaxCall &call, const EAXDISTORTIONPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXDISTORTION_NONE: break;
-    case EAXDISTORTION_ALLPARAMETERS: call.set_value<Exception>(props.mDistortion); break;
-    case EAXDISTORTION_EDGE: call.set_value<Exception>(props.mDistortion.flEdge); break;
-    case EAXDISTORTION_GAIN: call.set_value<Exception>(props.mDistortion.lGain); break;
-    case EAXDISTORTION_LOWPASSCUTOFF: call.set_value<Exception>(props.mDistortion.flLowPassCutOff); break;
-    case EAXDISTORTION_EQCENTER: call.set_value<Exception>(props.mDistortion.flEQCenter); break;
-    case EAXDISTORTION_EQBANDWIDTH: call.set_value<Exception>(props.mDistortion.flEQBandwidth); break;
+    case EAXDISTORTION_ALLPARAMETERS: call.set_value<Exception>(props); break;
+    case EAXDISTORTION_EDGE: call.set_value<Exception>(props.flEdge); break;
+    case EAXDISTORTION_GAIN: call.set_value<Exception>(props.lGain); break;
+    case EAXDISTORTION_LOWPASSCUTOFF: call.set_value<Exception>(props.flLowPassCutOff); break;
+    case EAXDISTORTION_EQCENTER: call.set_value<Exception>(props.flEQCenter); break;
+    case EAXDISTORTION_EQBANDWIDTH: call.set_value<Exception>(props.flEQBandwidth); break;
     default: fail_unknown_property_id();
     }
 }
 
-template<>
-void DistortionCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+void EaxDistortionCommitter::Set(const EaxCall &call, EAXDISTORTIONPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXDISTORTION_NONE: break;
-    case EAXDISTORTION_ALLPARAMETERS: defer<AllValidator>(call, props.mDistortion); break;
-    case EAXDISTORTION_EDGE: defer<EdgeValidator>(call, props.mDistortion.flEdge); break;
-    case EAXDISTORTION_GAIN: defer<GainValidator>(call, props.mDistortion.lGain); break;
-    case EAXDISTORTION_LOWPASSCUTOFF: defer<LowPassCutOffValidator>(call, props.mDistortion.flLowPassCutOff); break;
-    case EAXDISTORTION_EQCENTER: defer<EqCenterValidator>(call, props.mDistortion.flEQCenter); break;
-    case EAXDISTORTION_EQBANDWIDTH: defer<EqBandwidthValidator>(call, props.mDistortion.flEQBandwidth); break;
+    case EAXDISTORTION_ALLPARAMETERS: defer<AllValidator>(call, props); break;
+    case EAXDISTORTION_EDGE: defer<EdgeValidator>(call, props.flEdge); break;
+    case EAXDISTORTION_GAIN: defer<GainValidator>(call, props.lGain); break;
+    case EAXDISTORTION_LOWPASSCUTOFF: defer<LowPassCutOffValidator>(call, props.flLowPassCutOff); break;
+    case EAXDISTORTION_EQCENTER: defer<EqCenterValidator>(call, props.flEQCenter); break;
+    case EAXDISTORTION_EQBANDWIDTH: defer<EqBandwidthValidator>(call, props.flEQBandwidth); break;
     default: fail_unknown_property_id();
     }
 }

+ 73 - 87
Engine/lib/openal-soft/al/effects/echo.cpp

@@ -9,6 +9,7 @@
 
 #ifdef ALSOFT_EAX
 #include "alnumeric.h"
+#include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #endif // ALSOFT_EAX
@@ -19,102 +20,87 @@ namespace {
 static_assert(EchoMaxDelay >= AL_ECHO_MAX_DELAY, "Echo max delay too short");
 static_assert(EchoMaxLRDelay >= AL_ECHO_MAX_LRDELAY, "Echo max left-right delay too short");
 
-void Echo_setParami(EffectProps*, ALenum param, int)
+constexpr EffectProps genDefaultProps() noexcept
+{
+    EchoProps props{};
+    props.Delay    = AL_ECHO_DEFAULT_DELAY;
+    props.LRDelay  = AL_ECHO_DEFAULT_LRDELAY;
+    props.Damping  = AL_ECHO_DEFAULT_DAMPING;
+    props.Feedback = AL_ECHO_DEFAULT_FEEDBACK;
+    props.Spread   = AL_ECHO_DEFAULT_SPREAD;
+    return props;
+}
+
+} // namespace
+
+const EffectProps EchoEffectProps{genDefaultProps()};
+
+void EchoEffectHandler::SetParami(EchoProps&, ALenum param, int)
 { throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; }
-void Echo_setParamiv(EffectProps*, ALenum param, const int*)
+void EchoEffectHandler::SetParamiv(EchoProps&, ALenum param, const int*)
 { throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; }
-void Echo_setParamf(EffectProps *props, ALenum param, float val)
+void EchoEffectHandler::SetParamf(EchoProps &props, ALenum param, float val)
 {
     switch(param)
     {
     case AL_ECHO_DELAY:
         if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY))
             throw effect_exception{AL_INVALID_VALUE, "Echo delay out of range"};
-        props->Echo.Delay = val;
+        props.Delay = val;
         break;
 
     case AL_ECHO_LRDELAY:
         if(!(val >= AL_ECHO_MIN_LRDELAY && val <= AL_ECHO_MAX_LRDELAY))
             throw effect_exception{AL_INVALID_VALUE, "Echo LR delay out of range"};
-        props->Echo.LRDelay = val;
+        props.LRDelay = val;
         break;
 
     case AL_ECHO_DAMPING:
         if(!(val >= AL_ECHO_MIN_DAMPING && val <= AL_ECHO_MAX_DAMPING))
             throw effect_exception{AL_INVALID_VALUE, "Echo damping out of range"};
-        props->Echo.Damping = val;
+        props.Damping = val;
         break;
 
     case AL_ECHO_FEEDBACK:
         if(!(val >= AL_ECHO_MIN_FEEDBACK && val <= AL_ECHO_MAX_FEEDBACK))
             throw effect_exception{AL_INVALID_VALUE, "Echo feedback out of range"};
-        props->Echo.Feedback = val;
+        props.Feedback = val;
         break;
 
     case AL_ECHO_SPREAD:
         if(!(val >= AL_ECHO_MIN_SPREAD && val <= AL_ECHO_MAX_SPREAD))
             throw effect_exception{AL_INVALID_VALUE, "Echo spread out of range"};
-        props->Echo.Spread = val;
+        props.Spread = val;
         break;
 
     default:
         throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param};
     }
 }
-void Echo_setParamfv(EffectProps *props, ALenum param, const float *vals)
-{ Echo_setParamf(props, param, vals[0]); }
+void EchoEffectHandler::SetParamfv(EchoProps &props, ALenum param, const float *vals)
+{ SetParamf(props, param, *vals); }
 
-void Echo_getParami(const EffectProps*, ALenum param, int*)
+void EchoEffectHandler::GetParami(const EchoProps&, ALenum param, int*)
 { throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; }
-void Echo_getParamiv(const EffectProps*, ALenum param, int*)
+void EchoEffectHandler::GetParamiv(const EchoProps&, ALenum param, int*)
 { throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; }
-void Echo_getParamf(const EffectProps *props, ALenum param, float *val)
+void EchoEffectHandler::GetParamf(const EchoProps &props, ALenum param, float *val)
 {
     switch(param)
     {
-    case AL_ECHO_DELAY:
-        *val = props->Echo.Delay;
-        break;
-
-    case AL_ECHO_LRDELAY:
-        *val = props->Echo.LRDelay;
-        break;
-
-    case AL_ECHO_DAMPING:
-        *val = props->Echo.Damping;
-        break;
-
-    case AL_ECHO_FEEDBACK:
-        *val = props->Echo.Feedback;
-        break;
-
-    case AL_ECHO_SPREAD:
-        *val = props->Echo.Spread;
-        break;
+    case AL_ECHO_DELAY: *val = props.Delay; break;
+    case AL_ECHO_LRDELAY: *val = props.LRDelay; break;
+    case AL_ECHO_DAMPING: *val = props.Damping; break;
+    case AL_ECHO_FEEDBACK: *val = props.Feedback; break;
+    case AL_ECHO_SPREAD: *val = props.Spread; break;
 
     default:
         throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param};
     }
 }
-void Echo_getParamfv(const EffectProps *props, ALenum param, float *vals)
-{ Echo_getParamf(props, param, vals); }
-
-EffectProps genDefaultProps() noexcept
-{
-    EffectProps props{};
-    props.Echo.Delay    = AL_ECHO_DEFAULT_DELAY;
-    props.Echo.LRDelay  = AL_ECHO_DEFAULT_LRDELAY;
-    props.Echo.Damping  = AL_ECHO_DEFAULT_DAMPING;
-    props.Echo.Feedback = AL_ECHO_DEFAULT_FEEDBACK;
-    props.Echo.Spread   = AL_ECHO_DEFAULT_SPREAD;
-    return props;
-}
-
-} // namespace
-
-DEFINE_ALEFFECT_VTABLE(Echo);
+void EchoEffectHandler::GetParamfv(const EchoProps &props, ALenum param, float *vals)
+{ GetParamf(props, param, vals); }
 
-const EffectProps EchoEffectProps{genDefaultProps()};
 
 #ifdef ALSOFT_EAX
 namespace {
@@ -201,66 +187,66 @@ template<>
     throw Exception{message};
 }
 
-template<>
-bool EchoCommitter::commit(const EaxEffectProps &props)
+bool EaxEchoCommitter::commit(const EAXECHOPROPERTIES &props)
 {
-    if(props.mType == mEaxProps.mType && mEaxProps.mEcho.flDelay == props.mEcho.flDelay
-        && mEaxProps.mEcho.flLRDelay == props.mEcho.flLRDelay
-        && mEaxProps.mEcho.flDamping == props.mEcho.flDamping
-        && mEaxProps.mEcho.flFeedback == props.mEcho.flFeedback
-        && mEaxProps.mEcho.flSpread == props.mEcho.flSpread)
+    if(auto *cur = std::get_if<EAXECHOPROPERTIES>(&mEaxProps); cur && *cur == props)
         return false;
 
     mEaxProps = props;
-
-    mAlProps.Echo.Delay = props.mEcho.flDelay;
-    mAlProps.Echo.LRDelay = props.mEcho.flLRDelay;
-    mAlProps.Echo.Damping = props.mEcho.flDamping;
-    mAlProps.Echo.Feedback = props.mEcho.flFeedback;
-    mAlProps.Echo.Spread = props.mEcho.flSpread;
+    mAlProps = [&]{
+        EchoProps ret{};
+        ret.Delay = props.flDelay;
+        ret.LRDelay = props.flLRDelay;
+        ret.Damping = props.flDamping;
+        ret.Feedback = props.flFeedback;
+        ret.Spread = props.flSpread;
+        return ret;
+    }();
 
     return true;
 }
 
-template<>
-void EchoCommitter::SetDefaults(EaxEffectProps &props)
+void EaxEchoCommitter::SetDefaults(EaxEffectProps &props)
 {
-    props.mType = EaxEffectType::Echo;
-    props.mEcho.flDelay = EAXECHO_DEFAULTDELAY;
-    props.mEcho.flLRDelay = EAXECHO_DEFAULTLRDELAY;
-    props.mEcho.flDamping = EAXECHO_DEFAULTDAMPING;
-    props.mEcho.flFeedback = EAXECHO_DEFAULTFEEDBACK;
-    props.mEcho.flSpread = EAXECHO_DEFAULTSPREAD;
+    static constexpr EAXECHOPROPERTIES defprops{[]
+    {
+        EAXECHOPROPERTIES ret{};
+        ret.flDelay = EAXECHO_DEFAULTDELAY;
+        ret.flLRDelay = EAXECHO_DEFAULTLRDELAY;
+        ret.flDamping = EAXECHO_DEFAULTDAMPING;
+        ret.flFeedback = EAXECHO_DEFAULTFEEDBACK;
+        ret.flSpread = EAXECHO_DEFAULTSPREAD;
+        return ret;
+    }()};
+    props = defprops;
 }
 
-template<>
-void EchoCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+void EaxEchoCommitter::Get(const EaxCall &call, const EAXECHOPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXECHO_NONE: break;
-    case EAXECHO_ALLPARAMETERS: call.set_value<Exception>(props.mEcho); break;
-    case EAXECHO_DELAY: call.set_value<Exception>(props.mEcho.flDelay); break;
-    case EAXECHO_LRDELAY: call.set_value<Exception>(props.mEcho.flLRDelay); break;
-    case EAXECHO_DAMPING: call.set_value<Exception>(props.mEcho.flDamping); break;
-    case EAXECHO_FEEDBACK: call.set_value<Exception>(props.mEcho.flFeedback); break;
-    case EAXECHO_SPREAD: call.set_value<Exception>(props.mEcho.flSpread); break;
+    case EAXECHO_ALLPARAMETERS: call.set_value<Exception>(props); break;
+    case EAXECHO_DELAY: call.set_value<Exception>(props.flDelay); break;
+    case EAXECHO_LRDELAY: call.set_value<Exception>(props.flLRDelay); break;
+    case EAXECHO_DAMPING: call.set_value<Exception>(props.flDamping); break;
+    case EAXECHO_FEEDBACK: call.set_value<Exception>(props.flFeedback); break;
+    case EAXECHO_SPREAD: call.set_value<Exception>(props.flSpread); break;
     default: fail_unknown_property_id();
     }
 }
 
-template<>
-void EchoCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+void EaxEchoCommitter::Set(const EaxCall &call, EAXECHOPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXECHO_NONE: break;
-    case EAXECHO_ALLPARAMETERS: defer<AllValidator>(call, props.mEcho); break;
-    case EAXECHO_DELAY: defer<DelayValidator>(call, props.mEcho.flDelay); break;
-    case EAXECHO_LRDELAY: defer<LrDelayValidator>(call, props.mEcho.flLRDelay); break;
-    case EAXECHO_DAMPING: defer<DampingValidator>(call, props.mEcho.flDamping); break;
-    case EAXECHO_FEEDBACK: defer<FeedbackValidator>(call, props.mEcho.flFeedback); break;
-    case EAXECHO_SPREAD: defer<SpreadValidator>(call, props.mEcho.flSpread); break;
+    case EAXECHO_ALLPARAMETERS: defer<AllValidator>(call, props); break;
+    case EAXECHO_DELAY: defer<DelayValidator>(call, props.flDelay); break;
+    case EAXECHO_LRDELAY: defer<LrDelayValidator>(call, props.flLRDelay); break;
+    case EAXECHO_DAMPING: defer<DampingValidator>(call, props.flDamping); break;
+    case EAXECHO_FEEDBACK: defer<FeedbackValidator>(call, props.flFeedback); break;
+    case EAXECHO_SPREAD: defer<SpreadValidator>(call, props.flSpread); break;
     default: fail_unknown_property_id();
     }
 }

+ 0 - 6
Engine/lib/openal-soft/al/effects/effects.cpp

@@ -1,9 +1,3 @@
 #include "config.h"
 
-#ifdef ALSOFT_EAX
-
-#include <cassert>
-#include "AL/efx.h"
 #include "effects.h"
-
-#endif // ALSOFT_EAX

+ 40 - 62
Engine/lib/openal-soft/al/effects/effects.h

@@ -1,52 +1,47 @@
 #ifndef AL_EFFECTS_EFFECTS_H
 #define AL_EFFECTS_EFFECTS_H
 
-#include "AL/al.h"
-
-#include "core/except.h"
-
-#ifdef ALSOFT_EAX
-#include "al/eax/effect.h"
-#endif // ALSOFT_EAX
-
-union EffectProps;
-
-
-class effect_exception final : public al::base_exception {
-    ALenum mErrorCode;
-
-public:
-#ifdef __USE_MINGW_ANSI_STDIO
-    [[gnu::format(gnu_printf, 3, 4)]]
-#else
-    [[gnu::format(printf, 3, 4)]]
-#endif
-    effect_exception(ALenum code, const char *msg, ...);
-    ~effect_exception() override;
+#include <variant>
 
-    ALenum errorCode() const noexcept { return mErrorCode; }
-};
-
-
-struct EffectVtable {
-    void (*const setParami)(EffectProps *props, ALenum param, int val);
-    void (*const setParamiv)(EffectProps *props, ALenum param, const int *vals);
-    void (*const setParamf)(EffectProps *props, ALenum param, float val);
-    void (*const setParamfv)(EffectProps *props, ALenum param, const float *vals);
+#include "AL/al.h"
 
-    void (*const getParami)(const EffectProps *props, ALenum param, int *val);
-    void (*const getParamiv)(const EffectProps *props, ALenum param, int *vals);
-    void (*const getParamf)(const EffectProps *props, ALenum param, float *val);
-    void (*const getParamfv)(const EffectProps *props, ALenum param, float *vals);
+#include "al/error.h"
+#include "core/effects/base.h"
+
+#define DECL_HANDLER(N, T)                                                    \
+struct N {                                                                    \
+    using prop_type = T;                                                      \
+                                                                              \
+    static void SetParami(prop_type &props, ALenum param, int val);           \
+    static void SetParamiv(prop_type &props, ALenum param, const int *vals);  \
+    static void SetParamf(prop_type &props, ALenum param, float val);         \
+    static void SetParamfv(prop_type &props, ALenum param, const float *vals);\
+    static void GetParami(const prop_type &props, ALenum param, int *val);    \
+    static void GetParamiv(const prop_type &props, ALenum param, int *vals);  \
+    static void GetParamf(const prop_type &props, ALenum param, float *val);  \
+    static void GetParamfv(const prop_type &props, ALenum param, float *vals);\
 };
-
-#define DEFINE_ALEFFECT_VTABLE(T)           \
-const EffectVtable T##EffectVtable = {      \
-    T##_setParami, T##_setParamiv,          \
-    T##_setParamf, T##_setParamfv,          \
-    T##_getParami, T##_getParamiv,          \
-    T##_getParamf, T##_getParamfv,          \
-}
+DECL_HANDLER(NullEffectHandler, std::monostate)
+DECL_HANDLER(ReverbEffectHandler, ReverbProps)
+DECL_HANDLER(StdReverbEffectHandler, ReverbProps)
+DECL_HANDLER(AutowahEffectHandler, AutowahProps)
+DECL_HANDLER(ChorusEffectHandler, ChorusProps)
+DECL_HANDLER(CompressorEffectHandler, CompressorProps)
+DECL_HANDLER(DistortionEffectHandler, DistortionProps)
+DECL_HANDLER(EchoEffectHandler, EchoProps)
+DECL_HANDLER(EqualizerEffectHandler, EqualizerProps)
+DECL_HANDLER(FlangerEffectHandler, ChorusProps)
+DECL_HANDLER(FshifterEffectHandler, FshifterProps)
+DECL_HANDLER(ModulatorEffectHandler, ModulatorProps)
+DECL_HANDLER(PshifterEffectHandler, PshifterProps)
+DECL_HANDLER(VmorpherEffectHandler, VmorpherProps)
+DECL_HANDLER(DedicatedDialogEffectHandler, DedicatedProps)
+DECL_HANDLER(DedicatedLfeEffectHandler, DedicatedProps)
+DECL_HANDLER(ConvolutionEffectHandler, ConvolutionProps)
+#undef DECL_HANDLER
+
+
+using effect_exception = al::context_error;
 
 
 /* Default properties for the given effect types. */
@@ -64,25 +59,8 @@ extern const EffectProps FshifterEffectProps;
 extern const EffectProps ModulatorEffectProps;
 extern const EffectProps PshifterEffectProps;
 extern const EffectProps VmorpherEffectProps;
-extern const EffectProps DedicatedEffectProps;
+extern const EffectProps DedicatedDialogEffectProps;
+extern const EffectProps DedicatedLfeEffectProps;
 extern const EffectProps ConvolutionEffectProps;
 
-/* Vtables to get/set properties for the given effect types. */
-extern const EffectVtable NullEffectVtable;
-extern const EffectVtable ReverbEffectVtable;
-extern const EffectVtable StdReverbEffectVtable;
-extern const EffectVtable AutowahEffectVtable;
-extern const EffectVtable ChorusEffectVtable;
-extern const EffectVtable CompressorEffectVtable;
-extern const EffectVtable DistortionEffectVtable;
-extern const EffectVtable EchoEffectVtable;
-extern const EffectVtable EqualizerEffectVtable;
-extern const EffectVtable FlangerEffectVtable;
-extern const EffectVtable FshifterEffectVtable;
-extern const EffectVtable ModulatorEffectVtable;
-extern const EffectVtable PshifterEffectVtable;
-extern const EffectVtable VmorpherEffectVtable;
-extern const EffectVtable DedicatedEffectVtable;
-extern const EffectVtable ConvolutionEffectVtable;
-
 #endif /* AL_EFFECTS_EFFECTS_H */

+ 108 - 142
Engine/lib/openal-soft/al/effects/equalizer.cpp

@@ -9,6 +9,7 @@
 
 #ifdef ALSOFT_EAX
 #include "alnumeric.h"
+#include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #endif // ALSOFT_EAX
@@ -16,163 +17,133 @@
 
 namespace {
 
-void Equalizer_setParami(EffectProps*, ALenum param, int)
+constexpr EffectProps genDefaultProps() noexcept
+{
+    EqualizerProps props{};
+    props.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF;
+    props.LowGain = AL_EQUALIZER_DEFAULT_LOW_GAIN;
+    props.Mid1Center = AL_EQUALIZER_DEFAULT_MID1_CENTER;
+    props.Mid1Gain = AL_EQUALIZER_DEFAULT_MID1_GAIN;
+    props.Mid1Width = AL_EQUALIZER_DEFAULT_MID1_WIDTH;
+    props.Mid2Center = AL_EQUALIZER_DEFAULT_MID2_CENTER;
+    props.Mid2Gain = AL_EQUALIZER_DEFAULT_MID2_GAIN;
+    props.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH;
+    props.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF;
+    props.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN;
+    return props;
+}
+
+} // namespace
+
+const EffectProps EqualizerEffectProps{genDefaultProps()};
+
+void EqualizerEffectHandler::SetParami(EqualizerProps&, ALenum param, int)
 { throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; }
-void Equalizer_setParamiv(EffectProps*, ALenum param, const int*)
+void EqualizerEffectHandler::SetParamiv(EqualizerProps&, ALenum param, const int*)
 {
     throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x",
         param};
 }
-void Equalizer_setParamf(EffectProps *props, ALenum param, float val)
+void EqualizerEffectHandler::SetParamf(EqualizerProps &props, ALenum param, float val)
 {
     switch(param)
     {
     case AL_EQUALIZER_LOW_GAIN:
         if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN))
             throw effect_exception{AL_INVALID_VALUE, "Equalizer low-band gain out of range"};
-        props->Equalizer.LowGain = val;
+        props.LowGain = val;
         break;
 
     case AL_EQUALIZER_LOW_CUTOFF:
         if(!(val >= AL_EQUALIZER_MIN_LOW_CUTOFF && val <= AL_EQUALIZER_MAX_LOW_CUTOFF))
             throw effect_exception{AL_INVALID_VALUE, "Equalizer low-band cutoff out of range"};
-        props->Equalizer.LowCutoff = val;
+        props.LowCutoff = val;
         break;
 
     case AL_EQUALIZER_MID1_GAIN:
         if(!(val >= AL_EQUALIZER_MIN_MID1_GAIN && val <= AL_EQUALIZER_MAX_MID1_GAIN))
             throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band gain out of range"};
-        props->Equalizer.Mid1Gain = val;
+        props.Mid1Gain = val;
         break;
 
     case AL_EQUALIZER_MID1_CENTER:
         if(!(val >= AL_EQUALIZER_MIN_MID1_CENTER && val <= AL_EQUALIZER_MAX_MID1_CENTER))
             throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band center out of range"};
-        props->Equalizer.Mid1Center = val;
+        props.Mid1Center = val;
         break;
 
     case AL_EQUALIZER_MID1_WIDTH:
         if(!(val >= AL_EQUALIZER_MIN_MID1_WIDTH && val <= AL_EQUALIZER_MAX_MID1_WIDTH))
             throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band width out of range"};
-        props->Equalizer.Mid1Width = val;
+        props.Mid1Width = val;
         break;
 
     case AL_EQUALIZER_MID2_GAIN:
         if(!(val >= AL_EQUALIZER_MIN_MID2_GAIN && val <= AL_EQUALIZER_MAX_MID2_GAIN))
             throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band gain out of range"};
-        props->Equalizer.Mid2Gain = val;
+        props.Mid2Gain = val;
         break;
 
     case AL_EQUALIZER_MID2_CENTER:
         if(!(val >= AL_EQUALIZER_MIN_MID2_CENTER && val <= AL_EQUALIZER_MAX_MID2_CENTER))
             throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band center out of range"};
-        props->Equalizer.Mid2Center = val;
+        props.Mid2Center = val;
         break;
 
     case AL_EQUALIZER_MID2_WIDTH:
         if(!(val >= AL_EQUALIZER_MIN_MID2_WIDTH && val <= AL_EQUALIZER_MAX_MID2_WIDTH))
             throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band width out of range"};
-        props->Equalizer.Mid2Width = val;
+        props.Mid2Width = val;
         break;
 
     case AL_EQUALIZER_HIGH_GAIN:
         if(!(val >= AL_EQUALIZER_MIN_HIGH_GAIN && val <= AL_EQUALIZER_MAX_HIGH_GAIN))
             throw effect_exception{AL_INVALID_VALUE, "Equalizer high-band gain out of range"};
-        props->Equalizer.HighGain = val;
+        props.HighGain = val;
         break;
 
     case AL_EQUALIZER_HIGH_CUTOFF:
         if(!(val >= AL_EQUALIZER_MIN_HIGH_CUTOFF && val <= AL_EQUALIZER_MAX_HIGH_CUTOFF))
             throw effect_exception{AL_INVALID_VALUE, "Equalizer high-band cutoff out of range"};
-        props->Equalizer.HighCutoff = val;
+        props.HighCutoff = val;
         break;
 
     default:
         throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param};
     }
 }
-void Equalizer_setParamfv(EffectProps *props, ALenum param, const float *vals)
-{ Equalizer_setParamf(props, param, vals[0]); }
+void EqualizerEffectHandler::SetParamfv(EqualizerProps &props, ALenum param, const float *vals)
+{ SetParamf(props, param, *vals); }
 
-void Equalizer_getParami(const EffectProps*, ALenum param, int*)
+void EqualizerEffectHandler::GetParami(const EqualizerProps&, ALenum param, int*)
 { throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; }
-void Equalizer_getParamiv(const EffectProps*, ALenum param, int*)
+void EqualizerEffectHandler::GetParamiv(const EqualizerProps&, ALenum param, int*)
 {
     throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x",
         param};
 }
-void Equalizer_getParamf(const EffectProps *props, ALenum param, float *val)
+void EqualizerEffectHandler::GetParamf(const EqualizerProps &props, ALenum param, float *val)
 {
     switch(param)
     {
-    case AL_EQUALIZER_LOW_GAIN:
-        *val = props->Equalizer.LowGain;
-        break;
-
-    case AL_EQUALIZER_LOW_CUTOFF:
-        *val = props->Equalizer.LowCutoff;
-        break;
-
-    case AL_EQUALIZER_MID1_GAIN:
-        *val = props->Equalizer.Mid1Gain;
-        break;
-
-    case AL_EQUALIZER_MID1_CENTER:
-        *val = props->Equalizer.Mid1Center;
-        break;
-
-    case AL_EQUALIZER_MID1_WIDTH:
-        *val = props->Equalizer.Mid1Width;
-        break;
-
-    case AL_EQUALIZER_MID2_GAIN:
-        *val = props->Equalizer.Mid2Gain;
-        break;
-
-    case AL_EQUALIZER_MID2_CENTER:
-        *val = props->Equalizer.Mid2Center;
-        break;
-
-    case AL_EQUALIZER_MID2_WIDTH:
-        *val = props->Equalizer.Mid2Width;
-        break;
-
-    case AL_EQUALIZER_HIGH_GAIN:
-        *val = props->Equalizer.HighGain;
-        break;
-
-    case AL_EQUALIZER_HIGH_CUTOFF:
-        *val = props->Equalizer.HighCutoff;
-        break;
+    case AL_EQUALIZER_LOW_GAIN: *val = props.LowGain; break;
+    case AL_EQUALIZER_LOW_CUTOFF: *val = props.LowCutoff; break;
+    case AL_EQUALIZER_MID1_GAIN: *val = props.Mid1Gain; break;
+    case AL_EQUALIZER_MID1_CENTER: *val = props.Mid1Center; break;
+    case AL_EQUALIZER_MID1_WIDTH: *val = props.Mid1Width; break;
+    case AL_EQUALIZER_MID2_GAIN: *val = props.Mid2Gain; break;
+    case AL_EQUALIZER_MID2_CENTER: *val = props.Mid2Center; break;
+    case AL_EQUALIZER_MID2_WIDTH: *val = props.Mid2Width; break;
+    case AL_EQUALIZER_HIGH_GAIN: *val = props.HighGain; break;
+    case AL_EQUALIZER_HIGH_CUTOFF: *val = props.HighCutoff; break;
 
     default:
         throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param};
     }
 }
-void Equalizer_getParamfv(const EffectProps *props, ALenum param, float *vals)
-{ Equalizer_getParamf(props, param, vals); }
+void EqualizerEffectHandler::GetParamfv(const EqualizerProps &props, ALenum param, float *vals)
+{ GetParamf(props, param, vals); }
 
-EffectProps genDefaultProps() noexcept
-{
-    EffectProps props{};
-    props.Equalizer.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF;
-    props.Equalizer.LowGain = AL_EQUALIZER_DEFAULT_LOW_GAIN;
-    props.Equalizer.Mid1Center = AL_EQUALIZER_DEFAULT_MID1_CENTER;
-    props.Equalizer.Mid1Gain = AL_EQUALIZER_DEFAULT_MID1_GAIN;
-    props.Equalizer.Mid1Width = AL_EQUALIZER_DEFAULT_MID1_WIDTH;
-    props.Equalizer.Mid2Center = AL_EQUALIZER_DEFAULT_MID2_CENTER;
-    props.Equalizer.Mid2Gain = AL_EQUALIZER_DEFAULT_MID2_GAIN;
-    props.Equalizer.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH;
-    props.Equalizer.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF;
-    props.Equalizer.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN;
-    return props;
-}
-
-} // namespace
-
-DEFINE_ALEFFECT_VTABLE(Equalizer);
-
-const EffectProps EqualizerEffectProps{genDefaultProps()};
 
 #ifdef ALSOFT_EAX
 namespace {
@@ -319,91 +290,86 @@ template<>
     throw Exception{message};
 }
 
-template<>
-bool EqualizerCommitter::commit(const EaxEffectProps &props)
+bool EaxEqualizerCommitter::commit(const EAXEQUALIZERPROPERTIES &props)
 {
-    if(props.mType == mEaxProps.mType && mEaxProps.mEqualizer.lLowGain == props.mEqualizer.lLowGain
-        && mEaxProps.mEqualizer.flLowCutOff == props.mEqualizer.flLowCutOff
-        && mEaxProps.mEqualizer.lMid1Gain == props.mEqualizer.lMid1Gain
-        && mEaxProps.mEqualizer.flMid1Center == props.mEqualizer.flMid1Center
-        && mEaxProps.mEqualizer.flMid1Width == props.mEqualizer.flMid1Width
-        && mEaxProps.mEqualizer.lMid2Gain == props.mEqualizer.lMid2Gain
-        && mEaxProps.mEqualizer.flMid2Center == props.mEqualizer.flMid2Center
-        && mEaxProps.mEqualizer.flMid2Width == props.mEqualizer.flMid2Width
-        && mEaxProps.mEqualizer.lHighGain == props.mEqualizer.lHighGain
-        && mEaxProps.mEqualizer.flHighCutOff == props.mEqualizer.flHighCutOff)
+    if(auto *cur = std::get_if<EAXEQUALIZERPROPERTIES>(&mEaxProps); cur && *cur == props)
         return false;
 
     mEaxProps = props;
-
-    mAlProps.Equalizer.LowGain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lLowGain));
-    mAlProps.Equalizer.LowCutoff = props.mEqualizer.flLowCutOff;
-    mAlProps.Equalizer.Mid1Gain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lMid1Gain));
-    mAlProps.Equalizer.Mid1Center = props.mEqualizer.flMid1Center;
-    mAlProps.Equalizer.Mid1Width = props.mEqualizer.flMid1Width;
-    mAlProps.Equalizer.Mid2Gain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lMid2Gain));
-    mAlProps.Equalizer.Mid2Center = props.mEqualizer.flMid2Center;
-    mAlProps.Equalizer.Mid2Width = props.mEqualizer.flMid2Width;
-    mAlProps.Equalizer.HighGain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lHighGain));
-    mAlProps.Equalizer.HighCutoff = props.mEqualizer.flHighCutOff;
+    mAlProps = [&]{
+        EqualizerProps ret{};
+        ret.LowGain = level_mb_to_gain(static_cast<float>(props.lLowGain));
+        ret.LowCutoff = props.flLowCutOff;
+        ret.Mid1Gain = level_mb_to_gain(static_cast<float>(props.lMid1Gain));
+        ret.Mid1Center = props.flMid1Center;
+        ret.Mid1Width = props.flMid1Width;
+        ret.Mid2Gain = level_mb_to_gain(static_cast<float>(props.lMid2Gain));
+        ret.Mid2Center = props.flMid2Center;
+        ret.Mid2Width = props.flMid2Width;
+        ret.HighGain = level_mb_to_gain(static_cast<float>(props.lHighGain));
+        ret.HighCutoff = props.flHighCutOff;
+        return ret;
+    }();
 
     return true;
 }
 
-template<>
-void EqualizerCommitter::SetDefaults(EaxEffectProps &props)
+void EaxEqualizerCommitter::SetDefaults(EaxEffectProps &props)
 {
-    props.mType = EaxEffectType::Equalizer;
-    props.mEqualizer.lLowGain = EAXEQUALIZER_DEFAULTLOWGAIN;
-    props.mEqualizer.flLowCutOff = EAXEQUALIZER_DEFAULTLOWCUTOFF;
-    props.mEqualizer.lMid1Gain = EAXEQUALIZER_DEFAULTMID1GAIN;
-    props.mEqualizer.flMid1Center = EAXEQUALIZER_DEFAULTMID1CENTER;
-    props.mEqualizer.flMid1Width = EAXEQUALIZER_DEFAULTMID1WIDTH;
-    props.mEqualizer.lMid2Gain = EAXEQUALIZER_DEFAULTMID2GAIN;
-    props.mEqualizer.flMid2Center = EAXEQUALIZER_DEFAULTMID2CENTER;
-    props.mEqualizer.flMid2Width = EAXEQUALIZER_DEFAULTMID2WIDTH;
-    props.mEqualizer.lHighGain = EAXEQUALIZER_DEFAULTHIGHGAIN;
-    props.mEqualizer.flHighCutOff = EAXEQUALIZER_DEFAULTHIGHCUTOFF;
+    static constexpr EAXEQUALIZERPROPERTIES defprops{[]
+    {
+        EAXEQUALIZERPROPERTIES ret{};
+        ret.lLowGain = EAXEQUALIZER_DEFAULTLOWGAIN;
+        ret.flLowCutOff = EAXEQUALIZER_DEFAULTLOWCUTOFF;
+        ret.lMid1Gain = EAXEQUALIZER_DEFAULTMID1GAIN;
+        ret.flMid1Center = EAXEQUALIZER_DEFAULTMID1CENTER;
+        ret.flMid1Width = EAXEQUALIZER_DEFAULTMID1WIDTH;
+        ret.lMid2Gain = EAXEQUALIZER_DEFAULTMID2GAIN;
+        ret.flMid2Center = EAXEQUALIZER_DEFAULTMID2CENTER;
+        ret.flMid2Width = EAXEQUALIZER_DEFAULTMID2WIDTH;
+        ret.lHighGain = EAXEQUALIZER_DEFAULTHIGHGAIN;
+        ret.flHighCutOff = EAXEQUALIZER_DEFAULTHIGHCUTOFF;
+        return ret;
+    }()};
+    props = defprops;
 }
 
-template<>
-void EqualizerCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+void EaxEqualizerCommitter::Get(const EaxCall &call, const EAXEQUALIZERPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXEQUALIZER_NONE: break;
-    case EAXEQUALIZER_ALLPARAMETERS: call.set_value<Exception>(props.mEqualizer); break;
-    case EAXEQUALIZER_LOWGAIN: call.set_value<Exception>(props.mEqualizer.lLowGain); break;
-    case EAXEQUALIZER_LOWCUTOFF: call.set_value<Exception>(props.mEqualizer.flLowCutOff); break;
-    case EAXEQUALIZER_MID1GAIN: call.set_value<Exception>(props.mEqualizer.lMid1Gain); break;
-    case EAXEQUALIZER_MID1CENTER: call.set_value<Exception>(props.mEqualizer.flMid1Center); break;
-    case EAXEQUALIZER_MID1WIDTH: call.set_value<Exception>(props.mEqualizer.flMid1Width); break;
-    case EAXEQUALIZER_MID2GAIN: call.set_value<Exception>(props.mEqualizer.lMid2Gain); break;
-    case EAXEQUALIZER_MID2CENTER: call.set_value<Exception>(props.mEqualizer.flMid2Center); break;
-    case EAXEQUALIZER_MID2WIDTH: call.set_value<Exception>(props.mEqualizer.flMid2Width); break;
-    case EAXEQUALIZER_HIGHGAIN: call.set_value<Exception>(props.mEqualizer.lHighGain); break;
-    case EAXEQUALIZER_HIGHCUTOFF: call.set_value<Exception>(props.mEqualizer.flHighCutOff); break;
+    case EAXEQUALIZER_ALLPARAMETERS: call.set_value<Exception>(props); break;
+    case EAXEQUALIZER_LOWGAIN: call.set_value<Exception>(props.lLowGain); break;
+    case EAXEQUALIZER_LOWCUTOFF: call.set_value<Exception>(props.flLowCutOff); break;
+    case EAXEQUALIZER_MID1GAIN: call.set_value<Exception>(props.lMid1Gain); break;
+    case EAXEQUALIZER_MID1CENTER: call.set_value<Exception>(props.flMid1Center); break;
+    case EAXEQUALIZER_MID1WIDTH: call.set_value<Exception>(props.flMid1Width); break;
+    case EAXEQUALIZER_MID2GAIN: call.set_value<Exception>(props.lMid2Gain); break;
+    case EAXEQUALIZER_MID2CENTER: call.set_value<Exception>(props.flMid2Center); break;
+    case EAXEQUALIZER_MID2WIDTH: call.set_value<Exception>(props.flMid2Width); break;
+    case EAXEQUALIZER_HIGHGAIN: call.set_value<Exception>(props.lHighGain); break;
+    case EAXEQUALIZER_HIGHCUTOFF: call.set_value<Exception>(props.flHighCutOff); break;
     default: fail_unknown_property_id();
     }
 }
 
-template<>
-void EqualizerCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+void EaxEqualizerCommitter::Set(const EaxCall &call, EAXEQUALIZERPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXEQUALIZER_NONE: break;
-    case EAXEQUALIZER_ALLPARAMETERS: defer<AllValidator>(call, props.mEqualizer); break;
-    case EAXEQUALIZER_LOWGAIN: defer<LowGainValidator>(call, props.mEqualizer.lLowGain); break;
-    case EAXEQUALIZER_LOWCUTOFF: defer<LowCutOffValidator>(call, props.mEqualizer.flLowCutOff); break;
-    case EAXEQUALIZER_MID1GAIN: defer<Mid1GainValidator>(call, props.mEqualizer.lMid1Gain); break;
-    case EAXEQUALIZER_MID1CENTER: defer<Mid1CenterValidator>(call, props.mEqualizer.flMid1Center); break;
-    case EAXEQUALIZER_MID1WIDTH: defer<Mid1WidthValidator>(call, props.mEqualizer.flMid1Width); break;
-    case EAXEQUALIZER_MID2GAIN: defer<Mid2GainValidator>(call, props.mEqualizer.lMid2Gain); break;
-    case EAXEQUALIZER_MID2CENTER: defer<Mid2CenterValidator>(call, props.mEqualizer.flMid2Center); break;
-    case EAXEQUALIZER_MID2WIDTH: defer<Mid2WidthValidator>(call, props.mEqualizer.flMid2Width); break;
-    case EAXEQUALIZER_HIGHGAIN: defer<HighGainValidator>(call, props.mEqualizer.lHighGain); break;
-    case EAXEQUALIZER_HIGHCUTOFF: defer<HighCutOffValidator>(call, props.mEqualizer.flHighCutOff); break;
+    case EAXEQUALIZER_ALLPARAMETERS: defer<AllValidator>(call, props); break;
+    case EAXEQUALIZER_LOWGAIN: defer<LowGainValidator>(call, props.lLowGain); break;
+    case EAXEQUALIZER_LOWCUTOFF: defer<LowCutOffValidator>(call, props.flLowCutOff); break;
+    case EAXEQUALIZER_MID1GAIN: defer<Mid1GainValidator>(call, props.lMid1Gain); break;
+    case EAXEQUALIZER_MID1CENTER: defer<Mid1CenterValidator>(call, props.flMid1Center); break;
+    case EAXEQUALIZER_MID1WIDTH: defer<Mid1WidthValidator>(call, props.flMid1Width); break;
+    case EAXEQUALIZER_MID2GAIN: defer<Mid2GainValidator>(call, props.lMid2Gain); break;
+    case EAXEQUALIZER_MID2CENTER: defer<Mid2CenterValidator>(call, props.flMid2Center); break;
+    case EAXEQUALIZER_MID2WIDTH: defer<Mid2WidthValidator>(call, props.flMid2Width); break;
+    case EAXEQUALIZER_HIGHGAIN: defer<HighGainValidator>(call, props.lHighGain); break;
+    case EAXEQUALIZER_HIGHCUTOFF: defer<HighCutOffValidator>(call, props.flHighCutOff); break;
     default: fail_unknown_property_id();
     }
 }

+ 77 - 74
Engine/lib/openal-soft/al/effects/fshifter.cpp

@@ -1,18 +1,19 @@
 
 #include "config.h"
 
+#include <optional>
 #include <stdexcept>
 
 #include "AL/al.h"
 #include "AL/efx.h"
 
 #include "alc/effects/base.h"
-#include "aloptional.h"
 #include "effects.h"
 
 #ifdef ALSOFT_EAX
 #include <cassert>
 #include "alnumeric.h"
+#include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #endif // ALSOFT_EAX
@@ -20,7 +21,7 @@
 
 namespace {
 
-al::optional<FShifterDirection> DirectionFromEmum(ALenum value)
+constexpr std::optional<FShifterDirection> DirectionFromEmum(ALenum value) noexcept
 {
     switch(value)
     {
@@ -28,9 +29,9 @@ al::optional<FShifterDirection> DirectionFromEmum(ALenum value)
     case AL_FREQUENCY_SHIFTER_DIRECTION_UP: return FShifterDirection::Up;
     case AL_FREQUENCY_SHIFTER_DIRECTION_OFF: return FShifterDirection::Off;
     }
-    return al::nullopt;
+    return std::nullopt;
 }
-ALenum EnumFromDirection(FShifterDirection dir)
+constexpr ALenum EnumFromDirection(FShifterDirection dir)
 {
     switch(dir)
     {
@@ -41,31 +42,26 @@ ALenum EnumFromDirection(FShifterDirection dir)
     throw std::runtime_error{"Invalid direction: "+std::to_string(static_cast<int>(dir))};
 }
 
-void Fshifter_setParamf(EffectProps *props, ALenum param, float val)
+constexpr EffectProps genDefaultProps() noexcept
 {
-    switch(param)
-    {
-    case AL_FREQUENCY_SHIFTER_FREQUENCY:
-        if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY))
-            throw effect_exception{AL_INVALID_VALUE, "Frequency shifter frequency out of range"};
-        props->Fshifter.Frequency = val;
-        break;
-
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x",
-            param};
-    }
+    FshifterProps props{};
+    props.Frequency      = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY;
+    props.LeftDirection  = DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION).value();
+    props.RightDirection = DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION).value();
+    return props;
 }
-void Fshifter_setParamfv(EffectProps *props, ALenum param, const float *vals)
-{ Fshifter_setParamf(props, param, vals[0]); }
 
-void Fshifter_setParami(EffectProps *props, ALenum param, int val)
+} // namespace
+
+const EffectProps FshifterEffectProps{genDefaultProps()};
+
+void FshifterEffectHandler::SetParami(FshifterProps &props, ALenum param, int val)
 {
     switch(param)
     {
     case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
         if(auto diropt = DirectionFromEmum(val))
-            props->Fshifter.LeftDirection = *diropt;
+            props.LeftDirection = *diropt;
         else
             throw effect_exception{AL_INVALID_VALUE,
                 "Unsupported frequency shifter left direction: 0x%04x", val};
@@ -73,7 +69,7 @@ void Fshifter_setParami(EffectProps *props, ALenum param, int val)
 
     case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
         if(auto diropt = DirectionFromEmum(val))
-            props->Fshifter.RightDirection = *diropt;
+            props.RightDirection = *diropt;
         else
             throw effect_exception{AL_INVALID_VALUE,
                 "Unsupported frequency shifter right direction: 0x%04x", val};
@@ -84,33 +80,52 @@ void Fshifter_setParami(EffectProps *props, ALenum param, int val)
             "Invalid frequency shifter integer property 0x%04x", param};
     }
 }
-void Fshifter_setParamiv(EffectProps *props, ALenum param, const int *vals)
-{ Fshifter_setParami(props, param, vals[0]); }
+void FshifterEffectHandler::SetParamiv(FshifterProps &props, ALenum param, const int *vals)
+{ SetParami(props, param, *vals); }
+
+void FshifterEffectHandler::SetParamf(FshifterProps &props, ALenum param, float val)
+{
+    switch(param)
+    {
+    case AL_FREQUENCY_SHIFTER_FREQUENCY:
+        if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY))
+            throw effect_exception{AL_INVALID_VALUE, "Frequency shifter frequency out of range"};
+        props.Frequency = val;
+        break;
+
+    default:
+        throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x",
+            param};
+    }
+}
+void FshifterEffectHandler::SetParamfv(FshifterProps &props, ALenum param, const float *vals)
+{ SetParamf(props, param, *vals); }
 
-void Fshifter_getParami(const EffectProps *props, ALenum param, int *val)
+void FshifterEffectHandler::GetParami(const FshifterProps &props, ALenum param, int *val)
 {
     switch(param)
     {
     case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
-        *val = EnumFromDirection(props->Fshifter.LeftDirection);
+        *val = EnumFromDirection(props.LeftDirection);
         break;
     case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
-        *val = EnumFromDirection(props->Fshifter.RightDirection);
+        *val = EnumFromDirection(props.RightDirection);
         break;
+
     default:
         throw effect_exception{AL_INVALID_ENUM,
             "Invalid frequency shifter integer property 0x%04x", param};
     }
 }
-void Fshifter_getParamiv(const EffectProps *props, ALenum param, int *vals)
-{ Fshifter_getParami(props, param, vals); }
+void FshifterEffectHandler::GetParamiv(const FshifterProps &props, ALenum param, int *vals)
+{ GetParami(props, param, vals); }
 
-void Fshifter_getParamf(const EffectProps *props, ALenum param, float *val)
+void FshifterEffectHandler::GetParamf(const FshifterProps &props, ALenum param, float *val)
 {
     switch(param)
     {
     case AL_FREQUENCY_SHIFTER_FREQUENCY:
-        *val = props->Fshifter.Frequency;
+        *val = props.Frequency;
         break;
 
     default:
@@ -118,23 +133,9 @@ void Fshifter_getParamf(const EffectProps *props, ALenum param, float *val)
             param};
     }
 }
-void Fshifter_getParamfv(const EffectProps *props, ALenum param, float *vals)
-{ Fshifter_getParamf(props, param, vals); }
-
-EffectProps genDefaultProps() noexcept
-{
-    EffectProps props{};
-    props.Fshifter.Frequency      = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY;
-    props.Fshifter.LeftDirection  = *DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION);
-    props.Fshifter.RightDirection = *DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION);
-    return props;
-}
-
-} // namespace
-
-DEFINE_ALEFFECT_VTABLE(Fshifter);
+void FshifterEffectHandler::GetParamfv(const FshifterProps &props, ALenum param, float *vals)
+{ GetParamf(props, param, vals); }
 
-const EffectProps FshifterEffectProps{genDefaultProps()};
 
 #ifdef ALSOFT_EAX
 namespace {
@@ -197,13 +198,9 @@ template<>
     throw Exception{message};
 }
 
-template<>
-bool FrequencyShifterCommitter::commit(const EaxEffectProps &props)
+bool EaxFrequencyShifterCommitter::commit(const EAXFREQUENCYSHIFTERPROPERTIES &props)
 {
-    if(props.mType == mEaxProps.mType
-        && mEaxProps.mFrequencyShifter.flFrequency == props.mFrequencyShifter.flFrequency
-        && mEaxProps.mFrequencyShifter.ulLeftDirection == props.mFrequencyShifter.ulLeftDirection
-        && mEaxProps.mFrequencyShifter.ulRightDirection == props.mFrequencyShifter.ulRightDirection)
+    if(auto *cur = std::get_if<EAXFREQUENCYSHIFTERPROPERTIES>(&mEaxProps); cur && *cur == props)
         return false;
 
     mEaxProps = props;
@@ -217,46 +214,52 @@ bool FrequencyShifterCommitter::commit(const EaxEffectProps &props)
         return FShifterDirection::Off;
     };
 
-    mAlProps.Fshifter.Frequency = props.mFrequencyShifter.flFrequency;
-    mAlProps.Fshifter.LeftDirection = get_direction(props.mFrequencyShifter.ulLeftDirection);
-    mAlProps.Fshifter.RightDirection = get_direction(props.mFrequencyShifter.ulRightDirection);
+    mAlProps = [&]{
+        FshifterProps ret{};
+        ret.Frequency = props.flFrequency;
+        ret.LeftDirection = get_direction(props.ulLeftDirection);
+        ret.RightDirection = get_direction(props.ulRightDirection);
+        return ret;
+    }();
 
     return true;
 }
 
-template<>
-void FrequencyShifterCommitter::SetDefaults(EaxEffectProps &props)
+void EaxFrequencyShifterCommitter::SetDefaults(EaxEffectProps &props)
 {
-    props.mType = EaxEffectType::FrequencyShifter;
-    props.mFrequencyShifter.flFrequency = EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY;
-    props.mFrequencyShifter.ulLeftDirection = EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION;
-    props.mFrequencyShifter.ulRightDirection = EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION;
+    static constexpr EAXFREQUENCYSHIFTERPROPERTIES defprops{[]
+    {
+        EAXFREQUENCYSHIFTERPROPERTIES ret{};
+        ret.flFrequency = EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY;
+        ret.ulLeftDirection = EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION;
+        ret.ulRightDirection = EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION;
+        return ret;
+    }()};
+    props = defprops;
 }
 
-template<>
-void FrequencyShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+void EaxFrequencyShifterCommitter::Get(const EaxCall &call, const EAXFREQUENCYSHIFTERPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXFREQUENCYSHIFTER_NONE: break;
-    case EAXFREQUENCYSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props.mFrequencyShifter); break;
-    case EAXFREQUENCYSHIFTER_FREQUENCY: call.set_value<Exception>(props.mFrequencyShifter.flFrequency); break;
-    case EAXFREQUENCYSHIFTER_LEFTDIRECTION: call.set_value<Exception>(props.mFrequencyShifter.ulLeftDirection); break;
-    case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: call.set_value<Exception>(props.mFrequencyShifter.ulRightDirection); break;
+    case EAXFREQUENCYSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props); break;
+    case EAXFREQUENCYSHIFTER_FREQUENCY: call.set_value<Exception>(props.flFrequency); break;
+    case EAXFREQUENCYSHIFTER_LEFTDIRECTION: call.set_value<Exception>(props.ulLeftDirection); break;
+    case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: call.set_value<Exception>(props.ulRightDirection); break;
     default: fail_unknown_property_id();
     }
 }
 
-template<>
-void FrequencyShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+void EaxFrequencyShifterCommitter::Set(const EaxCall &call, EAXFREQUENCYSHIFTERPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXFREQUENCYSHIFTER_NONE: break;
-    case EAXFREQUENCYSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props.mFrequencyShifter); break;
-    case EAXFREQUENCYSHIFTER_FREQUENCY: defer<FrequencyValidator>(call, props.mFrequencyShifter.flFrequency); break;
-    case EAXFREQUENCYSHIFTER_LEFTDIRECTION: defer<LeftDirectionValidator>(call, props.mFrequencyShifter.ulLeftDirection); break;
-    case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: defer<RightDirectionValidator>(call, props.mFrequencyShifter.ulRightDirection); break;
+    case EAXFREQUENCYSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props); break;
+    case EAXFREQUENCYSHIFTER_FREQUENCY: defer<FrequencyValidator>(call, props.flFrequency); break;
+    case EAXFREQUENCYSHIFTER_LEFTDIRECTION: defer<LeftDirectionValidator>(call, props.ulLeftDirection); break;
+    case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: defer<RightDirectionValidator>(call, props.ulRightDirection); break;
     default: fail_unknown_property_id();
     }
 }

+ 80 - 87
Engine/lib/openal-soft/al/effects/modulator.cpp

@@ -1,18 +1,19 @@
 
 #include "config.h"
 
+#include <optional>
 #include <stdexcept>
 
 #include "AL/al.h"
 #include "AL/efx.h"
 
 #include "alc/effects/base.h"
-#include "aloptional.h"
 #include "effects.h"
 
 #ifdef ALSOFT_EAX
 #include <cassert>
 #include "alnumeric.h"
+#include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #endif // ALSOFT_EAX
@@ -20,7 +21,7 @@
 
 namespace {
 
-al::optional<ModulatorWaveform> WaveformFromEmum(ALenum value)
+constexpr std::optional<ModulatorWaveform> WaveformFromEmum(ALenum value) noexcept
 {
     switch(value)
     {
@@ -28,9 +29,9 @@ al::optional<ModulatorWaveform> WaveformFromEmum(ALenum value)
     case AL_RING_MODULATOR_SAWTOOTH: return ModulatorWaveform::Sawtooth;
     case AL_RING_MODULATOR_SQUARE: return ModulatorWaveform::Square;
     }
-    return al::nullopt;
+    return std::nullopt;
 }
-ALenum EnumFromWaveform(ModulatorWaveform type)
+constexpr ALenum EnumFromWaveform(ModulatorWaveform type)
 {
     switch(type)
     {
@@ -42,40 +43,31 @@ ALenum EnumFromWaveform(ModulatorWaveform type)
         std::to_string(static_cast<int>(type))};
 }
 
-void Modulator_setParamf(EffectProps *props, ALenum param, float val)
+constexpr EffectProps genDefaultProps() noexcept
 {
-    switch(param)
-    {
-    case AL_RING_MODULATOR_FREQUENCY:
-        if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY))
-            throw effect_exception{AL_INVALID_VALUE, "Modulator frequency out of range: %f", val};
-        props->Modulator.Frequency = val;
-        break;
+    ModulatorProps props{};
+    props.Frequency      = AL_RING_MODULATOR_DEFAULT_FREQUENCY;
+    props.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF;
+    props.Waveform       = WaveformFromEmum(AL_RING_MODULATOR_DEFAULT_WAVEFORM).value();
+    return props;
+}
 
-    case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
-        if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF))
-            throw effect_exception{AL_INVALID_VALUE, "Modulator high-pass cutoff out of range: %f", val};
-        props->Modulator.HighPassCutoff = val;
-        break;
+} // namespace
 
-    default:
-        throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param};
-    }
-}
-void Modulator_setParamfv(EffectProps *props, ALenum param, const float *vals)
-{ Modulator_setParamf(props, param, vals[0]); }
-void Modulator_setParami(EffectProps *props, ALenum param, int val)
+const EffectProps ModulatorEffectProps{genDefaultProps()};
+
+void ModulatorEffectHandler::SetParami(ModulatorProps &props, ALenum param, int val)
 {
     switch(param)
     {
     case AL_RING_MODULATOR_FREQUENCY:
     case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
-        Modulator_setParamf(props, param, static_cast<float>(val));
+        SetParamf(props, param, static_cast<float>(val));
         break;
 
     case AL_RING_MODULATOR_WAVEFORM:
         if(auto formopt = WaveformFromEmum(val))
-            props->Modulator.Waveform = *formopt;
+            props.Waveform = *formopt;
         else
             throw effect_exception{AL_INVALID_VALUE, "Invalid modulator waveform: 0x%04x", val};
         break;
@@ -85,62 +77,61 @@ void Modulator_setParami(EffectProps *props, ALenum param, int val)
             param};
     }
 }
-void Modulator_setParamiv(EffectProps *props, ALenum param, const int *vals)
-{ Modulator_setParami(props, param, vals[0]); }
+void ModulatorEffectHandler::SetParamiv(ModulatorProps &props, ALenum param, const int *vals)
+{ SetParami(props, param, *vals); }
 
-void Modulator_getParami(const EffectProps *props, ALenum param, int *val)
+void ModulatorEffectHandler::SetParamf(ModulatorProps &props, ALenum param, float val)
 {
     switch(param)
     {
     case AL_RING_MODULATOR_FREQUENCY:
-        *val = static_cast<int>(props->Modulator.Frequency);
+        if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY))
+            throw effect_exception{AL_INVALID_VALUE, "Modulator frequency out of range: %f", val};
+        props.Frequency = val;
         break;
+
     case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
-        *val = static_cast<int>(props->Modulator.HighPassCutoff);
-        break;
-    case AL_RING_MODULATOR_WAVEFORM:
-        *val = EnumFromWaveform(props->Modulator.Waveform);
+        if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF))
+            throw effect_exception{AL_INVALID_VALUE, "Modulator high-pass cutoff out of range: %f", val};
+        props.HighPassCutoff = val;
         break;
 
+    default:
+        throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param};
+    }
+}
+void ModulatorEffectHandler::SetParamfv(ModulatorProps &props, ALenum param, const float *vals)
+{ SetParamf(props, param, *vals); }
+
+void ModulatorEffectHandler::GetParami(const ModulatorProps &props, ALenum param, int *val)
+{
+    switch(param)
+    {
+    case AL_RING_MODULATOR_FREQUENCY: *val = static_cast<int>(props.Frequency); break;
+    case AL_RING_MODULATOR_HIGHPASS_CUTOFF: *val = static_cast<int>(props.HighPassCutoff); break;
+    case AL_RING_MODULATOR_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break;
+
     default:
         throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x",
             param};
     }
 }
-void Modulator_getParamiv(const EffectProps *props, ALenum param, int *vals)
-{ Modulator_getParami(props, param, vals); }
-void Modulator_getParamf(const EffectProps *props, ALenum param, float *val)
+void ModulatorEffectHandler::GetParamiv(const ModulatorProps &props, ALenum param, int *vals)
+{ GetParami(props, param, vals); }
+void ModulatorEffectHandler::GetParamf(const ModulatorProps &props, ALenum param, float *val)
 {
     switch(param)
     {
-    case AL_RING_MODULATOR_FREQUENCY:
-        *val = props->Modulator.Frequency;
-        break;
-    case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
-        *val = props->Modulator.HighPassCutoff;
-        break;
+    case AL_RING_MODULATOR_FREQUENCY: *val = props.Frequency; break;
+    case AL_RING_MODULATOR_HIGHPASS_CUTOFF: *val = props.HighPassCutoff; break;
 
     default:
         throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param};
     }
 }
-void Modulator_getParamfv(const EffectProps *props, ALenum param, float *vals)
-{ Modulator_getParamf(props, param, vals); }
-
-EffectProps genDefaultProps() noexcept
-{
-    EffectProps props{};
-    props.Modulator.Frequency      = AL_RING_MODULATOR_DEFAULT_FREQUENCY;
-    props.Modulator.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF;
-    props.Modulator.Waveform       = *WaveformFromEmum(AL_RING_MODULATOR_DEFAULT_WAVEFORM);
-    return props;
-}
+void ModulatorEffectHandler::GetParamfv(const ModulatorProps &props, ALenum param, float *vals)
+{ GetParamf(props, param, vals); }
 
-} // namespace
-
-DEFINE_ALEFFECT_VTABLE(Modulator);
-
-const EffectProps ModulatorEffectProps{genDefaultProps()};
 
 #ifdef ALSOFT_EAX
 namespace {
@@ -203,13 +194,9 @@ template<>
     throw Exception{message};
 }
 
-template<>
-bool ModulatorCommitter::commit(const EaxEffectProps &props)
+bool EaxModulatorCommitter::commit(const EAXRINGMODULATORPROPERTIES &props)
 {
-    if(props.mType == mEaxProps.mType
-        && mEaxProps.mModulator.flFrequency == props.mModulator.flFrequency
-        && mEaxProps.mModulator.flHighPassCutOff == props.mModulator.flHighPassCutOff
-        && mEaxProps.mModulator.ulWaveform == props.mModulator.ulWaveform)
+    if(auto *cur = std::get_if<EAXRINGMODULATORPROPERTIES>(&mEaxProps); cur && *cur == props)
         return false;
 
     mEaxProps = props;
@@ -225,46 +212,52 @@ bool ModulatorCommitter::commit(const EaxEffectProps &props)
         return ModulatorWaveform::Sinusoid;
     };
 
-    mAlProps.Modulator.Frequency = props.mModulator.flFrequency;
-    mAlProps.Modulator.HighPassCutoff = props.mModulator.flHighPassCutOff;
-    mAlProps.Modulator.Waveform = get_waveform(props.mModulator.ulWaveform);
+    mAlProps = [&]{
+        ModulatorProps ret{};
+        ret.Frequency = props.flFrequency;
+        ret.HighPassCutoff = props.flHighPassCutOff;
+        ret.Waveform = get_waveform(props.ulWaveform);
+        return ret;
+    }();
 
     return true;
 }
 
-template<>
-void ModulatorCommitter::SetDefaults(EaxEffectProps &props)
+void EaxModulatorCommitter::SetDefaults(EaxEffectProps &props)
 {
-    props.mType = EaxEffectType::Modulator;
-    props.mModulator.flFrequency = EAXRINGMODULATOR_DEFAULTFREQUENCY;
-    props.mModulator.flHighPassCutOff = EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF;
-    props.mModulator.ulWaveform = EAXRINGMODULATOR_DEFAULTWAVEFORM;
+    static constexpr EAXRINGMODULATORPROPERTIES defprops{[]
+    {
+        EAXRINGMODULATORPROPERTIES ret{};
+        ret.flFrequency = EAXRINGMODULATOR_DEFAULTFREQUENCY;
+        ret.flHighPassCutOff = EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF;
+        ret.ulWaveform = EAXRINGMODULATOR_DEFAULTWAVEFORM;
+        return ret;
+    }()};
+    props = defprops;
 }
 
-template<>
-void ModulatorCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+void EaxModulatorCommitter::Get(const EaxCall &call, const EAXRINGMODULATORPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXRINGMODULATOR_NONE: break;
-    case EAXRINGMODULATOR_ALLPARAMETERS: call.set_value<Exception>(props.mModulator); break;
-    case EAXRINGMODULATOR_FREQUENCY: call.set_value<Exception>(props.mModulator.flFrequency); break;
-    case EAXRINGMODULATOR_HIGHPASSCUTOFF: call.set_value<Exception>(props.mModulator.flHighPassCutOff); break;
-    case EAXRINGMODULATOR_WAVEFORM: call.set_value<Exception>(props.mModulator.ulWaveform); break;
+    case EAXRINGMODULATOR_ALLPARAMETERS: call.set_value<Exception>(props); break;
+    case EAXRINGMODULATOR_FREQUENCY: call.set_value<Exception>(props.flFrequency); break;
+    case EAXRINGMODULATOR_HIGHPASSCUTOFF: call.set_value<Exception>(props.flHighPassCutOff); break;
+    case EAXRINGMODULATOR_WAVEFORM: call.set_value<Exception>(props.ulWaveform); break;
     default: fail_unknown_property_id();
     }
 }
 
-template<>
-void ModulatorCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+void EaxModulatorCommitter::Set(const EaxCall &call, EAXRINGMODULATORPROPERTIES &props)
 {
-    switch (call.get_property_id())
+    switch(call.get_property_id())
     {
     case EAXRINGMODULATOR_NONE: break;
-    case EAXRINGMODULATOR_ALLPARAMETERS: defer<AllValidator>(call, props.mModulator); break;
-    case EAXRINGMODULATOR_FREQUENCY: defer<FrequencyValidator>(call, props.mModulator.flFrequency); break;
-    case EAXRINGMODULATOR_HIGHPASSCUTOFF: defer<HighPassCutOffValidator>(call, props.mModulator.flHighPassCutOff); break;
-    case EAXRINGMODULATOR_WAVEFORM: defer<WaveformValidator>(call, props.mModulator.ulWaveform); break;
+    case EAXRINGMODULATOR_ALLPARAMETERS: defer<AllValidator>(call, props); break;
+    case EAXRINGMODULATOR_FREQUENCY: defer<FrequencyValidator>(call, props.flFrequency); break;
+    case EAXRINGMODULATOR_HIGHPASSCUTOFF: defer<HighPassCutOffValidator>(call, props.flHighPassCutOff); break;
+    case EAXRINGMODULATOR_WAVEFORM: defer<WaveformValidator>(call, props.ulWaveform); break;
     default: fail_unknown_property_id();
     }
 }

+ 29 - 35
Engine/lib/openal-soft/al/effects/null.cpp

@@ -8,13 +8,23 @@
 #include "effects.h"
 
 #ifdef ALSOFT_EAX
+#include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #endif // ALSOFT_EAX
 
 
 namespace {
 
-void Null_setParami(EffectProps* /*props*/, ALenum param, int /*val*/)
+constexpr EffectProps genDefaultProps() noexcept
+{
+    return std::monostate{};
+}
+
+} // namespace
+
+const EffectProps NullEffectProps{genDefaultProps()};
+
+void NullEffectHandler::SetParami(std::monostate& /*props*/, ALenum param, int /*val*/)
 {
     switch(param)
     {
@@ -23,15 +33,15 @@ void Null_setParami(EffectProps* /*props*/, ALenum param, int /*val*/)
             param};
     }
 }
-void Null_setParamiv(EffectProps *props, ALenum param, const int *vals)
+void NullEffectHandler::SetParamiv(std::monostate &props, ALenum param, const int *vals)
 {
     switch(param)
     {
     default:
-        Null_setParami(props, param, vals[0]);
+        SetParami(props, param, *vals);
     }
 }
-void Null_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/)
+void NullEffectHandler::SetParamf(std::monostate& /*props*/, ALenum param, float /*val*/)
 {
     switch(param)
     {
@@ -40,16 +50,16 @@ void Null_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/)
             param};
     }
 }
-void Null_setParamfv(EffectProps *props, ALenum param, const float *vals)
+void NullEffectHandler::SetParamfv(std::monostate &props, ALenum param, const float *vals)
 {
     switch(param)
     {
     default:
-        Null_setParamf(props, param, vals[0]);
+        SetParamf(props, param, *vals);
     }
 }
 
-void Null_getParami(const EffectProps* /*props*/, ALenum param, int* /*val*/)
+void NullEffectHandler::GetParami(const std::monostate& /*props*/, ALenum param, int* /*val*/)
 {
     switch(param)
     {
@@ -58,15 +68,15 @@ void Null_getParami(const EffectProps* /*props*/, ALenum param, int* /*val*/)
             param};
     }
 }
-void Null_getParamiv(const EffectProps *props, ALenum param, int *vals)
+void NullEffectHandler::GetParamiv(const std::monostate &props, ALenum param, int *vals)
 {
     switch(param)
     {
     default:
-        Null_getParami(props, param, vals);
+        GetParami(props, param, vals);
     }
 }
-void Null_getParamf(const EffectProps* /*props*/, ALenum param, float* /*val*/)
+void NullEffectHandler::GetParamf(const std::monostate& /*props*/, ALenum param, float* /*val*/)
 {
     switch(param)
     {
@@ -75,27 +85,15 @@ void Null_getParamf(const EffectProps* /*props*/, ALenum param, float* /*val*/)
             param};
     }
 }
-void Null_getParamfv(const EffectProps *props, ALenum param, float *vals)
+void NullEffectHandler::GetParamfv(const std::monostate &props, ALenum param, float *vals)
 {
     switch(param)
     {
     default:
-        Null_getParamf(props, param, vals);
+        GetParamf(props, param, vals);
     }
 }
 
-EffectProps genDefaultProps() noexcept
-{
-    EffectProps props{};
-    return props;
-}
-
-} // namespace
-
-DEFINE_ALEFFECT_VTABLE(Null);
-
-const EffectProps NullEffectProps{genDefaultProps()};
-
 
 #ifdef ALSOFT_EAX
 namespace {
@@ -117,30 +115,26 @@ template<>
     throw Exception{message};
 }
 
-template<>
-bool NullCommitter::commit(const EaxEffectProps &props)
+bool EaxNullCommitter::commit(const std::monostate &props)
 {
-    const bool ret{props.mType != mEaxProps.mType};
+    const bool ret{std::holds_alternative<std::monostate>(mEaxProps)};
     mEaxProps = props;
+    mAlProps = std::monostate{};
     return ret;
 }
 
-template<>
-void NullCommitter::SetDefaults(EaxEffectProps &props)
+void EaxNullCommitter::SetDefaults(EaxEffectProps &props)
 {
-    props = EaxEffectProps{};
-    props.mType = EaxEffectType::None;
+    props = std::monostate{};
 }
 
-template<>
-void NullCommitter::Get(const EaxCall &call, const EaxEffectProps&)
+void EaxNullCommitter::Get(const EaxCall &call, const std::monostate&)
 {
     if(call.get_property_id() != 0)
         fail_unknown_property_id();
 }
 
-template<>
-void NullCommitter::Set(const EaxCall &call, EaxEffectProps&)
+void EaxNullCommitter::Set(const EaxCall &call, std::monostate&)
 {
     if(call.get_property_id() != 0)
         fail_unknown_property_id();

+ 49 - 57
Engine/lib/openal-soft/al/effects/pshifter.cpp

@@ -9,6 +9,7 @@
 
 #ifdef ALSOFT_EAX
 #include "alnumeric.h"
+#include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #endif // ALSOFT_EAX
@@ -16,28 +17,32 @@
 
 namespace {
 
-void Pshifter_setParamf(EffectProps*, ALenum param, float)
-{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; }
-void Pshifter_setParamfv(EffectProps*, ALenum param, const float*)
+constexpr EffectProps genDefaultProps() noexcept
 {
-    throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x",
-        param};
+    PshifterProps props{};
+    props.CoarseTune = AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE;
+    props.FineTune = AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE;
+    return props;
 }
 
-void Pshifter_setParami(EffectProps *props, ALenum param, int val)
+} // namespace
+
+const EffectProps PshifterEffectProps{genDefaultProps()};
+
+void PshifterEffectHandler::SetParami(PshifterProps &props, ALenum param, int val)
 {
     switch(param)
     {
     case AL_PITCH_SHIFTER_COARSE_TUNE:
         if(!(val >= AL_PITCH_SHIFTER_MIN_COARSE_TUNE && val <= AL_PITCH_SHIFTER_MAX_COARSE_TUNE))
             throw effect_exception{AL_INVALID_VALUE, "Pitch shifter coarse tune out of range"};
-        props->Pshifter.CoarseTune = val;
+        props.CoarseTune = val;
         break;
 
     case AL_PITCH_SHIFTER_FINE_TUNE:
         if(!(val >= AL_PITCH_SHIFTER_MIN_FINE_TUNE && val <= AL_PITCH_SHIFTER_MAX_FINE_TUNE))
             throw effect_exception{AL_INVALID_VALUE, "Pitch shifter fine tune out of range"};
-        props->Pshifter.FineTune = val;
+        props.FineTune = val;
         break;
 
     default:
@@ -45,49 +50,40 @@ void Pshifter_setParami(EffectProps *props, ALenum param, int val)
             param};
     }
 }
-void Pshifter_setParamiv(EffectProps *props, ALenum param, const int *vals)
-{ Pshifter_setParami(props, param, vals[0]); }
+void PshifterEffectHandler::SetParamiv(PshifterProps &props, ALenum param, const int *vals)
+{ SetParami(props, param, *vals); }
 
-void Pshifter_getParami(const EffectProps *props, ALenum param, int *val)
+void PshifterEffectHandler::SetParamf(PshifterProps&, ALenum param, float)
+{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; }
+void PshifterEffectHandler::SetParamfv(PshifterProps&, ALenum param, const float*)
+{
+    throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x",
+        param};
+}
+
+void PshifterEffectHandler::GetParami(const PshifterProps &props, ALenum param, int *val)
 {
     switch(param)
     {
-    case AL_PITCH_SHIFTER_COARSE_TUNE:
-        *val = props->Pshifter.CoarseTune;
-        break;
-    case AL_PITCH_SHIFTER_FINE_TUNE:
-        *val = props->Pshifter.FineTune;
-        break;
+    case AL_PITCH_SHIFTER_COARSE_TUNE: *val = props.CoarseTune; break;
+    case AL_PITCH_SHIFTER_FINE_TUNE: *val = props.FineTune; break;
 
     default:
         throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x",
             param};
     }
 }
-void Pshifter_getParamiv(const EffectProps *props, ALenum param, int *vals)
-{ Pshifter_getParami(props, param, vals); }
+void PshifterEffectHandler::GetParamiv(const PshifterProps &props, ALenum param, int *vals)
+{ GetParami(props, param, vals); }
 
-void Pshifter_getParamf(const EffectProps*, ALenum param, float*)
+void PshifterEffectHandler::GetParamf(const PshifterProps&, ALenum param, float*)
 { throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; }
-void Pshifter_getParamfv(const EffectProps*, ALenum param, float*)
+void PshifterEffectHandler::GetParamfv(const PshifterProps&, ALenum param, float*)
 {
     throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float vector-property 0x%04x",
         param};
 }
 
-EffectProps genDefaultProps() noexcept
-{
-    EffectProps props{};
-    props.Pshifter.CoarseTune = AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE;
-    props.Pshifter.FineTune   = AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE;
-    return props;
-}
-
-} // namespace
-
-DEFINE_ALEFFECT_VTABLE(Pshifter);
-
-const EffectProps PshifterEffectProps{genDefaultProps()};
 
 #ifdef ALSOFT_EAX
 namespace {
@@ -138,52 +134,48 @@ template<>
     throw Exception{message};
 }
 
-template<>
-bool PitchShifterCommitter::commit(const EaxEffectProps &props)
+bool EaxPitchShifterCommitter::commit(const EAXPITCHSHIFTERPROPERTIES &props)
 {
-    if(props.mType == mEaxProps.mType
-        && mEaxProps.mPitchShifter.lCoarseTune == props.mPitchShifter.lCoarseTune
-        && mEaxProps.mPitchShifter.lFineTune == props.mPitchShifter.lFineTune)
+    if(auto *cur = std::get_if<EAXPITCHSHIFTERPROPERTIES>(&mEaxProps); cur && *cur == props)
         return false;
 
     mEaxProps = props;
-
-    mAlProps.Pshifter.CoarseTune = static_cast<int>(mEaxProps.mPitchShifter.lCoarseTune);
-    mAlProps.Pshifter.FineTune = static_cast<int>(mEaxProps.mPitchShifter.lFineTune);
+    mAlProps = [&]{
+        PshifterProps ret{};
+        ret.CoarseTune = static_cast<int>(props.lCoarseTune);
+        ret.FineTune = static_cast<int>(props.lFineTune);
+        return ret;
+    }();
 
     return true;
 }
 
-template<>
-void PitchShifterCommitter::SetDefaults(EaxEffectProps &props)
+void EaxPitchShifterCommitter::SetDefaults(EaxEffectProps &props)
 {
-    props.mType = EaxEffectType::PitchShifter;
-    props.mPitchShifter.lCoarseTune = EAXPITCHSHIFTER_DEFAULTCOARSETUNE;
-    props.mPitchShifter.lFineTune = EAXPITCHSHIFTER_DEFAULTFINETUNE;
+    props = EAXPITCHSHIFTERPROPERTIES{EAXPITCHSHIFTER_DEFAULTCOARSETUNE,
+        EAXPITCHSHIFTER_DEFAULTFINETUNE};
 }
 
-template<>
-void PitchShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+void EaxPitchShifterCommitter::Get(const EaxCall &call, const EAXPITCHSHIFTERPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXPITCHSHIFTER_NONE: break;
-    case EAXPITCHSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props.mPitchShifter); break;
-    case EAXPITCHSHIFTER_COARSETUNE: call.set_value<Exception>(props.mPitchShifter.lCoarseTune); break;
-    case EAXPITCHSHIFTER_FINETUNE: call.set_value<Exception>(props.mPitchShifter.lFineTune); break;
+    case EAXPITCHSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props); break;
+    case EAXPITCHSHIFTER_COARSETUNE: call.set_value<Exception>(props.lCoarseTune); break;
+    case EAXPITCHSHIFTER_FINETUNE: call.set_value<Exception>(props.lFineTune); break;
     default: fail_unknown_property_id();
     }
 }
 
-template<>
-void PitchShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+void EaxPitchShifterCommitter::Set(const EaxCall &call, EAXPITCHSHIFTERPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
     case EAXPITCHSHIFTER_NONE: break;
-    case EAXPITCHSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props.mPitchShifter); break;
-    case EAXPITCHSHIFTER_COARSETUNE: defer<CoarseTuneValidator>(call, props.mPitchShifter.lCoarseTune); break;
-    case EAXPITCHSHIFTER_FINETUNE: defer<FineTuneValidator>(call, props.mPitchShifter.lFineTune); break;
+    case EAXPITCHSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props); break;
+    case EAXPITCHSHIFTER_COARSETUNE: defer<CoarseTuneValidator>(call, props.lCoarseTune); break;
+    case EAXPITCHSHIFTER_FINETUNE: defer<FineTuneValidator>(call, props.lFineTune); break;
     default: fail_unknown_property_id();
     }
 }

File diff suppressed because it is too large
+ 267 - 391
Engine/lib/openal-soft/al/effects/reverb.cpp


+ 92 - 156
Engine/lib/openal-soft/al/effects/vmorpher.cpp

@@ -1,18 +1,18 @@
 
 #include "config.h"
 
+#include <optional>
 #include <stdexcept>
 
 #include "AL/al.h"
 #include "AL/efx.h"
 
-#include "alc/effects/base.h"
-#include "aloptional.h"
+#include "core/effects/base.h"
 #include "effects.h"
 
 #ifdef ALSOFT_EAX
 #include <cassert>
-#include "alnumeric.h"
+#include "al/eax/effect.h"
 #include "al/eax/exception.h"
 #include "al/eax/utils.h"
 #endif // ALSOFT_EAX
@@ -20,7 +20,7 @@
 
 namespace {
 
-al::optional<VMorpherPhenome> PhenomeFromEnum(ALenum val)
+constexpr std::optional<VMorpherPhenome> PhenomeFromEnum(ALenum val) noexcept
 {
 #define HANDLE_PHENOME(x) case AL_VOCAL_MORPHER_PHONEME_ ## x:                \
     return VMorpherPhenome::x
@@ -57,10 +57,10 @@ al::optional<VMorpherPhenome> PhenomeFromEnum(ALenum val)
     HANDLE_PHENOME(V);
     HANDLE_PHENOME(Z);
     }
-    return al::nullopt;
+    return std::nullopt;
 #undef HANDLE_PHENOME
 }
-ALenum EnumFromPhenome(VMorpherPhenome phenome)
+constexpr ALenum EnumFromPhenome(VMorpherPhenome phenome)
 {
 #define HANDLE_PHENOME(x) case VMorpherPhenome::x: return AL_VOCAL_MORPHER_PHONEME_ ## x
     switch(phenome)
@@ -100,7 +100,7 @@ ALenum EnumFromPhenome(VMorpherPhenome phenome)
 #undef HANDLE_PHENOME
 }
 
-al::optional<VMorpherWaveform> WaveformFromEmum(ALenum value)
+constexpr std::optional<VMorpherWaveform> WaveformFromEmum(ALenum value) noexcept
 {
     switch(value)
     {
@@ -108,9 +108,9 @@ al::optional<VMorpherWaveform> WaveformFromEmum(ALenum value)
     case AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE: return VMorpherWaveform::Triangle;
     case AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH: return VMorpherWaveform::Sawtooth;
     }
-    return al::nullopt;
+    return std::nullopt;
 }
-ALenum EnumFromWaveform(VMorpherWaveform type)
+constexpr ALenum EnumFromWaveform(VMorpherWaveform type)
 {
     switch(type)
     {
@@ -122,13 +122,29 @@ ALenum EnumFromWaveform(VMorpherWaveform type)
         std::to_string(static_cast<int>(type))};
 }
 
-void Vmorpher_setParami(EffectProps *props, ALenum param, int val)
+constexpr EffectProps genDefaultProps() noexcept
+{
+    VmorpherProps props{};
+    props.Rate                 = AL_VOCAL_MORPHER_DEFAULT_RATE;
+    props.PhonemeA             = PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEA).value();
+    props.PhonemeB             = PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEB).value();
+    props.PhonemeACoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING;
+    props.PhonemeBCoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING;
+    props.Waveform             = WaveformFromEmum(AL_VOCAL_MORPHER_DEFAULT_WAVEFORM).value();
+    return props;
+}
+
+} // namespace
+
+const EffectProps VmorpherEffectProps{genDefaultProps()};
+
+void VmorpherEffectHandler::SetParami(VmorpherProps &props, ALenum param, int val)
 {
     switch(param)
     {
     case AL_VOCAL_MORPHER_PHONEMEA:
         if(auto phenomeopt = PhenomeFromEnum(val))
-            props->Vmorpher.PhonemeA = *phenomeopt;
+            props.PhonemeA = *phenomeopt;
         else
             throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a out of range: 0x%04x", val};
         break;
@@ -136,12 +152,12 @@ void Vmorpher_setParami(EffectProps *props, ALenum param, int val)
     case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING:
         if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING))
             throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a coarse tuning out of range"};
-        props->Vmorpher.PhonemeACoarseTuning = val;
+        props.PhonemeACoarseTuning = val;
         break;
 
     case AL_VOCAL_MORPHER_PHONEMEB:
         if(auto phenomeopt = PhenomeFromEnum(val))
-            props->Vmorpher.PhonemeB = *phenomeopt;
+            props.PhonemeB = *phenomeopt;
         else
             throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b out of range: 0x%04x", val};
         break;
@@ -149,12 +165,12 @@ void Vmorpher_setParami(EffectProps *props, ALenum param, int val)
     case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING:
         if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING))
             throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b coarse tuning out of range"};
-        props->Vmorpher.PhonemeBCoarseTuning = val;
+        props.PhonemeBCoarseTuning = val;
         break;
 
     case AL_VOCAL_MORPHER_WAVEFORM:
         if(auto formopt = WaveformFromEmum(val))
-            props->Vmorpher.Waveform = *formopt;
+            props.Waveform = *formopt;
         else
             throw effect_exception{AL_INVALID_VALUE, "Vocal morpher waveform out of range: 0x%04x", val};
         break;
@@ -164,19 +180,19 @@ void Vmorpher_setParami(EffectProps *props, ALenum param, int val)
             param};
     }
 }
-void Vmorpher_setParamiv(EffectProps*, ALenum param, const int*)
+void VmorpherEffectHandler::SetParamiv(VmorpherProps&, ALenum param, const int*)
 {
     throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x",
         param};
 }
-void Vmorpher_setParamf(EffectProps *props, ALenum param, float val)
+void VmorpherEffectHandler::SetParamf(VmorpherProps &props, ALenum param, float val)
 {
     switch(param)
     {
     case AL_VOCAL_MORPHER_RATE:
         if(!(val >= AL_VOCAL_MORPHER_MIN_RATE && val <= AL_VOCAL_MORPHER_MAX_RATE))
             throw effect_exception{AL_INVALID_VALUE, "Vocal morpher rate out of range"};
-        props->Vmorpher.Rate = val;
+        props.Rate = val;
         break;
 
     default:
@@ -184,49 +200,35 @@ void Vmorpher_setParamf(EffectProps *props, ALenum param, float val)
             param};
     }
 }
-void Vmorpher_setParamfv(EffectProps *props, ALenum param, const float *vals)
-{ Vmorpher_setParamf(props, param, vals[0]); }
+void VmorpherEffectHandler::SetParamfv(VmorpherProps &props, ALenum param, const float *vals)
+{ SetParamf(props, param, *vals); }
 
-void Vmorpher_getParami(const EffectProps *props, ALenum param, int* val)
+void VmorpherEffectHandler::GetParami(const VmorpherProps &props, ALenum param, int* val)
 {
     switch(param)
     {
-    case AL_VOCAL_MORPHER_PHONEMEA:
-        *val = EnumFromPhenome(props->Vmorpher.PhonemeA);
-        break;
-
-    case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING:
-        *val = props->Vmorpher.PhonemeACoarseTuning;
-        break;
-
-    case AL_VOCAL_MORPHER_PHONEMEB:
-        *val = EnumFromPhenome(props->Vmorpher.PhonemeB);
-        break;
-
-    case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING:
-        *val = props->Vmorpher.PhonemeBCoarseTuning;
-        break;
-
-    case AL_VOCAL_MORPHER_WAVEFORM:
-        *val = EnumFromWaveform(props->Vmorpher.Waveform);
-        break;
+    case AL_VOCAL_MORPHER_PHONEMEA: *val = EnumFromPhenome(props.PhonemeA); break;
+    case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING: *val = props.PhonemeACoarseTuning; break;
+    case AL_VOCAL_MORPHER_PHONEMEB: *val = EnumFromPhenome(props.PhonemeB); break;
+    case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING: *val = props.PhonemeBCoarseTuning; break;
+    case AL_VOCAL_MORPHER_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break;
 
     default:
         throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer property 0x%04x",
             param};
     }
 }
-void Vmorpher_getParamiv(const EffectProps*, ALenum param, int*)
+void VmorpherEffectHandler::GetParamiv(const VmorpherProps&, ALenum param, int*)
 {
     throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x",
         param};
 }
-void Vmorpher_getParamf(const EffectProps *props, ALenum param, float *val)
+void VmorpherEffectHandler::GetParamf(const VmorpherProps &props, ALenum param, float *val)
 {
     switch(param)
     {
     case AL_VOCAL_MORPHER_RATE:
-        *val = props->Vmorpher.Rate;
+        *val = props.Rate;
         break;
 
     default:
@@ -234,26 +236,9 @@ void Vmorpher_getParamf(const EffectProps *props, ALenum param, float *val)
             param};
     }
 }
-void Vmorpher_getParamfv(const EffectProps *props, ALenum param, float *vals)
-{ Vmorpher_getParamf(props, param, vals); }
+void VmorpherEffectHandler::GetParamfv(const VmorpherProps &props, ALenum param, float *vals)
+{ GetParamf(props, param, vals); }
 
-EffectProps genDefaultProps() noexcept
-{
-    EffectProps props{};
-    props.Vmorpher.Rate                 = AL_VOCAL_MORPHER_DEFAULT_RATE;
-    props.Vmorpher.PhonemeA             = *PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEA);
-    props.Vmorpher.PhonemeB             = *PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEB);
-    props.Vmorpher.PhonemeACoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING;
-    props.Vmorpher.PhonemeBCoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING;
-    props.Vmorpher.Waveform             = *WaveformFromEmum(AL_VOCAL_MORPHER_DEFAULT_WAVEFORM);
-    return props;
-}
-
-} // namespace
-
-DEFINE_ALEFFECT_VTABLE(Vmorpher);
-
-const EffectProps VmorpherEffectProps{genDefaultProps()};
 
 #ifdef ALSOFT_EAX
 namespace {
@@ -352,16 +337,9 @@ template<>
     throw Exception{message};
 }
 
-template<>
-bool VocalMorpherCommitter::commit(const EaxEffectProps &props)
+bool EaxVocalMorpherCommitter::commit(const EAXVOCALMORPHERPROPERTIES &props)
 {
-    if(props.mType == mEaxProps.mType
-        && mEaxProps.mVocalMorpher.ulPhonemeA == props.mVocalMorpher.ulPhonemeA
-        && mEaxProps.mVocalMorpher.lPhonemeACoarseTuning == props.mVocalMorpher.lPhonemeACoarseTuning
-        && mEaxProps.mVocalMorpher.ulPhonemeB == props.mVocalMorpher.ulPhonemeB
-        && mEaxProps.mVocalMorpher.lPhonemeBCoarseTuning == props.mVocalMorpher.lPhonemeBCoarseTuning
-        && mEaxProps.mVocalMorpher.ulWaveform == props.mVocalMorpher.ulWaveform
-        && mEaxProps.mVocalMorpher.flRate == props.mVocalMorpher.flRate)
+    if(auto *cur = std::get_if<EAXVOCALMORPHERPROPERTIES>(&mEaxProps); cur && *cur == props)
         return false;
 
     mEaxProps = props;
@@ -413,107 +391,65 @@ bool VocalMorpherCommitter::commit(const EaxEffectProps &props)
         return VMorpherWaveform::Sinusoid;
     };
 
-    mAlProps.Vmorpher.PhonemeA = get_phoneme(props.mVocalMorpher.ulPhonemeA);
-    mAlProps.Vmorpher.PhonemeACoarseTuning = static_cast<int>(props.mVocalMorpher.lPhonemeACoarseTuning);
-    mAlProps.Vmorpher.PhonemeB = get_phoneme(props.mVocalMorpher.ulPhonemeB);
-    mAlProps.Vmorpher.PhonemeBCoarseTuning = static_cast<int>(props.mVocalMorpher.lPhonemeBCoarseTuning);
-    mAlProps.Vmorpher.Waveform = get_waveform(props.mVocalMorpher.ulWaveform);
-    mAlProps.Vmorpher.Rate = props.mVocalMorpher.flRate;
+    mAlProps = [&]{
+        VmorpherProps ret{};
+        ret.PhonemeA = get_phoneme(props.ulPhonemeA);
+        ret.PhonemeACoarseTuning = static_cast<int>(props.lPhonemeACoarseTuning);
+        ret.PhonemeB = get_phoneme(props.ulPhonemeB);
+        ret.PhonemeBCoarseTuning = static_cast<int>(props.lPhonemeBCoarseTuning);
+        ret.Waveform = get_waveform(props.ulWaveform);
+        ret.Rate = props.flRate;
+        return ret;
+    }();
 
     return true;
 }
 
-template<>
-void VocalMorpherCommitter::SetDefaults(EaxEffectProps &props)
+void EaxVocalMorpherCommitter::SetDefaults(EaxEffectProps &props)
 {
-    props.mType = EaxEffectType::VocalMorpher;
-    props.mVocalMorpher.ulPhonemeA = EAXVOCALMORPHER_DEFAULTPHONEMEA;
-    props.mVocalMorpher.lPhonemeACoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING;
-    props.mVocalMorpher.ulPhonemeB = EAXVOCALMORPHER_DEFAULTPHONEMEB;
-    props.mVocalMorpher.lPhonemeBCoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING;
-    props.mVocalMorpher.ulWaveform = EAXVOCALMORPHER_DEFAULTWAVEFORM;
-    props.mVocalMorpher.flRate = EAXVOCALMORPHER_DEFAULTRATE;
+    static constexpr EAXVOCALMORPHERPROPERTIES defprops{[]
+    {
+        EAXVOCALMORPHERPROPERTIES ret{};
+        ret.ulPhonemeA = EAXVOCALMORPHER_DEFAULTPHONEMEA;
+        ret.lPhonemeACoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING;
+        ret.ulPhonemeB = EAXVOCALMORPHER_DEFAULTPHONEMEB;
+        ret.lPhonemeBCoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING;
+        ret.ulWaveform = EAXVOCALMORPHER_DEFAULTWAVEFORM;
+        ret.flRate = EAXVOCALMORPHER_DEFAULTRATE;
+        return ret;
+    }()};
+    props = defprops;
 }
 
-template<>
-void VocalMorpherCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+void EaxVocalMorpherCommitter::Get(const EaxCall &call, const EAXVOCALMORPHERPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
-    case EAXVOCALMORPHER_NONE:
-        break;
-
-    case EAXVOCALMORPHER_ALLPARAMETERS:
-        call.set_value<Exception>(props.mVocalMorpher);
-        break;
-
-    case EAXVOCALMORPHER_PHONEMEA:
-        call.set_value<Exception>(props.mVocalMorpher.ulPhonemeA);
-        break;
-
-    case EAXVOCALMORPHER_PHONEMEACOARSETUNING:
-        call.set_value<Exception>(props.mVocalMorpher.lPhonemeACoarseTuning);
-        break;
-
-    case EAXVOCALMORPHER_PHONEMEB:
-        call.set_value<Exception>(props.mVocalMorpher.ulPhonemeB);
-        break;
-
-    case EAXVOCALMORPHER_PHONEMEBCOARSETUNING:
-        call.set_value<Exception>(props.mVocalMorpher.lPhonemeBCoarseTuning);
-        break;
-
-    case EAXVOCALMORPHER_WAVEFORM:
-        call.set_value<Exception>(props.mVocalMorpher.ulWaveform);
-        break;
-
-    case EAXVOCALMORPHER_RATE:
-        call.set_value<Exception>(props.mVocalMorpher.flRate);
-        break;
-
-    default:
-        fail_unknown_property_id();
+    case EAXVOCALMORPHER_NONE: break;
+    case EAXVOCALMORPHER_ALLPARAMETERS: call.set_value<Exception>(props); break;
+    case EAXVOCALMORPHER_PHONEMEA: call.set_value<Exception>(props.ulPhonemeA); break;
+    case EAXVOCALMORPHER_PHONEMEACOARSETUNING: call.set_value<Exception>(props.lPhonemeACoarseTuning); break;
+    case EAXVOCALMORPHER_PHONEMEB: call.set_value<Exception>(props.ulPhonemeB); break;
+    case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: call.set_value<Exception>(props.lPhonemeBCoarseTuning); break;
+    case EAXVOCALMORPHER_WAVEFORM: call.set_value<Exception>(props.ulWaveform); break;
+    case EAXVOCALMORPHER_RATE: call.set_value<Exception>(props.flRate); break;
+    default: fail_unknown_property_id();
     }
 }
 
-template<>
-void VocalMorpherCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+void EaxVocalMorpherCommitter::Set(const EaxCall &call, EAXVOCALMORPHERPROPERTIES &props)
 {
     switch(call.get_property_id())
     {
-    case EAXVOCALMORPHER_NONE:
-        break;
-
-    case EAXVOCALMORPHER_ALLPARAMETERS:
-        defer<AllValidator>(call, props.mVocalMorpher);
-        break;
-
-    case EAXVOCALMORPHER_PHONEMEA:
-        defer<PhonemeAValidator>(call, props.mVocalMorpher.ulPhonemeA);
-        break;
-
-    case EAXVOCALMORPHER_PHONEMEACOARSETUNING:
-        defer<PhonemeACoarseTuningValidator>(call, props.mVocalMorpher.lPhonemeACoarseTuning);
-        break;
-
-    case EAXVOCALMORPHER_PHONEMEB:
-        defer<PhonemeBValidator>(call, props.mVocalMorpher.ulPhonemeB);
-        break;
-
-    case EAXVOCALMORPHER_PHONEMEBCOARSETUNING:
-        defer<PhonemeBCoarseTuningValidator>(call, props.mVocalMorpher.lPhonemeBCoarseTuning);
-        break;
-
-    case EAXVOCALMORPHER_WAVEFORM:
-        defer<WaveformValidator>(call, props.mVocalMorpher.ulWaveform);
-        break;
-
-    case EAXVOCALMORPHER_RATE:
-        defer<RateValidator>(call, props.mVocalMorpher.flRate);
-        break;
-
-    default:
-        fail_unknown_property_id();
+    case EAXVOCALMORPHER_NONE: break;
+    case EAXVOCALMORPHER_ALLPARAMETERS: defer<AllValidator>(call, props); break;
+    case EAXVOCALMORPHER_PHONEMEA: defer<PhonemeAValidator>(call, props.ulPhonemeA); break;
+    case EAXVOCALMORPHER_PHONEMEACOARSETUNING: defer<PhonemeACoarseTuningValidator>(call, props.lPhonemeACoarseTuning); break;
+    case EAXVOCALMORPHER_PHONEMEB: defer<PhonemeBValidator>(call, props.ulPhonemeB); break;
+    case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: defer<PhonemeBCoarseTuningValidator>(call, props.lPhonemeBCoarseTuning); break;
+    case EAXVOCALMORPHER_WAVEFORM: defer<WaveformValidator>(call, props.ulWaveform); break;
+    case EAXVOCALMORPHER_RATE: defer<RateValidator>(call, props.flRate); break;
+    default: fail_unknown_property_id();
     }
 }
 

+ 76 - 25
Engine/lib/openal-soft/al/error.cpp

@@ -20,6 +20,8 @@
 
 #include "config.h"
 
+#include "error.h"
+
 #ifdef _WIN32
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
@@ -29,27 +31,45 @@
 #include <csignal>
 #include <cstdarg>
 #include <cstdio>
+#include <cstdlib>
 #include <cstring>
-#include <mutex>
+#include <limits>
+#include <optional>
+#include <string>
+#include <utility>
+#include <vector>
 
 #include "AL/al.h"
 #include "AL/alc.h"
 
+#include "al/debug.h"
+#include "alc/alconfig.h"
 #include "alc/context.h"
-#include "almalloc.h"
-#include "core/except.h"
+#include "alc/inprogext.h"
 #include "core/logging.h"
 #include "opthelpers.h"
-#include "vector.h"
+#include "strutils.h"
 
 
-bool TrapALError{false};
+namespace al {
+context_error::context_error(ALenum code, const char *msg, ...) : mErrorCode{code}
+{
+    /* NOLINTBEGIN(*-array-to-pointer-decay) */
+    std::va_list args;
+    va_start(args, msg);
+    setMessage(msg, args);
+    va_end(args);
+    /* NOLINTEND(*-array-to-pointer-decay) */
+}
+context_error::~context_error() = default;
+} /* namespace al */
 
 void ALCcontext::setError(ALenum errorCode, const char *msg, ...)
 {
-    auto message = al::vector<char>(256);
+    auto message = std::vector<char>(256);
 
-    va_list args, args2;
+    /* NOLINTBEGIN(*-array-to-pointer-decay) */
+    std::va_list args, args2;
     va_start(args, msg);
     va_copy(args2, args);
     int msglen{std::vsnprintf(message.data(), message.size(), msg, args)};
@@ -60,9 +80,15 @@ void ALCcontext::setError(ALenum errorCode, const char *msg, ...)
     }
     va_end(args2);
     va_end(args);
+    /* NOLINTEND(*-array-to-pointer-decay) */
 
-    if(msglen >= 0) msg = message.data();
-    else msg = "<internal error constructing message>";
+    if(msglen >= 0)
+        msg = message.data();
+    else
+    {
+        msg = "<internal error constructing message>";
+        msglen = static_cast<int>(strlen(msg));
+    }
 
     WARN("Error generated on context %p, code 0x%04x, \"%s\"\n",
         decltype(std::declval<void*>()){this}, errorCode, msg);
@@ -77,30 +103,55 @@ void ALCcontext::setError(ALenum errorCode, const char *msg, ...)
 #endif
     }
 
-    ALenum curerr{AL_NO_ERROR};
-    mLastError.compare_exchange_strong(curerr, errorCode);
+    if(mLastThreadError.get() == AL_NO_ERROR)
+        mLastThreadError.set(errorCode);
+
+    debugMessage(DebugSource::API, DebugType::Error, 0, DebugSeverity::High,
+        {msg, static_cast<uint>(msglen)});
 }
 
-AL_API ALenum AL_APIENTRY alGetError(void)
-START_API_FUNC
+/* Special-case alGetError since it (potentially) raises a debug signal and
+ * returns a non-default value for a null context.
+ */
+AL_API auto AL_APIENTRY alGetError() noexcept -> ALenum
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY
+    if(auto context = GetContextRef()) LIKELY
+        return alGetErrorDirect(context.get());
+
+    auto get_value = [](const char *envname, const char *optname) -> ALenum
     {
-        static constexpr ALenum deferror{AL_INVALID_OPERATION};
-        WARN("Querying error state on null context (implicitly 0x%04x)\n", deferror);
-        if(TrapALError)
+        auto optstr = al::getenv(envname);
+        if(!optstr)
+            optstr = ConfigValueStr({}, "game_compat", optname);
+        if(optstr)
         {
+            char *end{};
+            auto value = std::strtoul(optstr->c_str(), &end, 0);
+            if(end && *end == '\0' && value <= std::numeric_limits<ALenum>::max())
+                return static_cast<ALenum>(value);
+            ERR("Invalid default error value: \"%s\"", optstr->c_str());
+        }
+        return AL_INVALID_OPERATION;
+    };
+    static const ALenum deferror{get_value("__ALSOFT_DEFAULT_ERROR", "default-error")};
+
+    WARN("Querying error state on null context (implicitly 0x%04x)\n", deferror);
+    if(TrapALError)
+    {
 #ifdef _WIN32
-            if(IsDebuggerPresent())
-                DebugBreak();
+        if(IsDebuggerPresent())
+            DebugBreak();
 #elif defined(SIGTRAP)
-            raise(SIGTRAP);
+        raise(SIGTRAP);
 #endif
-        }
-        return deferror;
     }
+    return deferror;
+}
 
-    return context->mLastError.exchange(AL_NO_ERROR);
+FORCE_ALIGN ALenum AL_APIENTRY alGetErrorDirect(ALCcontext *context) noexcept
+{
+    ALenum ret{context->mLastThreadError.get()};
+    if(ret != AL_NO_ERROR) UNLIKELY
+        context->mLastThreadError.set(AL_NO_ERROR);
+    return ret;
 }
-END_API_FUNC

+ 27 - 0
Engine/lib/openal-soft/al/error.h

@@ -0,0 +1,27 @@
+#ifndef AL_ERROR_H
+#define AL_ERROR_H
+
+#include "AL/al.h"
+
+#include "core/except.h"
+
+namespace al {
+
+class context_error final : public al::base_exception {
+    ALenum mErrorCode{};
+
+public:
+#ifdef __MINGW32__
+    [[gnu::format(__MINGW_PRINTF_FORMAT, 3, 4)]]
+#else
+    [[gnu::format(printf, 3, 4)]]
+#endif
+    context_error(ALenum code, const char *msg, ...);
+    ~context_error() final;
+
+    [[nodiscard]] auto errorCode() const noexcept -> ALenum { return mErrorCode; }
+};
+
+} /* namespace al */
+
+#endif /* AL_ERROR_H */

+ 114 - 89
Engine/lib/openal-soft/al/event.cpp

@@ -3,35 +3,49 @@
 
 #include "event.h"
 
-#include <algorithm>
+#include <array>
 #include <atomic>
-#include <cstring>
+#include <bitset>
 #include <exception>
 #include <memory>
 #include <mutex>
 #include <new>
+#include <optional>
 #include <string>
+#include <string_view>
 #include <thread>
+#include <tuple>
 #include <utility>
+#include <variant>
 
 #include "AL/al.h"
 #include "AL/alc.h"
+#include "AL/alext.h"
 
-#include "albyte.h"
 #include "alc/context.h"
-#include "alc/effects/base.h"
-#include "alc/inprogext.h"
-#include "almalloc.h"
+#include "alsem.h"
+#include "alspan.h"
 #include "core/async_event.h"
-#include "core/except.h"
+#include "core/context.h"
+#include "core/effects/base.h"
 #include "core/logging.h"
-#include "core/voice_change.h"
+#include "debug.h"
+#include "direct_defs.h"
+#include "error.h"
+#include "intrusive_ptr.h"
 #include "opthelpers.h"
 #include "ringbuffer.h"
-#include "threads.h"
 
 
-static int EventThread(ALCcontext *context)
+namespace {
+
+template<typename... Ts>
+struct overloaded : Ts... { using Ts::operator()...; };
+
+template<typename... Ts>
+overloaded(Ts...) -> overloaded<Ts...>;
+
+int EventThread(ALCcontext *context)
 {
     RingBuffer *ring{context->mAsyncEvents.get()};
     bool quitnow{false};
@@ -44,76 +58,98 @@ static int EventThread(ALCcontext *context)
             continue;
         }
 
-        std::lock_guard<std::mutex> _{context->mEventCbLock};
-        do {
-            auto *evt_ptr = reinterpret_cast<AsyncEvent*>(evt_data.buf);
-            evt_data.buf += sizeof(AsyncEvent);
-            evt_data.len -= 1;
-
-            AsyncEvent evt{*evt_ptr};
-            al::destroy_at(evt_ptr);
-            ring->readAdvance(1);
-
-            quitnow = evt.EnumType == AsyncEvent::KillThread;
+        std::lock_guard<std::mutex> eventlock{context->mEventCbLock};
+        auto evt_span = al::span{std::launder(reinterpret_cast<AsyncEvent*>(evt_data.buf)),
+            evt_data.len};
+        for(auto &event : evt_span)
+        {
+            quitnow = std::holds_alternative<AsyncKillThread>(event);
             if(quitnow) UNLIKELY break;
 
-            if(evt.EnumType == AsyncEvent::ReleaseEffectState)
-            {
-                al::intrusive_ptr<EffectState>{evt.u.mEffectState};
-                continue;
-            }
-
             auto enabledevts = context->mEnabledEvts.load(std::memory_order_acquire);
-            if(!context->mEventCb || !enabledevts.test(evt.EnumType))
-                continue;
-
-            if(evt.EnumType == AsyncEvent::SourceStateChange)
+            auto proc_killthread = [](AsyncKillThread&) { };
+            auto proc_release = [](AsyncEffectReleaseEvent &evt)
+            {
+                al::intrusive_ptr<EffectState>{evt.mEffectState};
+            };
+            auto proc_srcstate = [context,enabledevts](AsyncSourceStateEvent &evt)
             {
+                if(!context->mEventCb
+                    || !enabledevts.test(al::to_underlying(AsyncEnableBits::SourceState)))
+                    return;
+
                 ALuint state{};
-                std::string msg{"Source ID " + std::to_string(evt.u.srcstate.id)};
+                std::string msg{"Source ID " + std::to_string(evt.mId)};
                 msg += " state has changed to ";
-                switch(evt.u.srcstate.state)
+                switch(evt.mState)
                 {
-                case AsyncEvent::SrcState::Reset:
+                case AsyncSrcState::Reset:
                     msg += "AL_INITIAL";
                     state = AL_INITIAL;
                     break;
-                case AsyncEvent::SrcState::Stop:
+                case AsyncSrcState::Stop:
                     msg += "AL_STOPPED";
                     state = AL_STOPPED;
                     break;
-                case AsyncEvent::SrcState::Play:
+                case AsyncSrcState::Play:
                     msg += "AL_PLAYING";
                     state = AL_PLAYING;
                     break;
-                case AsyncEvent::SrcState::Pause:
+                case AsyncSrcState::Pause:
                     msg += "AL_PAUSED";
                     state = AL_PAUSED;
                     break;
                 }
-                context->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.u.srcstate.id,
-                    state, static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
-            }
-            else if(evt.EnumType == AsyncEvent::BufferCompleted)
+                context->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.mId, state,
+                    static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
+            };
+            auto proc_buffercomp = [context,enabledevts](AsyncBufferCompleteEvent &evt)
             {
-                std::string msg{std::to_string(evt.u.bufcomp.count)};
-                if(evt.u.bufcomp.count == 1) msg += " buffer completed";
+                if(!context->mEventCb
+                    || !enabledevts.test(al::to_underlying(AsyncEnableBits::BufferCompleted)))
+                    return;
+
+                std::string msg{std::to_string(evt.mCount)};
+                if(evt.mCount == 1) msg += " buffer completed";
                 else msg += " buffers completed";
-                context->mEventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, evt.u.bufcomp.id,
-                    evt.u.bufcomp.count, static_cast<ALsizei>(msg.length()), msg.c_str(),
-                    context->mEventParam);
-            }
-            else if(evt.EnumType == AsyncEvent::Disconnected)
+                context->mEventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, evt.mId, evt.mCount,
+                    static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
+            };
+            auto proc_disconnect = [context,enabledevts](AsyncDisconnectEvent &evt)
             {
-                context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0,
-                    static_cast<ALsizei>(strlen(evt.u.disconnect.msg)), evt.u.disconnect.msg,
-                    context->mEventParam);
-            }
-        } while(evt_data.len != 0);
+                context->debugMessage(DebugSource::System, DebugType::Error, 0,
+                    DebugSeverity::High, evt.msg);
+
+                if(context->mEventCb
+                    && enabledevts.test(al::to_underlying(AsyncEnableBits::Disconnected)))
+                    context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0,
+                        static_cast<ALsizei>(evt.msg.length()), evt.msg.c_str(),
+                        context->mEventParam);
+            };
+
+            std::visit(overloaded{proc_srcstate, proc_buffercomp, proc_release, proc_disconnect,
+                proc_killthread}, event);
+        }
+        std::destroy(evt_span.begin(), evt_span.end());
+        ring->readAdvance(evt_span.size());
     }
     return 0;
 }
 
+constexpr std::optional<AsyncEnableBits> GetEventType(ALenum etype) noexcept
+{
+    switch(etype)
+    {
+    case AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT: return AsyncEnableBits::BufferCompleted;
+    case AL_EVENT_TYPE_DISCONNECTED_SOFT: return AsyncEnableBits::Disconnected;
+    case AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT: return AsyncEnableBits::SourceState;
+    }
+    return std::nullopt;
+}
+
+} // namespace
+
+
 void StartEventThrd(ALCcontext *ctx)
 {
     try {
@@ -138,7 +174,7 @@ void StopEventThrd(ALCcontext *ctx)
             evt_data = ring->getWriteVector().first;
         } while(evt_data.len == 0);
     }
-    al::construct_at(reinterpret_cast<AsyncEvent*>(evt_data.buf), AsyncEvent::KillThread);
+    std::ignore = InitAsyncEvent<AsyncKillThread>(evt_data.buf);
     ring->writeAdvance(1);
 
     ctx->mEventSem.post();
@@ -146,34 +182,25 @@ void StopEventThrd(ALCcontext *ctx)
         ctx->mEventThread.join();
 }
 
-AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+AL_API DECL_FUNCEXT3(void, alEventControl,SOFT, ALsizei,count, const ALenum*,types, ALboolean,enable)
+FORCE_ALIGN void AL_APIENTRY alEventControlDirectSOFT(ALCcontext *context, ALsizei count,
+    const ALenum *types, ALboolean enable) noexcept
+try {
+    if(count < 0)
+        throw al::context_error{AL_INVALID_VALUE, "Controlling %d events", count};
+    if(count <= 0) UNLIKELY return;
 
-    if(count < 0) context->setError(AL_INVALID_VALUE, "Controlling %d events", count);
-    if(count <= 0) return;
-    if(!types) return context->setError(AL_INVALID_VALUE, "NULL pointer");
+    if(!types)
+        throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
 
     ContextBase::AsyncEventBitset flags{};
-    const ALenum *types_end = types+count;
-    auto bad_type = std::find_if_not(types, types_end,
-        [&flags](ALenum type) noexcept -> bool
-        {
-            if(type == AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT)
-                flags.set(AsyncEvent::BufferCompleted);
-            else if(type == AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT)
-                flags.set(AsyncEvent::SourceStateChange);
-            else if(type == AL_EVENT_TYPE_DISCONNECTED_SOFT)
-                flags.set(AsyncEvent::Disconnected);
-            else
-                return false;
-            return true;
-        }
-    );
-    if(bad_type != types_end)
-        return context->setError(AL_INVALID_ENUM, "Invalid event type 0x%04x", *bad_type);
+    for(ALenum evttype : al::span{types, static_cast<uint>(count)})
+    {
+        auto etype = GetEventType(evttype);
+        if(!etype)
+            throw al::context_error{AL_INVALID_ENUM, "Invalid event type 0x%04x", evttype};
+        flags.set(al::to_underlying(*etype));
+    }
 
     if(enable)
     {
@@ -196,20 +223,18 @@ START_API_FUNC
         /* Wait to ensure the event handler sees the changed flags before
          * returning.
          */
-        std::lock_guard<std::mutex> _{context->mEventCbLock};
+        std::lock_guard<std::mutex> eventlock{context->mEventCbLock};
     }
 }
-END_API_FUNC
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
+}
 
-AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam)
-START_API_FUNC
+AL_API DECL_FUNCEXT2(void, alEventCallback,SOFT, ALEVENTPROCSOFT,callback, void*,userParam)
+FORCE_ALIGN void AL_APIENTRY alEventCallbackDirectSOFT(ALCcontext *context,
+    ALEVENTPROCSOFT callback, void *userParam) noexcept
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    std::lock_guard<std::mutex> __{context->mEventCbLock};
+    std::lock_guard<std::mutex> eventlock{context->mEventCbLock};
     context->mEventCb = callback;
     context->mEventParam = userParam;
 }
-END_API_FUNC

+ 24 - 28
Engine/lib/openal-soft/al/extension.cpp

@@ -20,63 +20,59 @@
 
 #include "config.h"
 
-#include <cctype>
-#include <cstdlib>
-#include <cstring>
+#include <string_view>
+#include <vector>
 
 #include "AL/al.h"
 #include "AL/alc.h"
 
 #include "alc/context.h"
+#include "alc/inprogext.h"
 #include "alstring.h"
-#include "core/except.h"
+#include "direct_defs.h"
 #include "opthelpers.h"
 
 
-AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extName)
-START_API_FUNC
+AL_API DECL_FUNC1(ALboolean, alIsExtensionPresent, const ALchar*,extName)
+FORCE_ALIGN ALboolean AL_APIENTRY alIsExtensionPresentDirect(ALCcontext *context, const ALchar *extName) noexcept
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return AL_FALSE;
-
     if(!extName) UNLIKELY
     {
         context->setError(AL_INVALID_VALUE, "NULL pointer");
         return AL_FALSE;
     }
 
-    size_t len{strlen(extName)};
-    const char *ptr{context->mExtensionList};
-    while(ptr && *ptr)
+    const std::string_view tofind{extName};
+    for(std::string_view ext : context->mExtensions)
     {
-        if(al::strncasecmp(ptr, extName, len) == 0 && (ptr[len] == '\0' || isspace(ptr[len])))
+        if(al::case_compare(ext, tofind) == 0)
             return AL_TRUE;
-
-        if((ptr=strchr(ptr, ' ')) != nullptr)
-        {
-            do {
-                ++ptr;
-            } while(isspace(*ptr));
-        }
     }
 
     return AL_FALSE;
 }
-END_API_FUNC
 
 
-AL_API ALvoid* AL_APIENTRY alGetProcAddress(const ALchar *funcName)
-START_API_FUNC
+AL_API ALvoid* AL_APIENTRY alGetProcAddress(const ALchar *funcName) noexcept
 {
     if(!funcName) return nullptr;
     return alcGetProcAddress(nullptr, funcName);
 }
-END_API_FUNC
 
-AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *enumName)
-START_API_FUNC
+FORCE_ALIGN ALvoid* AL_APIENTRY alGetProcAddressDirect(ALCcontext*, const ALchar *funcName) noexcept
+{
+    if(!funcName) return nullptr;
+    return alcGetProcAddress(nullptr, funcName);
+}
+
+AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *enumName) noexcept
+{
+    if(!enumName) return ALenum{0};
+    return alcGetEnumValue(nullptr, enumName);
+}
+
+FORCE_ALIGN ALenum AL_APIENTRY alGetEnumValueDirect(ALCcontext*, const ALchar *enumName) noexcept
 {
-    if(!enumName) return static_cast<ALenum>(0);
+    if(!enumName) return ALenum{0};
     return alcGetEnumValue(nullptr, enumName);
 }
-END_API_FUNC

+ 394 - 486
Engine/lib/openal-soft/al/filter.cpp

@@ -29,8 +29,10 @@
 #include <iterator>
 #include <memory>
 #include <mutex>
-#include <new>
 #include <numeric>
+#include <string>
+#include <unordered_map>
+#include <vector>
 
 #include "AL/al.h"
 #include "AL/alc.h"
@@ -39,252 +41,19 @@
 #include "albit.h"
 #include "alc/context.h"
 #include "alc/device.h"
+#include "alc/inprogext.h"
 #include "almalloc.h"
 #include "alnumeric.h"
-#include "core/except.h"
+#include "alspan.h"
+#include "direct_defs.h"
+#include "error.h"
+#include "intrusive_ptr.h"
 #include "opthelpers.h"
-#include "vector.h"
 
 
 namespace {
 
-class filter_exception final : public al::base_exception {
-    ALenum mErrorCode;
-
-public:
-#ifdef __USE_MINGW_ANSI_STDIO
-    [[gnu::format(gnu_printf, 3, 4)]]
-#else
-    [[gnu::format(printf, 3, 4)]]
-#endif
-    filter_exception(ALenum code, const char *msg, ...);
-    ~filter_exception() override;
-
-    ALenum errorCode() const noexcept { return mErrorCode; }
-};
-
-filter_exception::filter_exception(ALenum code, const char* msg, ...) : mErrorCode{code}
-{
-    std::va_list args;
-    va_start(args, msg);
-    setMessage(msg, args);
-    va_end(args);
-}
-filter_exception::~filter_exception() = default;
-
-
-#define DEFINE_ALFILTER_VTABLE(T)                                  \
-const ALfilter::Vtable T##_vtable = {                              \
-    T##_setParami, T##_setParamiv, T##_setParamf, T##_setParamfv,  \
-    T##_getParami, T##_getParamiv, T##_getParamf, T##_getParamfv,  \
-}
-
-void ALlowpass_setParami(ALfilter*, ALenum param, int)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; }
-void ALlowpass_setParamiv(ALfilter*, ALenum param, const int*)
-{
-    throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer-vector property 0x%04x",
-        param};
-}
-void ALlowpass_setParamf(ALfilter *filter, ALenum param, float val)
-{
-    switch(param)
-    {
-    case AL_LOWPASS_GAIN:
-        if(!(val >= AL_LOWPASS_MIN_GAIN && val <= AL_LOWPASS_MAX_GAIN))
-            throw filter_exception{AL_INVALID_VALUE, "Low-pass gain %f out of range", val};
-        filter->Gain = val;
-        break;
-
-    case AL_LOWPASS_GAINHF:
-        if(!(val >= AL_LOWPASS_MIN_GAINHF && val <= AL_LOWPASS_MAX_GAINHF))
-            throw filter_exception{AL_INVALID_VALUE, "Low-pass gainhf %f out of range", val};
-        filter->GainHF = val;
-        break;
-
-    default:
-        throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param};
-    }
-}
-void ALlowpass_setParamfv(ALfilter *filter, ALenum param, const float *vals)
-{ ALlowpass_setParamf(filter, param, vals[0]); }
-
-void ALlowpass_getParami(const ALfilter*, ALenum param, int*)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; }
-void ALlowpass_getParamiv(const ALfilter*, ALenum param, int*)
-{
-    throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer-vector property 0x%04x",
-        param};
-}
-void ALlowpass_getParamf(const ALfilter *filter, ALenum param, float *val)
-{
-    switch(param)
-    {
-    case AL_LOWPASS_GAIN:
-        *val = filter->Gain;
-        break;
-
-    case AL_LOWPASS_GAINHF:
-        *val = filter->GainHF;
-        break;
-
-    default:
-        throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param};
-    }
-}
-void ALlowpass_getParamfv(const ALfilter *filter, ALenum param, float *vals)
-{ ALlowpass_getParamf(filter, param, vals); }
-
-DEFINE_ALFILTER_VTABLE(ALlowpass);
-
-
-void ALhighpass_setParami(ALfilter*, ALenum param, int)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; }
-void ALhighpass_setParamiv(ALfilter*, ALenum param, const int*)
-{
-    throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer-vector property 0x%04x",
-        param};
-}
-void ALhighpass_setParamf(ALfilter *filter, ALenum param, float val)
-{
-    switch(param)
-    {
-    case AL_HIGHPASS_GAIN:
-        if(!(val >= AL_HIGHPASS_MIN_GAIN && val <= AL_HIGHPASS_MAX_GAIN))
-            throw filter_exception{AL_INVALID_VALUE, "High-pass gain %f out of range", val};
-        filter->Gain = val;
-        break;
-
-    case AL_HIGHPASS_GAINLF:
-        if(!(val >= AL_HIGHPASS_MIN_GAINLF && val <= AL_HIGHPASS_MAX_GAINLF))
-            throw filter_exception{AL_INVALID_VALUE, "High-pass gainlf %f out of range", val};
-        filter->GainLF = val;
-        break;
-
-    default:
-        throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param};
-    }
-}
-void ALhighpass_setParamfv(ALfilter *filter, ALenum param, const float *vals)
-{ ALhighpass_setParamf(filter, param, vals[0]); }
-
-void ALhighpass_getParami(const ALfilter*, ALenum param, int*)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; }
-void ALhighpass_getParamiv(const ALfilter*, ALenum param, int*)
-{
-    throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer-vector property 0x%04x",
-        param};
-}
-void ALhighpass_getParamf(const ALfilter *filter, ALenum param, float *val)
-{
-    switch(param)
-    {
-    case AL_HIGHPASS_GAIN:
-        *val = filter->Gain;
-        break;
-
-    case AL_HIGHPASS_GAINLF:
-        *val = filter->GainLF;
-        break;
-
-    default:
-        throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param};
-    }
-}
-void ALhighpass_getParamfv(const ALfilter *filter, ALenum param, float *vals)
-{ ALhighpass_getParamf(filter, param, vals); }
-
-DEFINE_ALFILTER_VTABLE(ALhighpass);
-
-
-void ALbandpass_setParami(ALfilter*, ALenum param, int)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; }
-void ALbandpass_setParamiv(ALfilter*, ALenum param, const int*)
-{
-    throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer-vector property 0x%04x",
-        param};
-}
-void ALbandpass_setParamf(ALfilter *filter, ALenum param, float val)
-{
-    switch(param)
-    {
-    case AL_BANDPASS_GAIN:
-        if(!(val >= AL_BANDPASS_MIN_GAIN && val <= AL_BANDPASS_MAX_GAIN))
-            throw filter_exception{AL_INVALID_VALUE, "Band-pass gain %f out of range", val};
-        filter->Gain = val;
-        break;
-
-    case AL_BANDPASS_GAINHF:
-        if(!(val >= AL_BANDPASS_MIN_GAINHF && val <= AL_BANDPASS_MAX_GAINHF))
-            throw filter_exception{AL_INVALID_VALUE, "Band-pass gainhf %f out of range", val};
-        filter->GainHF = val;
-        break;
-
-    case AL_BANDPASS_GAINLF:
-        if(!(val >= AL_BANDPASS_MIN_GAINLF && val <= AL_BANDPASS_MAX_GAINLF))
-            throw filter_exception{AL_INVALID_VALUE, "Band-pass gainlf %f out of range", val};
-        filter->GainLF = val;
-        break;
-
-    default:
-        throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param};
-    }
-}
-void ALbandpass_setParamfv(ALfilter *filter, ALenum param, const float *vals)
-{ ALbandpass_setParamf(filter, param, vals[0]); }
-
-void ALbandpass_getParami(const ALfilter*, ALenum param, int*)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; }
-void ALbandpass_getParamiv(const ALfilter*, ALenum param, int*)
-{
-    throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer-vector property 0x%04x",
-        param};
-}
-void ALbandpass_getParamf(const ALfilter *filter, ALenum param, float *val)
-{
-    switch(param)
-    {
-    case AL_BANDPASS_GAIN:
-        *val = filter->Gain;
-        break;
-
-    case AL_BANDPASS_GAINHF:
-        *val = filter->GainHF;
-        break;
-
-    case AL_BANDPASS_GAINLF:
-        *val = filter->GainLF;
-        break;
-
-    default:
-        throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param};
-    }
-}
-void ALbandpass_getParamfv(const ALfilter *filter, ALenum param, float *vals)
-{ ALbandpass_getParamf(filter, param, vals); }
-
-DEFINE_ALFILTER_VTABLE(ALbandpass);
-
-
-void ALnullfilter_setParami(ALfilter*, ALenum param, int)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
-void ALnullfilter_setParamiv(ALfilter*, ALenum param, const int*)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
-void ALnullfilter_setParamf(ALfilter*, ALenum param, float)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
-void ALnullfilter_setParamfv(ALfilter*, ALenum param, const float*)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
-
-void ALnullfilter_getParami(const ALfilter*, ALenum param, int*)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
-void ALnullfilter_getParamiv(const ALfilter*, ALenum param, int*)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
-void ALnullfilter_getParamf(const ALfilter*, ALenum param, float*)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
-void ALnullfilter_getParamfv(const ALfilter*, ALenum param, float*)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
-
-DEFINE_ALFILTER_VTABLE(ALnullfilter);
+using SubListAllocator = al::allocator<std::array<ALfilter,64>>;
 
 
 void InitFilterParams(ALfilter *filter, ALenum type)
@@ -293,44 +62,44 @@ void InitFilterParams(ALfilter *filter, ALenum type)
     {
         filter->Gain = AL_LOWPASS_DEFAULT_GAIN;
         filter->GainHF = AL_LOWPASS_DEFAULT_GAINHF;
-        filter->HFReference = LOWPASSFREQREF;
+        filter->HFReference = LowPassFreqRef;
         filter->GainLF = 1.0f;
-        filter->LFReference = HIGHPASSFREQREF;
-        filter->vtab = &ALlowpass_vtable;
+        filter->LFReference = HighPassFreqRef;
+        filter->mTypeVariant.emplace<LowpassFilterTable>();
     }
     else if(type == AL_FILTER_HIGHPASS)
     {
         filter->Gain = AL_HIGHPASS_DEFAULT_GAIN;
         filter->GainHF = 1.0f;
-        filter->HFReference = LOWPASSFREQREF;
+        filter->HFReference = LowPassFreqRef;
         filter->GainLF = AL_HIGHPASS_DEFAULT_GAINLF;
-        filter->LFReference = HIGHPASSFREQREF;
-        filter->vtab = &ALhighpass_vtable;
+        filter->LFReference = HighPassFreqRef;
+        filter->mTypeVariant.emplace<HighpassFilterTable>();
     }
     else if(type == AL_FILTER_BANDPASS)
     {
         filter->Gain = AL_BANDPASS_DEFAULT_GAIN;
         filter->GainHF = AL_BANDPASS_DEFAULT_GAINHF;
-        filter->HFReference = LOWPASSFREQREF;
+        filter->HFReference = LowPassFreqRef;
         filter->GainLF = AL_BANDPASS_DEFAULT_GAINLF;
-        filter->LFReference = HIGHPASSFREQREF;
-        filter->vtab = &ALbandpass_vtable;
+        filter->LFReference = HighPassFreqRef;
+        filter->mTypeVariant.emplace<BandpassFilterTable>();
     }
     else
     {
         filter->Gain = 1.0f;
         filter->GainHF = 1.0f;
-        filter->HFReference = LOWPASSFREQREF;
+        filter->HFReference = LowPassFreqRef;
         filter->GainLF = 1.0f;
-        filter->LFReference = HIGHPASSFREQREF;
-        filter->vtab = &ALnullfilter_vtable;
+        filter->LFReference = HighPassFreqRef;
+        filter->mTypeVariant.emplace<NullFilterTable>();
     }
     filter->type = type;
 }
 
-bool EnsureFilters(ALCdevice *device, size_t needed)
-{
-    size_t count{std::accumulate(device->FilterList.cbegin(), device->FilterList.cend(), size_t{0},
+auto EnsureFilters(ALCdevice *device, size_t needed) noexcept -> bool
+try {
+    size_t count{std::accumulate(device->FilterList.cbegin(), device->FilterList.cend(), 0_uz,
         [](size_t cur, const FilterSubList &sublist) noexcept -> size_t
         { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
 
@@ -339,22 +108,20 @@ bool EnsureFilters(ALCdevice *device, size_t needed)
         if(device->FilterList.size() >= 1<<25) UNLIKELY
             return false;
 
-        device->FilterList.emplace_back();
-        auto sublist = device->FilterList.end() - 1;
-        sublist->FreeMask = ~0_u64;
-        sublist->Filters = static_cast<ALfilter*>(al_calloc(alignof(ALfilter), sizeof(ALfilter)*64));
-        if(!sublist->Filters) UNLIKELY
-        {
-            device->FilterList.pop_back();
-            return false;
-        }
-        count += 64;
+        FilterSubList sublist{};
+        sublist.FreeMask = ~0_u64;
+        sublist.Filters = SubListAllocator{}.allocate(1);
+        device->FilterList.emplace_back(std::move(sublist));
+        count += std::tuple_size_v<SubListAllocator::value_type>;
     }
     return true;
 }
+catch(...) {
+    return false;
+}
 
 
-ALfilter *AllocFilter(ALCdevice *device)
+ALfilter *AllocFilter(ALCdevice *device) noexcept
 {
     auto sublist = std::find_if(device->FilterList.begin(), device->FilterList.end(),
         [](const FilterSubList &entry) noexcept -> bool
@@ -363,7 +130,7 @@ ALfilter *AllocFilter(ALCdevice *device)
     auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
     ASSUME(slidx < 64);
 
-    ALfilter *filter{al::construct_at(sublist->Filters + slidx)};
+    ALfilter *filter{al::construct_at(al::to_address(sublist->Filters->begin() + slidx))};
     InitFilterParams(filter, AL_FILTER_NULL);
 
     /* Add 1 to avoid filter ID 0. */
@@ -376,17 +143,19 @@ ALfilter *AllocFilter(ALCdevice *device)
 
 void FreeFilter(ALCdevice *device, ALfilter *filter)
 {
+    device->mFilterNames.erase(filter->id);
+
     const ALuint id{filter->id - 1};
     const size_t lidx{id >> 6};
     const ALuint slidx{id & 0x3f};
 
-    al::destroy_at(filter);
+    std::destroy_at(filter);
 
     device->FilterList[lidx].FreeMask |= 1_u64 << slidx;
 }
 
 
-inline ALfilter *LookupFilter(ALCdevice *device, ALuint id)
+auto LookupFilter(ALCdevice *device, ALuint id) noexcept -> ALfilter*
 {
     const size_t lidx{(id-1) >> 6};
     const ALuint slidx{(id-1) & 0x3f};
@@ -396,327 +165,466 @@ inline ALfilter *LookupFilter(ALCdevice *device, ALuint id)
     FilterSubList &sublist = device->FilterList[lidx];
     if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
         return nullptr;
-    return sublist.Filters + slidx;
+    return al::to_address(sublist.Filters->begin() + slidx);
 }
 
 } // namespace
 
-AL_API void AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters)
-START_API_FUNC
+/* Null filter parameter handlers */
+template<>
+void FilterTable<NullFilterTable>::setParami(ALfilter*, ALenum param, int)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+template<>
+void FilterTable<NullFilterTable>::setParamiv(ALfilter*, ALenum param, const int*)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+template<>
+void FilterTable<NullFilterTable>::setParamf(ALfilter*, ALenum param, float)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+template<>
+void FilterTable<NullFilterTable>::setParamfv(ALfilter*, ALenum param, const float*)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+template<>
+void FilterTable<NullFilterTable>::getParami(const ALfilter*, ALenum param, int*)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+template<>
+void FilterTable<NullFilterTable>::getParamiv(const ALfilter*, ALenum param, int*)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+template<>
+void FilterTable<NullFilterTable>::getParamf(const ALfilter*, ALenum param, float*)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+template<>
+void FilterTable<NullFilterTable>::getParamfv(const ALfilter*, ALenum param, float*)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+
+/* Lowpass parameter handlers */
+template<>
+void FilterTable<LowpassFilterTable>::setParami(ALfilter*, ALenum param, int)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; }
+template<>
+void FilterTable<LowpassFilterTable>::setParamiv(ALfilter *filter, ALenum param, const int *values)
+{ setParami(filter, param, *values); }
+template<>
+void FilterTable<LowpassFilterTable>::setParamf(ALfilter *filter, ALenum param, float val)
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    if(n < 0) UNLIKELY
-        context->setError(AL_INVALID_VALUE, "Generating %d filters", n);
-    if(n <= 0) UNLIKELY return;
-
-    ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->FilterLock};
-    if(!EnsureFilters(device, static_cast<ALuint>(n)))
+    switch(param)
     {
-        context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d filter%s", n, (n==1)?"":"s");
+    case AL_LOWPASS_GAIN:
+        if(!(val >= AL_LOWPASS_MIN_GAIN && val <= AL_LOWPASS_MAX_GAIN))
+            throw al::context_error{AL_INVALID_VALUE, "Low-pass gain %f out of range", val};
+        filter->Gain = val;
         return;
-    }
 
-    if(n == 1) LIKELY
+    case AL_LOWPASS_GAINHF:
+        if(!(val >= AL_LOWPASS_MIN_GAINHF && val <= AL_LOWPASS_MAX_GAINHF))
+            throw al::context_error{AL_INVALID_VALUE, "Low-pass gainhf %f out of range", val};
+        filter->GainHF = val;
+        return;
+    }
+    throw al::context_error{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param};
+}
+template<>
+void FilterTable<LowpassFilterTable>::setParamfv(ALfilter *filter, ALenum param, const float *vals)
+{ setParamf(filter, param, *vals); }
+template<>
+void FilterTable<LowpassFilterTable>::getParami(const ALfilter*, ALenum param, int*)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; }
+template<>
+void FilterTable<LowpassFilterTable>::getParamiv(const ALfilter *filter, ALenum param, int *values)
+{ getParami(filter, param, values); }
+template<>
+void FilterTable<LowpassFilterTable>::getParamf(const ALfilter *filter, ALenum param, float *val)
+{
+    switch(param)
     {
-        /* Special handling for the easy and normal case. */
-        ALfilter *filter{AllocFilter(device)};
-        if(filter) filters[0] = filter->id;
+    case AL_LOWPASS_GAIN: *val = filter->Gain; return;
+    case AL_LOWPASS_GAINHF: *val = filter->GainHF; return;
     }
-    else
+    throw al::context_error{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param};
+}
+template<>
+void FilterTable<LowpassFilterTable>::getParamfv(const ALfilter *filter, ALenum param, float *vals)
+{ getParamf(filter, param, vals); }
+
+/* Highpass parameter handlers */
+template<>
+void FilterTable<HighpassFilterTable>::setParami(ALfilter*, ALenum param, int)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; }
+template<>
+void FilterTable<HighpassFilterTable>::setParamiv(ALfilter *filter, ALenum param, const int *values)
+{ setParami(filter, param, *values); }
+template<>
+void FilterTable<HighpassFilterTable>::setParamf(ALfilter *filter, ALenum param, float val)
+{
+    switch(param)
+    {
+    case AL_HIGHPASS_GAIN:
+        if(!(val >= AL_HIGHPASS_MIN_GAIN && val <= AL_HIGHPASS_MAX_GAIN))
+            throw al::context_error{AL_INVALID_VALUE, "High-pass gain %f out of range", val};
+        filter->Gain = val;
+        return;
+
+    case AL_HIGHPASS_GAINLF:
+        if(!(val >= AL_HIGHPASS_MIN_GAINLF && val <= AL_HIGHPASS_MAX_GAINLF))
+            throw al::context_error{AL_INVALID_VALUE, "High-pass gainlf %f out of range", val};
+        filter->GainLF = val;
+        return;
+    }
+    throw al::context_error{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param};
+}
+template<>
+void FilterTable<HighpassFilterTable>::setParamfv(ALfilter *filter, ALenum param, const float *vals)
+{ setParamf(filter, param, *vals); }
+template<>
+void FilterTable<HighpassFilterTable>::getParami(const ALfilter*, ALenum param, int*)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; }
+template<>
+void FilterTable<HighpassFilterTable>::getParamiv(const ALfilter *filter, ALenum param, int *values)
+{ getParami(filter, param, values); }
+template<>
+void FilterTable<HighpassFilterTable>::getParamf(const ALfilter *filter, ALenum param, float *val)
+{
+    switch(param)
     {
-        /* Store the allocated buffer IDs in a separate local list, to avoid
-         * modifying the user storage in case of failure.
-         */
-        al::vector<ALuint> ids;
-        ids.reserve(static_cast<ALuint>(n));
-        do {
-            ALfilter *filter{AllocFilter(device)};
-            ids.emplace_back(filter->id);
-        } while(--n);
-        std::copy(ids.begin(), ids.end(), filters);
+    case AL_HIGHPASS_GAIN: *val = filter->Gain; return;
+    case AL_HIGHPASS_GAINLF: *val = filter->GainLF; return;
     }
+    throw al::context_error{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param};
 }
-END_API_FUNC
+template<>
+void FilterTable<HighpassFilterTable>::getParamfv(const ALfilter *filter, ALenum param, float *vals)
+{ getParamf(filter, param, vals); }
+
+/* Bandpass parameter handlers */
+template<>
+void FilterTable<BandpassFilterTable>::setParami(ALfilter*, ALenum param, int)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; }
+template<>
+void FilterTable<BandpassFilterTable>::setParamiv(ALfilter *filter, ALenum param, const int *values)
+{ setParami(filter, param, *values); }
+template<>
+void FilterTable<BandpassFilterTable>::setParamf(ALfilter *filter, ALenum param, float val)
+{
+    switch(param)
+    {
+    case AL_BANDPASS_GAIN:
+        if(!(val >= AL_BANDPASS_MIN_GAIN && val <= AL_BANDPASS_MAX_GAIN))
+            throw al::context_error{AL_INVALID_VALUE, "Band-pass gain %f out of range", val};
+        filter->Gain = val;
+        return;
 
-AL_API void AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters)
-START_API_FUNC
+    case AL_BANDPASS_GAINHF:
+        if(!(val >= AL_BANDPASS_MIN_GAINHF && val <= AL_BANDPASS_MAX_GAINHF))
+            throw al::context_error{AL_INVALID_VALUE, "Band-pass gainhf %f out of range", val};
+        filter->GainHF = val;
+        return;
+
+    case AL_BANDPASS_GAINLF:
+        if(!(val >= AL_BANDPASS_MIN_GAINLF && val <= AL_BANDPASS_MAX_GAINLF))
+            throw al::context_error{AL_INVALID_VALUE, "Band-pass gainlf %f out of range", val};
+        filter->GainLF = val;
+        return;
+    }
+    throw al::context_error{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param};
+}
+template<>
+void FilterTable<BandpassFilterTable>::setParamfv(ALfilter *filter, ALenum param, const float *vals)
+{ setParamf(filter, param, *vals); }
+template<>
+void FilterTable<BandpassFilterTable>::getParami(const ALfilter*, ALenum param, int*)
+{ throw al::context_error{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; }
+template<>
+void FilterTable<BandpassFilterTable>::getParamiv(const ALfilter *filter, ALenum param, int *values)
+{ getParami(filter, param, values); }
+template<>
+void FilterTable<BandpassFilterTable>::getParamf(const ALfilter *filter, ALenum param, float *val)
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+    switch(param)
+    {
+    case AL_BANDPASS_GAIN: *val = filter->Gain; return;
+    case AL_BANDPASS_GAINHF: *val = filter->GainHF; return;
+    case AL_BANDPASS_GAINLF: *val = filter->GainLF; return;
+    }
+    throw al::context_error{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param};
+}
+template<>
+void FilterTable<BandpassFilterTable>::getParamfv(const ALfilter *filter, ALenum param, float *vals)
+{ getParamf(filter, param, vals); }
+
+
+AL_API DECL_FUNC2(void, alGenFilters, ALsizei,n, ALuint*,filters)
+FORCE_ALIGN void AL_APIENTRY alGenFiltersDirect(ALCcontext *context, ALsizei n, ALuint *filters) noexcept
+try {
+    if(n < 0)
+        throw al::context_error{AL_INVALID_VALUE, "Generating %d filters", n};
+    if(n <= 0) UNLIKELY return;
+
+    ALCdevice *device{context->mALDevice.get()};
+    std::lock_guard<std::mutex> filterlock{device->FilterLock};
+
+    const al::span fids{filters, static_cast<ALuint>(n)};
+    if(!EnsureFilters(device, fids.size()))
+        throw al::context_error{AL_OUT_OF_MEMORY, "Failed to allocate %d filter%s", n,
+            (n == 1) ? "" : "s"};
+
+    std::generate(fids.begin(), fids.end(), [device]{ return AllocFilter(device)->id; });
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
+}
 
-    if(n < 0) UNLIKELY
-        context->setError(AL_INVALID_VALUE, "Deleting %d filters", n);
+AL_API DECL_FUNC2(void, alDeleteFilters, ALsizei,n, const ALuint*,filters)
+FORCE_ALIGN void AL_APIENTRY alDeleteFiltersDirect(ALCcontext *context, ALsizei n,
+    const ALuint *filters) noexcept
+try {
+    if(n < 0)
+        throw al::context_error{AL_INVALID_VALUE, "Deleting %d filters", n};
     if(n <= 0) UNLIKELY return;
 
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->FilterLock};
+    std::lock_guard<std::mutex> filterlock{device->FilterLock};
 
     /* First try to find any filters that are invalid. */
     auto validate_filter = [device](const ALuint fid) -> bool
     { return !fid || LookupFilter(device, fid) != nullptr; };
 
-    const ALuint *filters_end = filters + n;
-    auto invflt = std::find_if_not(filters, filters_end, validate_filter);
-    if(invflt != filters_end) UNLIKELY
-    {
-        context->setError(AL_INVALID_NAME, "Invalid filter ID %u", *invflt);
-        return;
-    }
+    const al::span fids{filters, static_cast<ALuint>(n)};
+    auto invflt = std::find_if_not(fids.begin(), fids.end(), validate_filter);
+    if(invflt != fids.end())
+        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", *invflt};
 
     /* All good. Delete non-0 filter IDs. */
     auto delete_filter = [device](const ALuint fid) -> void
     {
-        ALfilter *filter{fid ? LookupFilter(device, fid) : nullptr};
-        if(filter) FreeFilter(device, filter);
+        if(ALfilter *filter{fid ? LookupFilter(device, fid) : nullptr})
+            FreeFilter(device, filter);
     };
-    std::for_each(filters, filters_end, delete_filter);
+    std::for_each(fids.begin(), fids.end(), delete_filter);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
 
-AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter)
-START_API_FUNC
+AL_API DECL_FUNC1(ALboolean, alIsFilter, ALuint,filter)
+FORCE_ALIGN ALboolean AL_APIENTRY alIsFilterDirect(ALCcontext *context, ALuint filter) noexcept
 {
-    ContextRef context{GetContextRef()};
-    if(context) LIKELY
-    {
-        ALCdevice *device{context->mALDevice.get()};
-        std::lock_guard<std::mutex> _{device->FilterLock};
-        if(!filter || LookupFilter(device, filter))
-            return AL_TRUE;
-    }
+    ALCdevice *device{context->mALDevice.get()};
+    std::lock_guard<std::mutex> filterlock{device->FilterLock};
+    if(!filter || LookupFilter(device, filter))
+        return AL_TRUE;
     return AL_FALSE;
 }
-END_API_FUNC
-
 
-AL_API void AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint value)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
 
+AL_API DECL_FUNC3(void, alFilteri, ALuint,filter, ALenum,param, ALint,value)
+FORCE_ALIGN void AL_APIENTRY alFilteriDirect(ALCcontext *context, ALuint filter, ALenum param,
+    ALint value) noexcept
+try {
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->FilterLock};
+    std::lock_guard<std::mutex> filterlock{device->FilterLock};
 
     ALfilter *alfilt{LookupFilter(device, filter)};
-    if(!alfilt) UNLIKELY
-        context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
-    else
+    if(!alfilt)
+        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
+
+    switch(param)
     {
-        if(param == AL_FILTER_TYPE)
-        {
-            if(value == AL_FILTER_NULL || value == AL_FILTER_LOWPASS
-                || value == AL_FILTER_HIGHPASS || value == AL_FILTER_BANDPASS)
-                InitFilterParams(alfilt, value);
-            else
-                context->setError(AL_INVALID_VALUE, "Invalid filter type 0x%04x", value);
-        }
-        else try
-        {
-            /* Call the appropriate handler */
-            alfilt->setParami(param, value);
-        }
-        catch(filter_exception &e) {
-            context->setError(e.errorCode(), "%s", e.what());
-        }
+    case AL_FILTER_TYPE:
+        if(!(value == AL_FILTER_NULL || value == AL_FILTER_LOWPASS
+            || value == AL_FILTER_HIGHPASS || value == AL_FILTER_BANDPASS))
+            throw al::context_error{AL_INVALID_VALUE, "Invalid filter type 0x%04x", value};
+        InitFilterParams(alfilt, value);
+        return;
     }
+
+    /* Call the appropriate handler */
+    std::visit([alfilt,param,value](auto&& thunk){thunk.setParami(alfilt, param, value);},
+        alfilt->mTypeVariant);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *values)
-START_API_FUNC
-{
+AL_API DECL_FUNC3(void, alFilteriv, ALuint,filter, ALenum,param, const ALint*,values)
+FORCE_ALIGN void AL_APIENTRY alFilterivDirect(ALCcontext *context, ALuint filter, ALenum param,
+    const ALint *values) noexcept
+try {
     switch(param)
     {
     case AL_FILTER_TYPE:
-        alFilteri(filter, param, values[0]);
+        alFilteriDirect(context, filter, param, *values);
         return;
     }
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->FilterLock};
+    std::lock_guard<std::mutex> filterlock{device->FilterLock};
 
     ALfilter *alfilt{LookupFilter(device, filter)};
-    if(!alfilt) UNLIKELY
-        context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
-    else try
-    {
-        /* Call the appropriate handler */
-        alfilt->setParamiv(param, values);
-    }
-    catch(filter_exception &e) {
-        context->setError(e.errorCode(), "%s", e.what());
-    }
-}
-END_API_FUNC
+    if(!alfilt)
+        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
 
-AL_API void AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat value)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+    /* Call the appropriate handler */
+    std::visit([alfilt,param,values](auto&& thunk){thunk.setParamiv(alfilt, param, values);},
+        alfilt->mTypeVariant);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
+}
 
+AL_API DECL_FUNC3(void, alFilterf, ALuint,filter, ALenum,param, ALfloat,value)
+FORCE_ALIGN void AL_APIENTRY alFilterfDirect(ALCcontext *context, ALuint filter, ALenum param,
+    ALfloat value) noexcept
+try {
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->FilterLock};
+    std::lock_guard<std::mutex> filterlock{device->FilterLock};
 
     ALfilter *alfilt{LookupFilter(device, filter)};
-    if(!alfilt) UNLIKELY
-        context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
-    else try
-    {
-        /* Call the appropriate handler */
-        alfilt->setParamf(param, value);
-    }
-    catch(filter_exception &e) {
-        context->setError(e.errorCode(), "%s", e.what());
-    }
-}
-END_API_FUNC
+    if(!alfilt)
+        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
 
-AL_API void AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat *values)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+    /* Call the appropriate handler */
+    std::visit([alfilt,param,value](auto&& thunk){thunk.setParamf(alfilt, param, value);},
+        alfilt->mTypeVariant);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
+}
 
+AL_API DECL_FUNC3(void, alFilterfv, ALuint,filter, ALenum,param, const ALfloat*,values)
+FORCE_ALIGN void AL_APIENTRY alFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param,
+    const ALfloat *values) noexcept
+try {
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->FilterLock};
+    std::lock_guard<std::mutex> filterlock{device->FilterLock};
 
     ALfilter *alfilt{LookupFilter(device, filter)};
-    if(!alfilt) UNLIKELY
-        context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
-    else try
-    {
-        /* Call the appropriate handler */
-        alfilt->setParamfv(param, values);
-    }
-    catch(filter_exception &e) {
-        context->setError(e.errorCode(), "%s", e.what());
-    }
-}
-END_API_FUNC
+    if(!alfilt)
+        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
 
-AL_API void AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *value)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+    /* Call the appropriate handler */
+    std::visit([alfilt,param,values](auto&& thunk){thunk.setParamfv(alfilt, param, values);},
+        alfilt->mTypeVariant);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
+}
 
+AL_API DECL_FUNC3(void, alGetFilteri, ALuint,filter, ALenum,param, ALint*,value)
+FORCE_ALIGN void AL_APIENTRY alGetFilteriDirect(ALCcontext *context, ALuint filter, ALenum param,
+    ALint *value) noexcept
+try {
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->FilterLock};
+    std::lock_guard<std::mutex> filterlock{device->FilterLock};
 
     const ALfilter *alfilt{LookupFilter(device, filter)};
-    if(!alfilt) UNLIKELY
-        context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
-    else
+    if(!alfilt)
+        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
+
+    switch(param)
     {
-        if(param == AL_FILTER_TYPE)
-            *value = alfilt->type;
-        else try
-        {
-            /* Call the appropriate handler */
-            alfilt->getParami(param, value);
-        }
-        catch(filter_exception &e) {
-            context->setError(e.errorCode(), "%s", e.what());
-        }
+    case AL_FILTER_TYPE:
+        *value = alfilt->type;
+        return;
     }
+
+    /* Call the appropriate handler */
+    std::visit([alfilt,param,value](auto&& thunk){thunk.getParami(alfilt, param, value);},
+        alfilt->mTypeVariant);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *values)
-START_API_FUNC
-{
+AL_API DECL_FUNC3(void, alGetFilteriv, ALuint,filter, ALenum,param, ALint*,values)
+FORCE_ALIGN void AL_APIENTRY alGetFilterivDirect(ALCcontext *context, ALuint filter, ALenum param,
+    ALint *values) noexcept
+try {
     switch(param)
     {
     case AL_FILTER_TYPE:
-        alGetFilteri(filter, param, values);
+        alGetFilteriDirect(context, filter, param, values);
         return;
     }
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+    ALCdevice *device{context->mALDevice.get()};
+    std::lock_guard<std::mutex> filterlock{device->FilterLock};
+
+    const ALfilter *alfilt{LookupFilter(device, filter)};
+    if(!alfilt)
+        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
+
+    /* Call the appropriate handler */
+    std::visit([alfilt,param,values](auto&& thunk){thunk.getParamiv(alfilt, param, values);},
+        alfilt->mTypeVariant);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
+}
 
+AL_API DECL_FUNC3(void, alGetFilterf, ALuint,filter, ALenum,param, ALfloat*,value)
+FORCE_ALIGN void AL_APIENTRY alGetFilterfDirect(ALCcontext *context, ALuint filter, ALenum param,
+    ALfloat *value) noexcept
+try {
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->FilterLock};
+    std::lock_guard<std::mutex> filterlock{device->FilterLock};
 
     const ALfilter *alfilt{LookupFilter(device, filter)};
     if(!alfilt) UNLIKELY
-        context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
-    else try
-    {
-        /* Call the appropriate handler */
-        alfilt->getParamiv(param, values);
-    }
-    catch(filter_exception &e) {
-        context->setError(e.errorCode(), "%s", e.what());
-    }
-}
-END_API_FUNC
+        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
 
-AL_API void AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *value)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+    /* Call the appropriate handler */
+    std::visit([alfilt,param,value](auto&& thunk){thunk.getParamf(alfilt, param, value);},
+        alfilt->mTypeVariant);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
+}
 
+AL_API DECL_FUNC3(void, alGetFilterfv, ALuint,filter, ALenum,param, ALfloat*,values)
+FORCE_ALIGN void AL_APIENTRY alGetFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param,
+    ALfloat *values) noexcept
+try {
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->FilterLock};
+    std::lock_guard<std::mutex> filterlock{device->FilterLock};
 
     const ALfilter *alfilt{LookupFilter(device, filter)};
     if(!alfilt) UNLIKELY
-        context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
-    else try
-    {
-        /* Call the appropriate handler */
-        alfilt->getParamf(param, value);
-    }
-    catch(filter_exception &e) {
-        context->setError(e.errorCode(), "%s", e.what());
-    }
+        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", filter};
+
+    /* Call the appropriate handler */
+    std::visit([alfilt,param,values](auto&& thunk){thunk.getParamfv(alfilt, param, values);},
+        alfilt->mTypeVariant);
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *values)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
 
+void ALfilter::SetName(ALCcontext *context, ALuint id, std::string_view name)
+{
     ALCdevice *device{context->mALDevice.get()};
-    std::lock_guard<std::mutex> _{device->FilterLock};
+    std::lock_guard<std::mutex> filterlock{device->FilterLock};
 
-    const ALfilter *alfilt{LookupFilter(device, filter)};
-    if(!alfilt) UNLIKELY
-        context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
-    else try
-    {
-        /* Call the appropriate handler */
-        alfilt->getParamfv(param, values);
-    }
-    catch(filter_exception &e) {
-        context->setError(e.errorCode(), "%s", e.what());
-    }
+    auto filter = LookupFilter(device, id);
+    if(!filter)
+        throw al::context_error{AL_INVALID_NAME, "Invalid filter ID %u", id};
+
+    device->mFilterNames.insert_or_assign(id, name);
 }
-END_API_FUNC
 
 
 FilterSubList::~FilterSubList()
 {
+    if(!Filters)
+        return;
+
     uint64_t usemask{~FreeMask};
     while(usemask)
     {
         const int idx{al::countr_zero(usemask)};
-        al::destroy_at(Filters+idx);
+        std::destroy_at(al::to_address(Filters->begin() + idx));
         usemask &= ~(1_u64 << idx);
     }
     FreeMask = ~usemask;
-    al_free(Filters);
+    SubListAllocator{}.deallocate(Filters, 1);
     Filters = nullptr;
 }

+ 50 - 26
Engine/lib/openal-soft/al/filter.h

@@ -1,15 +1,40 @@
 #ifndef AL_FILTER_H
 #define AL_FILTER_H
 
+#include <array>
+#include <cstdint>
+#include <string_view>
+#include <utility>
+#include <variant>
 
 #include "AL/al.h"
 #include "AL/alc.h"
-#include "AL/alext.h"
+#include "AL/efx.h"
 
 #include "almalloc.h"
+#include "alnumeric.h"
 
-#define LOWPASSFREQREF  5000.0f
-#define HIGHPASSFREQREF  250.0f
+
+inline constexpr float LowPassFreqRef{5000.0f};
+inline constexpr float HighPassFreqRef{250.0f};
+
+template<typename T>
+struct FilterTable {
+    static void setParami(struct ALfilter*, ALenum, int);
+    static void setParamiv(struct ALfilter*, ALenum, const int*);
+    static void setParamf(struct ALfilter*, ALenum, float);
+    static void setParamfv(struct ALfilter*, ALenum, const float*);
+
+    static void getParami(const struct ALfilter*, ALenum, int*);
+    static void getParamiv(const struct ALfilter*, ALenum, int*);
+    static void getParamf(const struct ALfilter*, ALenum, float*);
+    static void getParamfv(const struct ALfilter*, ALenum, float*);
+};
+
+struct NullFilterTable : public FilterTable<NullFilterTable> { };
+struct LowpassFilterTable : public FilterTable<LowpassFilterTable> { };
+struct HighpassFilterTable : public FilterTable<HighpassFilterTable> { };
+struct BandpassFilterTable : public FilterTable<BandpassFilterTable> { };
 
 
 struct ALfilter {
@@ -17,36 +42,35 @@ struct ALfilter {
 
     float Gain{1.0f};
     float GainHF{1.0f};
-    float HFReference{LOWPASSFREQREF};
+    float HFReference{LowPassFreqRef};
     float GainLF{1.0f};
-    float LFReference{HIGHPASSFREQREF};
+    float LFReference{HighPassFreqRef};
 
-    struct Vtable {
-        void (*const setParami )(ALfilter *filter, ALenum param, int val);
-        void (*const setParamiv)(ALfilter *filter, ALenum param, const int *vals);
-        void (*const setParamf )(ALfilter *filter, ALenum param, float val);
-        void (*const setParamfv)(ALfilter *filter, ALenum param, const float *vals);
-
-        void (*const getParami )(const ALfilter *filter, ALenum param, int *val);
-        void (*const getParamiv)(const ALfilter *filter, ALenum param, int *vals);
-        void (*const getParamf )(const ALfilter *filter, ALenum param, float *val);
-        void (*const getParamfv)(const ALfilter *filter, ALenum param, float *vals);
-    };
-    const Vtable *vtab{nullptr};
+    using TableTypes = std::variant<NullFilterTable,LowpassFilterTable,HighpassFilterTable,
+        BandpassFilterTable>;
+    TableTypes mTypeVariant;
 
     /* Self ID */
     ALuint id{0};
 
-    void setParami(ALenum param, int value) { vtab->setParami(this, param, value); }
-    void setParamiv(ALenum param, const int *values) { vtab->setParamiv(this, param, values); }
-    void setParamf(ALenum param, float value) { vtab->setParamf(this, param, value); }
-    void setParamfv(ALenum param, const float *values) { vtab->setParamfv(this, param, values); }
-    void getParami(ALenum param, int *value) const { vtab->getParami(this, param, value); }
-    void getParamiv(ALenum param, int *values) const { vtab->getParamiv(this, param, values); }
-    void getParamf(ALenum param, float *value) const { vtab->getParamf(this, param, value); }
-    void getParamfv(ALenum param, float *values) const { vtab->getParamfv(this, param, values); }
+    static void SetName(ALCcontext *context, ALuint id, std::string_view name);
+
+    DISABLE_ALLOC
+};
+
+struct FilterSubList {
+    uint64_t FreeMask{~0_u64};
+    gsl::owner<std::array<ALfilter,64>*> Filters{nullptr};
+
+    FilterSubList() noexcept = default;
+    FilterSubList(const FilterSubList&) = delete;
+    FilterSubList(FilterSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Filters{rhs.Filters}
+    { rhs.FreeMask = ~0_u64; rhs.Filters = nullptr; }
+    ~FilterSubList();
 
-    DISABLE_ALLOC()
+    FilterSubList& operator=(const FilterSubList&) = delete;
+    FilterSubList& operator=(FilterSubList&& rhs) noexcept
+    { std::swap(FreeMask, rhs.FreeMask); std::swap(Filters, rhs.Filters); return *this; }
 };
 
 #endif

+ 203 - 245
Engine/lib/openal-soft/al/listener.cpp

@@ -22,6 +22,7 @@
 
 #include "listener.h"
 
+#include <algorithm>
 #include <cmath>
 #include <mutex>
 
@@ -30,9 +31,10 @@
 #include "AL/efx.h"
 
 #include "alc/context.h"
-#include "almalloc.h"
-#include "atomic.h"
-#include "core/except.h"
+#include "alc/inprogext.h"
+#include "alspan.h"
+#include "direct_defs.h"
+#include "error.h"
 #include "opthelpers.h"
 
 
@@ -68,377 +70,333 @@ inline void CommitAndUpdateProps(ALCcontext *context)
 
 } // namespace
 
-AL_API void AL_APIENTRY alListenerf(ALenum param, ALfloat value)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
+AL_API DECL_FUNC2(void, alListenerf, ALenum,param, ALfloat,value)
+FORCE_ALIGN void AL_APIENTRY alListenerfDirect(ALCcontext *context, ALenum param, ALfloat value) noexcept
+try {
     ALlistener &listener = context->mListener;
-    std::lock_guard<std::mutex> _{context->mPropLock};
+    std::lock_guard<std::mutex> proplock{context->mPropLock};
     switch(param)
     {
     case AL_GAIN:
         if(!(value >= 0.0f && std::isfinite(value)))
-            return context->setError(AL_INVALID_VALUE, "Listener gain out of range");
+            throw al::context_error{AL_INVALID_VALUE, "Listener gain out of range"};
         listener.Gain = value;
-        UpdateProps(context.get());
-        break;
+        UpdateProps(context);
+        return;
 
     case AL_METERS_PER_UNIT:
         if(!(value >= AL_MIN_METERS_PER_UNIT && value <= AL_MAX_METERS_PER_UNIT))
-            return context->setError(AL_INVALID_VALUE, "Listener meters per unit out of range");
+            throw al::context_error{AL_INVALID_VALUE, "Listener meters per unit out of range"};
         listener.mMetersPerUnit = value;
-        UpdateProps(context.get());
-        break;
-
-    default:
-        context->setError(AL_INVALID_ENUM, "Invalid listener float property");
+        UpdateProps(context);
+        return;
     }
+    throw al::context_error{AL_INVALID_ENUM, "Invalid listener float property 0x%x", param};
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
-
-AL_API void AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
 
+AL_API DECL_FUNC4(void, alListener3f, ALenum,param, ALfloat,value1, ALfloat,value2, ALfloat,value3)
+FORCE_ALIGN void AL_APIENTRY alListener3fDirect(ALCcontext *context, ALenum param, ALfloat value1,
+    ALfloat value2, ALfloat value3) noexcept
+try {
     ALlistener &listener = context->mListener;
-    std::lock_guard<std::mutex> _{context->mPropLock};
+    std::lock_guard<std::mutex> proplock{context->mPropLock};
     switch(param)
     {
     case AL_POSITION:
         if(!(std::isfinite(value1) && std::isfinite(value2) && std::isfinite(value3)))
-            return context->setError(AL_INVALID_VALUE, "Listener position out of range");
+            throw al::context_error{AL_INVALID_VALUE, "Listener position out of range"};
         listener.Position[0] = value1;
         listener.Position[1] = value2;
         listener.Position[2] = value3;
-        CommitAndUpdateProps(context.get());
-        break;
+        CommitAndUpdateProps(context);
+        return;
 
     case AL_VELOCITY:
         if(!(std::isfinite(value1) && std::isfinite(value2) && std::isfinite(value3)))
-            return context->setError(AL_INVALID_VALUE, "Listener velocity out of range");
+            throw al::context_error{AL_INVALID_VALUE, "Listener velocity out of range"};
         listener.Velocity[0] = value1;
         listener.Velocity[1] = value2;
         listener.Velocity[2] = value3;
-        CommitAndUpdateProps(context.get());
-        break;
-
-    default:
-        context->setError(AL_INVALID_ENUM, "Invalid listener 3-float property");
+        CommitAndUpdateProps(context);
+        return;
     }
+    throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-float property 0x%x", param};
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values)
-START_API_FUNC
-{
-    if(values)
+AL_API DECL_FUNC2(void, alListenerfv, ALenum,param, const ALfloat*,values)
+FORCE_ALIGN void AL_APIENTRY alListenerfvDirect(ALCcontext *context, ALenum param,
+    const ALfloat *values) noexcept
+try {
+    if(!values)
+        throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
+
+    switch(param)
     {
-        switch(param)
-        {
-        case AL_GAIN:
-        case AL_METERS_PER_UNIT:
-            alListenerf(param, values[0]);
-            return;
+    case AL_GAIN:
+    case AL_METERS_PER_UNIT:
+        alListenerfDirect(context, param, *values);
+        return;
 
-        case AL_POSITION:
-        case AL_VELOCITY:
-            alListener3f(param, values[0], values[1], values[2]);
-            return;
-        }
+    case AL_POSITION:
+    case AL_VELOCITY:
+        auto vals = al::span<const float,3>{values, 3_uz};
+        alListener3fDirect(context, param, vals[0], vals[1], vals[2]);
+        return;
     }
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    if(!values) UNLIKELY
-        return context->setError(AL_INVALID_VALUE, "NULL pointer");
-
     ALlistener &listener = context->mListener;
-    std::lock_guard<std::mutex> _{context->mPropLock};
+    std::lock_guard<std::mutex> proplock{context->mPropLock};
     switch(param)
     {
     case AL_ORIENTATION:
-        if(!(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]) &&
-             std::isfinite(values[3]) && std::isfinite(values[4]) && std::isfinite(values[5])))
+        auto vals = al::span<const float,6>{values, 6_uz};
+        if(!std::all_of(vals.cbegin(), vals.cend(), [](float f) { return std::isfinite(f); }))
             return context->setError(AL_INVALID_VALUE, "Listener orientation out of range");
         /* AT then UP */
-        listener.OrientAt[0] = values[0];
-        listener.OrientAt[1] = values[1];
-        listener.OrientAt[2] = values[2];
-        listener.OrientUp[0] = values[3];
-        listener.OrientUp[1] = values[4];
-        listener.OrientUp[2] = values[5];
-        CommitAndUpdateProps(context.get());
-        break;
-
-    default:
-        context->setError(AL_INVALID_ENUM, "Invalid listener float-vector property");
+        std::copy_n(vals.cbegin(), 3, listener.OrientAt.begin());
+        std::copy_n(vals.cbegin()+3, 3, listener.OrientUp.begin());
+        CommitAndUpdateProps(context);
+        return;
     }
+    throw al::context_error{AL_INVALID_ENUM, "Invalid listener float-vector property 0x%x", param};
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
 
 
-AL_API void AL_APIENTRY alListeneri(ALenum param, ALint /*value*/)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    switch(param)
-    {
-    default:
-        context->setError(AL_INVALID_ENUM, "Invalid listener integer property");
-    }
+AL_API DECL_FUNC2(void, alListeneri, ALenum,param, ALint,value)
+FORCE_ALIGN void AL_APIENTRY alListeneriDirect(ALCcontext *context, ALenum param, ALint /*value*/) noexcept
+try {
+    std::lock_guard<std::mutex> proplock{context->mPropLock};
+    throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer property 0x%x", param};
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3)
-START_API_FUNC
-{
+AL_API DECL_FUNC4(void, alListener3i, ALenum,param, ALint,value1, ALint,value2, ALint,value3)
+FORCE_ALIGN void AL_APIENTRY alListener3iDirect(ALCcontext *context, ALenum param, ALint value1,
+    ALint value2, ALint value3) noexcept
+try {
     switch(param)
     {
     case AL_POSITION:
     case AL_VELOCITY:
-        alListener3f(param, static_cast<ALfloat>(value1), static_cast<ALfloat>(value2),
-            static_cast<ALfloat>(value3));
+        alListener3fDirect(context, param, static_cast<ALfloat>(value1),
+            static_cast<ALfloat>(value2), static_cast<ALfloat>(value3));
         return;
     }
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    switch(param)
-    {
-    default:
-        context->setError(AL_INVALID_ENUM, "Invalid listener 3-integer property");
-    }
+    std::lock_guard<std::mutex> proplock{context->mPropLock};
+    throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-integer property 0x%x", param};
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values)
-START_API_FUNC
-{
-    if(values)
+AL_API DECL_FUNC2(void, alListeneriv, ALenum,param, const ALint*,values)
+FORCE_ALIGN void AL_APIENTRY alListenerivDirect(ALCcontext *context, ALenum param,
+    const ALint *values) noexcept
+try {
+    if(!values)
+        throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
+
+    al::span<const ALint> vals;
+    switch(param)
     {
-        ALfloat fvals[6];
-        switch(param)
-        {
-        case AL_POSITION:
-        case AL_VELOCITY:
-            alListener3f(param, static_cast<ALfloat>(values[0]), static_cast<ALfloat>(values[1]),
-                static_cast<ALfloat>(values[2]));
-            return;
+    case AL_POSITION:
+    case AL_VELOCITY:
+        vals = {values, 3_uz};
+        alListener3fDirect(context, param, static_cast<ALfloat>(vals[0]),
+            static_cast<ALfloat>(vals[1]), static_cast<ALfloat>(vals[2]));
+        return;
 
-        case AL_ORIENTATION:
-            fvals[0] = static_cast<ALfloat>(values[0]);
-            fvals[1] = static_cast<ALfloat>(values[1]);
-            fvals[2] = static_cast<ALfloat>(values[2]);
-            fvals[3] = static_cast<ALfloat>(values[3]);
-            fvals[4] = static_cast<ALfloat>(values[4]);
-            fvals[5] = static_cast<ALfloat>(values[5]);
-            alListenerfv(param, fvals);
-            return;
-        }
+    case AL_ORIENTATION:
+        vals = {values, 6_uz};
+        const std::array fvals{static_cast<ALfloat>(vals[0]), static_cast<ALfloat>(vals[1]),
+            static_cast<ALfloat>(vals[2]), static_cast<ALfloat>(vals[3]),
+            static_cast<ALfloat>(vals[4]), static_cast<ALfloat>(vals[5]),
+        };
+        alListenerfvDirect(context, param, fvals.data());
+        return;
     }
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    if(!values) UNLIKELY
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(param)
-    {
-    default:
-        context->setError(AL_INVALID_ENUM, "Invalid listener integer-vector property");
-    }
+    std::lock_guard<std::mutex> proplock{context->mPropLock};
+    throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer-vector property 0x%x",
+        param};
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
 
 
-AL_API void AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+AL_API DECL_FUNC2(void, alGetListenerf, ALenum,param, ALfloat*,value)
+FORCE_ALIGN void AL_APIENTRY alGetListenerfDirect(ALCcontext *context, ALenum param,
+    ALfloat *value) noexcept
+try {
+    if(!value)
+        throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
 
     ALlistener &listener = context->mListener;
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    if(!value)
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(param)
+    std::lock_guard<std::mutex> proplock{context->mPropLock};
+    switch(param)
     {
-    case AL_GAIN:
-        *value = listener.Gain;
-        break;
-
-    case AL_METERS_PER_UNIT:
-        *value = listener.mMetersPerUnit;
-        break;
-
-    default:
-        context->setError(AL_INVALID_ENUM, "Invalid listener float property");
+    case AL_GAIN: *value = listener.Gain; return;
+    case AL_METERS_PER_UNIT: *value = listener.mMetersPerUnit; return;
     }
+    throw al::context_error{AL_INVALID_ENUM, "Invalid listener float property 0x%x", param};
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+AL_API DECL_FUNC4(void, alGetListener3f, ALenum,param, ALfloat*,value1, ALfloat*,value2, ALfloat*,value3)
+FORCE_ALIGN void AL_APIENTRY alGetListener3fDirect(ALCcontext *context, ALenum param,
+    ALfloat *value1, ALfloat *value2, ALfloat *value3) noexcept
+try {
+    if(!value1 || !value2 || !value3)
+        throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
 
     ALlistener &listener = context->mListener;
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    if(!value1 || !value2 || !value3)
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(param)
+    std::lock_guard<std::mutex> proplock{context->mPropLock};
+    switch(param)
     {
     case AL_POSITION:
         *value1 = listener.Position[0];
         *value2 = listener.Position[1];
         *value3 = listener.Position[2];
-        break;
+        return;
 
     case AL_VELOCITY:
         *value1 = listener.Velocity[0];
         *value2 = listener.Velocity[1];
         *value3 = listener.Velocity[2];
-        break;
-
-    default:
-        context->setError(AL_INVALID_ENUM, "Invalid listener 3-float property");
+        return;
     }
+    throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-float property 0x%x", param};
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values)
-START_API_FUNC
-{
+AL_API DECL_FUNC2(void, alGetListenerfv, ALenum,param, ALfloat*,values)
+FORCE_ALIGN void AL_APIENTRY alGetListenerfvDirect(ALCcontext *context, ALenum param,
+    ALfloat *values) noexcept
+try {
+    if(!values)
+        throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
+
     switch(param)
     {
     case AL_GAIN:
     case AL_METERS_PER_UNIT:
-        alGetListenerf(param, values);
+        alGetListenerfDirect(context, param, values);
         return;
 
     case AL_POSITION:
     case AL_VELOCITY:
-        alGetListener3f(param, values+0, values+1, values+2);
+        auto vals = al::span<ALfloat,3>{values, 3_uz};
+        alGetListener3fDirect(context, param, &vals[0], &vals[1], &vals[2]);
         return;
     }
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
     ALlistener &listener = context->mListener;
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    if(!values)
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(param)
+    std::lock_guard<std::mutex> proplock{context->mPropLock};
+    switch(param)
     {
     case AL_ORIENTATION:
+        al::span<ALfloat,6> vals{values, 6_uz};
         // AT then UP
-        values[0] = listener.OrientAt[0];
-        values[1] = listener.OrientAt[1];
-        values[2] = listener.OrientAt[2];
-        values[3] = listener.OrientUp[0];
-        values[4] = listener.OrientUp[1];
-        values[5] = listener.OrientUp[2];
-        break;
-
-    default:
-        context->setError(AL_INVALID_ENUM, "Invalid listener float-vector property");
+        std::copy_n(listener.OrientAt.cbegin(), 3, vals.begin());
+        std::copy_n(listener.OrientUp.cbegin(), 3, vals.begin()+3);
+        return;
     }
+    throw al::context_error{AL_INVALID_ENUM, "Invalid listener float-vector property 0x%x", param};
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
 
 
-AL_API void AL_APIENTRY alGetListeneri(ALenum param, ALint *value)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    if(!value)
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(param)
-    {
-    default:
-        context->setError(AL_INVALID_ENUM, "Invalid listener integer property");
-    }
+AL_API DECL_FUNC2(void, alGetListeneri, ALenum,param, ALint*,value)
+FORCE_ALIGN void AL_APIENTRY alGetListeneriDirect(ALCcontext *context, ALenum param, ALint *value) noexcept
+try {
+    if(!value) throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
+    std::lock_guard<std::mutex> proplock{context->mPropLock};
+    throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer property 0x%x", param};
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+AL_API DECL_FUNC4(void, alGetListener3i, ALenum,param, ALint*,value1, ALint*,value2, ALint*,value3)
+FORCE_ALIGN void AL_APIENTRY alGetListener3iDirect(ALCcontext *context, ALenum param,
+    ALint *value1, ALint *value2, ALint *value3) noexcept
+try {
+    if(!value1 || !value2 || !value3)
+        throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
 
     ALlistener &listener = context->mListener;
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    if(!value1 || !value2 || !value3)
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(param)
+    std::lock_guard<std::mutex> proplock{context->mPropLock};
+    switch(param)
     {
     case AL_POSITION:
         *value1 = static_cast<ALint>(listener.Position[0]);
         *value2 = static_cast<ALint>(listener.Position[1]);
         *value3 = static_cast<ALint>(listener.Position[2]);
-        break;
+        return;
 
     case AL_VELOCITY:
         *value1 = static_cast<ALint>(listener.Velocity[0]);
         *value2 = static_cast<ALint>(listener.Velocity[1]);
         *value3 = static_cast<ALint>(listener.Velocity[2]);
-        break;
-
-    default:
-        context->setError(AL_INVALID_ENUM, "Invalid listener 3-integer property");
+        return;
     }
+    throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-integer property 0x%x", param};
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values)
-START_API_FUNC
-{
+AL_API DECL_FUNC2(void, alGetListeneriv, ALenum,param, ALint*,values)
+FORCE_ALIGN void AL_APIENTRY alGetListenerivDirect(ALCcontext *context, ALenum param,
+    ALint *values) noexcept
+try {
+    if(!values)
+        throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
+
     switch(param)
     {
     case AL_POSITION:
     case AL_VELOCITY:
-        alGetListener3i(param, values+0, values+1, values+2);
+        auto vals = al::span<ALint,3>{values, 3_uz};
+        alGetListener3iDirect(context, param, &vals[0], &vals[1], &vals[2]);
         return;
     }
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
     ALlistener &listener = context->mListener;
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    if(!values)
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(param)
+    std::lock_guard<std::mutex> proplock{context->mPropLock};
+
+    static constexpr auto f2i = [](const float val) noexcept { return static_cast<ALint>(val); };
+    switch(param)
     {
     case AL_ORIENTATION:
+        auto vals = al::span<ALint,6>{values, 6_uz};
         // AT then UP
-        values[0] = static_cast<ALint>(listener.OrientAt[0]);
-        values[1] = static_cast<ALint>(listener.OrientAt[1]);
-        values[2] = static_cast<ALint>(listener.OrientAt[2]);
-        values[3] = static_cast<ALint>(listener.OrientUp[0]);
-        values[4] = static_cast<ALint>(listener.OrientUp[1]);
-        values[5] = static_cast<ALint>(listener.OrientUp[2]);
-        break;
-
-    default:
-        context->setError(AL_INVALID_ENUM, "Invalid listener integer-vector property");
+        std::transform(listener.OrientAt.cbegin(), listener.OrientAt.cend(), vals.begin(), f2i);
+        std::transform(listener.OrientUp.cbegin(), listener.OrientUp.cend(), vals.begin()+3, f2i);
+        return;
     }
+    throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer-vector property 0x%x",
+        param};
+}
+catch(al::context_error& e) {
+    context->setError(e.errorCode(), "%s", e.what());
 }
-END_API_FUNC

+ 1 - 3
Engine/lib/openal-soft/al/listener.h

@@ -3,8 +3,6 @@
 
 #include <array>
 
-#include "AL/al.h"
-#include "AL/alc.h"
 #include "AL/efx.h"
 
 #include "almalloc.h"
@@ -18,7 +16,7 @@ struct ALlistener {
     float Gain{1.0f};
     float mMetersPerUnit{AL_DEFAULT_METERS_PER_UNIT};
 
-    DISABLE_ALLOC()
+    DISABLE_ALLOC
 };
 
 #endif

File diff suppressed because it is too large
+ 969 - 1211
Engine/lib/openal-soft/al/source.cpp


+ 110 - 83
Engine/lib/openal-soft/al/source.h

@@ -2,26 +2,26 @@
 #define AL_SOURCE_H
 
 #include <array>
-#include <atomic>
 #include <cstddef>
-#include <iterator>
-#include <limits>
+#include <cstdint>
 #include <deque>
+#include <limits>
+#include <string_view>
+#include <utility>
 
 #include "AL/al.h"
 #include "AL/alc.h"
+#include "AL/alext.h"
 
-#include "alc/alu.h"
-#include "alc/context.h"
-#include "alc/inprogext.h"
-#include "aldeque.h"
 #include "almalloc.h"
+#include "alnumbers.h"
 #include "alnumeric.h"
-#include "atomic.h"
+#include "alspan.h"
+#include "core/context.h"
 #include "core/voice.h"
-#include "vector.h"
 
 #ifdef ALSOFT_EAX
+#include "eax/api.h"
 #include "eax/call.h"
 #include "eax/exception.h"
 #include "eax/fx_slot_index.h"
@@ -30,23 +30,23 @@
 
 struct ALbuffer;
 struct ALeffectslot;
-
+enum class Resampler : uint8_t;
 
 enum class SourceStereo : bool {
     Normal = AL_NORMAL_SOFT,
     Enhanced = AL_SUPER_STEREO_SOFT
 };
 
-#define DEFAULT_SENDS  2
+inline constexpr size_t DefaultSendCount{2};
 
-#define INVALID_VOICE_IDX static_cast<ALuint>(-1)
+inline constexpr ALuint InvalidVoiceIndex{std::numeric_limits<ALuint>::max()};
 
-extern bool sBufferSubDataCompat;
+inline bool sBufferSubDataCompat{false};
 
 struct ALbufferQueueItem : public VoiceBufferItem {
     ALbuffer *mBuffer{nullptr};
 
-    DISABLE_ALLOC()
+    DISABLE_ALLOC
 };
 
 
@@ -88,6 +88,7 @@ struct ALsource {
     DirectMode DirectChannels{DirectMode::Off};
     SpatializeMode mSpatialize{SpatializeMode::Auto};
     SourceStereo mStereoMode{SourceStereo::Normal};
+    bool mPanningEnabled{false};
 
     bool DryGainHFAuto{true};
     bool WetGainAuto{true};
@@ -105,24 +106,27 @@ struct ALsource {
 
     float Radius{0.0f};
     float EnhWidth{0.593f};
+    float mPan{0.0f};
 
     /** Direct filter and auxiliary send info. */
-    struct {
-        float Gain;
-        float GainHF;
-        float HFReference;
-        float GainLF;
-        float LFReference;
-    } Direct;
+    struct DirectData {
+        float Gain{};
+        float GainHF{};
+        float HFReference{};
+        float GainLF{};
+        float LFReference{};
+    };
+    DirectData Direct;
+
     struct SendData {
-        ALeffectslot *Slot;
-        float Gain;
-        float GainHF;
-        float HFReference;
-        float GainLF;
-        float LFReference;
+        ALeffectslot *Slot{};
+        float Gain{};
+        float GainHF{};
+        float HFReference{};
+        float GainLF{};
+        float LFReference{};
     };
-    std::array<SendData,MAX_SENDS> Send;
+    std::array<SendData,MaxSendCount> Send;
 
     /**
      * Last user-specified offset, and the offset type (bytes, samples, or
@@ -138,26 +142,28 @@ struct ALsource {
     ALenum state{AL_INITIAL};
 
     /** Source Buffer Queue head. */
-    al::deque<ALbufferQueueItem> mQueue;
+    std::deque<ALbufferQueueItem> mQueue;
 
     bool mPropsDirty{true};
 
     /* Index into the context's Voices array. Lazily updated, only checked and
      * reset when looking up the voice.
      */
-    ALuint VoiceIdx{INVALID_VOICE_IDX};
+    ALuint VoiceIdx{InvalidVoiceIndex};
 
     /** Self ID */
     ALuint id{0};
 
 
-    ALsource();
+    ALsource() noexcept;
     ~ALsource();
 
     ALsource(const ALsource&) = delete;
     ALsource& operator=(const ALsource&) = delete;
 
-    DISABLE_ALLOC()
+    static void SetName(ALCcontext *context, ALuint id, std::string_view name);
+
+    DISABLE_ALLOC
 
 #ifdef ALSOFT_EAX
 public:
@@ -171,18 +177,18 @@ public:
 private:
     using Exception = EaxSourceException;
 
-    static constexpr auto eax_max_speakers = 9;
+    static constexpr auto eax_max_speakers{9u};
 
-    using EaxFxSlotIds = const GUID* [EAX_MAX_FXSLOTS];
+    using EaxFxSlotIds = std::array<const GUID*,EAX_MAX_FXSLOTS>;
 
-    static constexpr const EaxFxSlotIds eax4_fx_slot_ids = {
+    static constexpr const EaxFxSlotIds eax4_fx_slot_ids{
         &EAXPROPERTYID_EAX40_FXSlot0,
         &EAXPROPERTYID_EAX40_FXSlot1,
         &EAXPROPERTYID_EAX40_FXSlot2,
         &EAXPROPERTYID_EAX40_FXSlot3,
     };
 
-    static constexpr const EaxFxSlotIds eax5_fx_slot_ids = {
+    static constexpr const EaxFxSlotIds eax5_fx_slot_ids{
         &EAXPROPERTYID_EAX50_FXSlot0,
         &EAXPROPERTYID_EAX50_FXSlot1,
         &EAXPROPERTYID_EAX50_FXSlot2,
@@ -215,11 +221,6 @@ private:
         Eax3Props source;
         EaxSends sends;
         EAX40ACTIVEFXSLOTS active_fx_slots;
-
-        bool operator==(const Eax4Props& rhs) noexcept
-        {
-            return std::memcmp(this, &rhs, sizeof(Eax4Props)) == 0;
-        }
     };
 
     struct Eax4State {
@@ -232,11 +233,6 @@ private:
         EaxSends sends;
         EAX50ACTIVEFXSLOTS active_fx_slots;
         EaxSpeakerLevels speaker_levels;
-
-        bool operator==(const Eax5Props& rhs) noexcept
-        {
-            return std::memcmp(this, &rhs, sizeof(Eax5Props)) == 0;
-        }
     };
 
     struct Eax5State {
@@ -546,7 +542,24 @@ private:
     struct Eax5SourceAllValidator {
         void operator()(const EAX50SOURCEPROPERTIES& props) const
         {
-            Eax3SourceAllValidator{}(static_cast<const Eax3Props&>(props));
+            Eax2SourceDirectValidator{}(props.lDirect);
+            Eax2SourceDirectHfValidator{}(props.lDirectHF);
+            Eax2SourceRoomValidator{}(props.lRoom);
+            Eax2SourceRoomHfValidator{}(props.lRoomHF);
+            Eax2SourceObstructionValidator{}(props.lObstruction);
+            Eax2SourceObstructionLfRatioValidator{}(props.flObstructionLFRatio);
+            Eax2SourceOcclusionValidator{}(props.lOcclusion);
+            Eax2SourceOcclusionLfRatioValidator{}(props.flOcclusionLFRatio);
+            Eax2SourceOcclusionRoomRatioValidator{}(props.flOcclusionRoomRatio);
+            Eax3SourceOcclusionDirectRatioValidator{}(props.flOcclusionDirectRatio);
+            Eax3SourceExclusionValidator{}(props.lExclusion);
+            Eax3SourceExclusionLfRatioValidator{}(props.flExclusionLFRatio);
+            Eax2SourceOutsideVolumeHfValidator{}(props.lOutsideVolumeHF);
+            Eax3SourceDopplerFactorValidator{}(props.flDopplerFactor);
+            Eax3SourceRolloffFactorValidator{}(props.flRolloffFactor);
+            Eax2SourceRoomRolloffFactorValidator{}(props.flRoomRolloffFactor);
+            Eax2SourceAirAbsorptionFactorValidator{}(props.flAirAbsorptionFactor);
+            Eax5SourceFlagsValidator{}(props.ulFlags);
             Eax5SourceMacroFXFactorValidator{}(props.flMacroFXFactor);
         }
     };
@@ -809,39 +822,38 @@ private:
     [[noreturn]] static void eax_fail_unknown_active_fx_slot_id();
     [[noreturn]] static void eax_fail_unknown_receiving_fx_slot_id();
 
-    void eax_set_sends_defaults(EaxSends& sends, const EaxFxSlotIds& ids) noexcept;
-    void eax1_set_defaults(Eax1Props& props) noexcept;
+    static void eax_set_sends_defaults(EaxSends& sends, const EaxFxSlotIds& ids) noexcept;
+    static void eax1_set_defaults(Eax1Props& props) noexcept;
     void eax1_set_defaults() noexcept;
-    void eax2_set_defaults(Eax2Props& props) noexcept;
+    static void eax2_set_defaults(Eax2Props& props) noexcept;
     void eax2_set_defaults() noexcept;
-    void eax3_set_defaults(Eax3Props& props) noexcept;
+    static void eax3_set_defaults(Eax3Props& props) noexcept;
     void eax3_set_defaults() noexcept;
-    void eax4_set_sends_defaults(EaxSends& sends) noexcept;
-    void eax4_set_active_fx_slots_defaults(EAX40ACTIVEFXSLOTS& slots) noexcept;
+    static void eax4_set_sends_defaults(EaxSends& sends) noexcept;
+    static void eax4_set_active_fx_slots_defaults(EAX40ACTIVEFXSLOTS& slots) noexcept;
     void eax4_set_defaults() noexcept;
-    void eax5_set_source_defaults(EAX50SOURCEPROPERTIES& props) noexcept;
-    void eax5_set_sends_defaults(EaxSends& sends) noexcept;
-    void eax5_set_active_fx_slots_defaults(EAX50ACTIVEFXSLOTS& slots) noexcept;
-    void eax5_set_speaker_levels_defaults(EaxSpeakerLevels& speaker_levels) noexcept;
-    void eax5_set_defaults(Eax5Props& props) noexcept;
+    static void eax5_set_source_defaults(EAX50SOURCEPROPERTIES& props) noexcept;
+    static void eax5_set_sends_defaults(EaxSends& sends) noexcept;
+    static void eax5_set_active_fx_slots_defaults(EAX50ACTIVEFXSLOTS& slots) noexcept;
+    static void eax5_set_speaker_levels_defaults(EaxSpeakerLevels& speaker_levels) noexcept;
+    static void eax5_set_defaults(Eax5Props& props) noexcept;
     void eax5_set_defaults() noexcept;
     void eax_set_defaults() noexcept;
 
-    void eax1_translate(const Eax1Props& src, Eax5Props& dst) noexcept;
-    void eax2_translate(const Eax2Props& src, Eax5Props& dst) noexcept;
-    void eax3_translate(const Eax3Props& src, Eax5Props& dst) noexcept;
-    void eax4_translate(const Eax4Props& src, Eax5Props& dst) noexcept;
+    static void eax1_translate(const Eax1Props& src, Eax5Props& dst) noexcept;
+    static void eax2_translate(const Eax2Props& src, Eax5Props& dst) noexcept;
+    static void eax3_translate(const Eax3Props& src, Eax5Props& dst) noexcept;
+    static void eax4_translate(const Eax4Props& src, Eax5Props& dst) noexcept;
 
     static float eax_calculate_dst_occlusion_mb(
         long src_occlusion_mb,
         float path_ratio,
         float lf_ratio) noexcept;
 
-    EaxAlLowPassParam eax_create_direct_filter_param() const noexcept;
+    [[nodiscard]] auto eax_create_direct_filter_param() const noexcept -> EaxAlLowPassParam;
 
-    EaxAlLowPassParam eax_create_room_filter_param(
-        const ALeffectslot& fx_slot,
-        const EAXSOURCEALLSENDPROPERTIES& send) const noexcept;
+    [[nodiscard]] auto eax_create_room_filter_param(const ALeffectslot& fx_slot,
+        const EAXSOURCEALLSENDPROPERTIES& send) const noexcept -> EaxAlLowPassParam;
 
     void eax_update_direct_filter();
     void eax_update_room_filters();
@@ -894,16 +906,16 @@ private:
         }
     }
 
-    void eax_get_active_fx_slot_id(const EaxCall& call, const GUID* ids, size_t max_count);
-    void eax1_get(const EaxCall& call, const Eax1Props& props);
-    void eax2_get(const EaxCall& call, const Eax2Props& props);
-    void eax3_get_obstruction(const EaxCall& call, const Eax3Props& props);
-    void eax3_get_occlusion(const EaxCall& call, const Eax3Props& props);
-    void eax3_get_exclusion(const EaxCall& call, const Eax3Props& props);
-    void eax3_get(const EaxCall& call, const Eax3Props& props);
+    static void eax_get_active_fx_slot_id(const EaxCall& call, const al::span<const GUID> src_ids);
+    static void eax1_get(const EaxCall& call, const Eax1Props& props);
+    static void eax2_get(const EaxCall& call, const Eax2Props& props);
+    static void eax3_get_obstruction(const EaxCall& call, const Eax3Props& props);
+    static void eax3_get_occlusion(const EaxCall& call, const Eax3Props& props);
+    static void eax3_get_exclusion(const EaxCall& call, const Eax3Props& props);
+    static void eax3_get(const EaxCall& call, const Eax3Props& props);
     void eax4_get(const EaxCall& call, const Eax4Props& props);
-    void eax5_get_all_2d(const EaxCall& call, const EAX50SOURCEPROPERTIES& props);
-    void eax5_get_speaker_levels(const EaxCall& call, const EaxSpeakerLevels& props);
+    static void eax5_get_all_2d(const EaxCall& call, const EAX50SOURCEPROPERTIES& props);
+    static void eax5_get_speaker_levels(const EaxCall& call, const EaxSpeakerLevels& props);
     void eax5_get(const EaxCall& call, const Eax5Props& props);
     void eax_get(const EaxCall& call);
 
@@ -976,21 +988,21 @@ private:
     }
 
     template<typename TValidator, size_t TIdCount>
-    void eax_defer_active_fx_slot_id(const EaxCall& call, GUID (&dst_ids)[TIdCount])
+    void eax_defer_active_fx_slot_id(const EaxCall& call, const al::span<GUID,TIdCount> dst_ids)
     {
         const auto src_ids = call.get_values<const GUID>(TIdCount);
         std::for_each(src_ids.cbegin(), src_ids.cend(), TValidator{});
-        std::uninitialized_copy(src_ids.cbegin(), src_ids.cend(), dst_ids);
+        std::uninitialized_copy(src_ids.cbegin(), src_ids.cend(), dst_ids.begin());
     }
 
     template<size_t TIdCount>
-    void eax4_defer_active_fx_slot_id(const EaxCall& call, GUID (&dst_ids)[TIdCount])
+    void eax4_defer_active_fx_slot_id(const EaxCall& call, const al::span<GUID,TIdCount> dst_ids)
     {
         eax_defer_active_fx_slot_id<Eax4ActiveFxSlotIdValidator>(call, dst_ids);
     }
 
     template<size_t TIdCount>
-    void eax5_defer_active_fx_slot_id(const EaxCall& call, GUID (&dst_ids)[TIdCount])
+    void eax5_defer_active_fx_slot_id(const EaxCall& call, const al::span<GUID,TIdCount> dst_ids)
     {
         eax_defer_active_fx_slot_id<Eax5ActiveFxSlotIdValidator>(call, dst_ids);
     }
@@ -1022,12 +1034,12 @@ private:
     void eax_set_efx_wet_gain_auto();
     void eax_set_efx_wet_gain_hf_auto();
 
-    void eax1_set(const EaxCall& call, Eax1Props& props);
-    void eax2_set(const EaxCall& call, Eax2Props& props);
+    static void eax1_set(const EaxCall& call, Eax1Props& props);
+    static void eax2_set(const EaxCall& call, Eax2Props& props);
     void eax3_set(const EaxCall& call, Eax3Props& props);
     void eax4_set(const EaxCall& call, Eax4Props& props);
-    void eax5_defer_all_2d(const EaxCall& call, EAX50SOURCEPROPERTIES& props);
-    void eax5_defer_speaker_levels(const EaxCall& call, EaxSpeakerLevels& props);
+    static void eax5_defer_all_2d(const EaxCall& call, EAX50SOURCEPROPERTIES& props);
+    static void eax5_defer_speaker_levels(const EaxCall& call, EaxSpeakerLevels& props);
     void eax5_set(const EaxCall& call, Eax5Props& props);
     void eax_set(const EaxCall& call);
 
@@ -1041,4 +1053,19 @@ private:
 
 void UpdateAllSourceProps(ALCcontext *context);
 
+struct SourceSubList {
+    uint64_t FreeMask{~0_u64};
+    gsl::owner<std::array<ALsource,64>*> Sources{nullptr};
+
+    SourceSubList() noexcept = default;
+    SourceSubList(const SourceSubList&) = delete;
+    SourceSubList(SourceSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Sources{rhs.Sources}
+    { rhs.FreeMask = ~0_u64; rhs.Sources = nullptr; }
+    ~SourceSubList();
+
+    SourceSubList& operator=(const SourceSubList&) = delete;
+    SourceSubList& operator=(SourceSubList&& rhs) noexcept
+    { std::swap(FreeMask, rhs.FreeMask); std::swap(Sources, rhs.Sources); return *this; }
+};
+
 #endif

+ 330 - 666
Engine/lib/openal-soft/al/state.cpp

@@ -22,26 +22,32 @@
 
 #include "version.h"
 
+#include <array>
 #include <atomic>
 #include <cmath>
+#include <deque>
 #include <mutex>
+#include <optional>
 #include <stdexcept>
 #include <string>
+#include <utility>
 
 #include "AL/al.h"
 #include "AL/alc.h"
 #include "AL/alext.h"
 
+#include "al/debug.h"
+#include "al/listener.h"
 #include "alc/alu.h"
 #include "alc/context.h"
 #include "alc/inprogext.h"
 #include "alnumeric.h"
-#include "aloptional.h"
 #include "atomic.h"
 #include "core/context.h"
-#include "core/except.h"
+#include "core/logging.h"
 #include "core/mixer/defs.h"
 #include "core/voice.h"
+#include "direct_defs.h"
 #include "intrusive_ptr.h"
 #include "opthelpers.h"
 #include "strutils.h"
@@ -56,17 +62,19 @@
 
 namespace {
 
-constexpr ALchar alVendor[] = "OpenAL Community";
-constexpr ALchar alVersion[] = "1.1 ALSOFT " ALSOFT_VERSION;
-constexpr ALchar alRenderer[] = "OpenAL Soft";
+[[nodiscard]] constexpr auto GetVendorString() noexcept { return "OpenAL Community"; }
+[[nodiscard]] constexpr auto GetVersionString() noexcept { return "1.1 ALSOFT " ALSOFT_VERSION; }
+[[nodiscard]] constexpr auto GetRendererString() noexcept { return "OpenAL Soft"; }
 
-// Error Messages
-constexpr ALchar alNoError[] = "No Error";
-constexpr ALchar alErrInvalidName[] = "Invalid Name";
-constexpr ALchar alErrInvalidEnum[] = "Invalid Enum";
-constexpr ALchar alErrInvalidValue[] = "Invalid Value";
-constexpr ALchar alErrInvalidOp[] = "Invalid Operation";
-constexpr ALchar alErrOutOfMemory[] = "Out of Memory";
+/* Error Messages */
+[[nodiscard]] constexpr auto GetNoErrorString() noexcept { return "No Error"; }
+[[nodiscard]] constexpr auto GetInvalidNameString() noexcept { return "Invalid Name"; }
+[[nodiscard]] constexpr auto GetInvalidEnumString() noexcept { return "Invalid Enum"; }
+[[nodiscard]] constexpr auto GetInvalidValueString() noexcept { return "Invalid Value"; }
+[[nodiscard]] constexpr auto GetInvalidOperationString() noexcept { return "Invalid Operation"; }
+[[nodiscard]] constexpr auto GetOutOfMemoryString() noexcept { return "Out of Memory"; }
+[[nodiscard]] constexpr auto GetStackOverflowString() noexcept { return "Stack Overflow"; }
+[[nodiscard]] constexpr auto GetStackUnderflowString() noexcept { return "Stack Underflow"; }
 
 /* Resampler strings */
 template<Resampler rtype> struct ResamplerName { };
@@ -74,8 +82,10 @@ template<> struct ResamplerName<Resampler::Point>
 { static constexpr const ALchar *Get() noexcept { return "Nearest"; } };
 template<> struct ResamplerName<Resampler::Linear>
 { static constexpr const ALchar *Get() noexcept { return "Linear"; } };
-template<> struct ResamplerName<Resampler::Cubic>
-{ static constexpr const ALchar *Get() noexcept { return "Cubic"; } };
+template<> struct ResamplerName<Resampler::Spline>
+{ static constexpr const ALchar *Get() noexcept { return "Cubic Spline"; } };
+template<> struct ResamplerName<Resampler::Gaussian>
+{ static constexpr const ALchar *Get() noexcept { return "4-point Gaussian"; } };
 template<> struct ResamplerName<Resampler::FastBSinc12>
 { static constexpr const ALchar *Get() noexcept { return "11th order Sinc (fast)"; } };
 template<> struct ResamplerName<Resampler::BSinc12>
@@ -92,7 +102,8 @@ const ALchar *GetResamplerName(const Resampler rtype)
     {
     HANDLE_RESAMPLER(Resampler::Point);
     HANDLE_RESAMPLER(Resampler::Linear);
-    HANDLE_RESAMPLER(Resampler::Cubic);
+    HANDLE_RESAMPLER(Resampler::Spline);
+    HANDLE_RESAMPLER(Resampler::Gaussian);
     HANDLE_RESAMPLER(Resampler::FastBSinc12);
     HANDLE_RESAMPLER(Resampler::BSinc12);
     HANDLE_RESAMPLER(Resampler::FastBSinc24);
@@ -103,7 +114,7 @@ const ALchar *GetResamplerName(const Resampler rtype)
     throw std::runtime_error{"Unexpected resampler index"};
 }
 
-al::optional<DistanceModel> DistanceModelFromALenum(ALenum model)
+constexpr auto DistanceModelFromALenum(ALenum model) noexcept -> std::optional<DistanceModel>
 {
     switch(model)
     {
@@ -115,9 +126,9 @@ al::optional<DistanceModel> DistanceModelFromALenum(ALenum model)
     case AL_EXPONENT_DISTANCE: return DistanceModel::Exponent;
     case AL_EXPONENT_DISTANCE_CLAMPED: return DistanceModel::ExponentClamped;
     }
-    return al::nullopt;
+    return std::nullopt;
 }
-ALenum ALenumFromDistanceModel(DistanceModel model)
+constexpr auto ALenumFromDistanceModel(DistanceModel model) -> ALenum
 {
     switch(model)
     {
@@ -132,810 +143,463 @@ ALenum ALenumFromDistanceModel(DistanceModel model)
     throw std::runtime_error{"Unexpected distance model "+std::to_string(static_cast<int>(model))};
 }
 
-} // namespace
-
-/* WARNING: Non-standard export! Not part of any extension, or exposed in the
- * alcFunctions list.
- */
-AL_API const ALchar* AL_APIENTRY alsoft_get_version(void)
-START_API_FUNC
-{
-    static const auto spoof = al::getenv("ALSOFT_SPOOF_VERSION");
-    if(spoof) return spoof->c_str();
-    return ALSOFT_VERSION;
-}
-END_API_FUNC
-
-#define DO_UPDATEPROPS() do {                                                 \
-    if(!context->mDeferUpdates)                                               \
-        UpdateContextProps(context.get());                                    \
-    else                                                                      \
-        context->mPropsDirty = true;                                          \
-} while(0)
-
-
-AL_API void AL_APIENTRY alEnable(ALenum capability)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    switch(capability)
-    {
-    case AL_SOURCE_DISTANCE_MODEL:
-        {
-            std::lock_guard<std::mutex> _{context->mPropLock};
-            context->mSourceDistanceModel = true;
-            DO_UPDATEPROPS();
-        }
-        break;
-
-    case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
-        context->setError(AL_INVALID_OPERATION, "Re-enabling AL_STOP_SOURCES_ON_DISCONNECT_SOFT not yet supported");
-        break;
-
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid enable property 0x%04x", capability);
-    }
-}
-END_API_FUNC
-
-AL_API void AL_APIENTRY alDisable(ALenum capability)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    switch(capability)
-    {
-    case AL_SOURCE_DISTANCE_MODEL:
-        {
-            std::lock_guard<std::mutex> _{context->mPropLock};
-            context->mSourceDistanceModel = false;
-            DO_UPDATEPROPS();
-        }
-        break;
-
-    case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
-        context->mStopVoicesOnDisconnect = false;
-        break;
-
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid disable property 0x%04x", capability);
-    }
-}
-END_API_FUNC
-
-AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return AL_FALSE;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    ALboolean value{AL_FALSE};
-    switch(capability)
-    {
-    case AL_SOURCE_DISTANCE_MODEL:
-        value = context->mSourceDistanceModel ? AL_TRUE : AL_FALSE;
-        break;
-
-    case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
-        value = context->mStopVoicesOnDisconnect ? AL_TRUE : AL_FALSE;
-        break;
-
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid is enabled property 0x%04x", capability);
-    }
-
-    return value;
-}
-END_API_FUNC
-
-AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum pname)
-START_API_FUNC
+enum PropertyValue : ALenum {
+    DopplerFactor = AL_DOPPLER_FACTOR,
+    DopplerVelocity = AL_DOPPLER_VELOCITY,
+    DistanceModel = AL_DISTANCE_MODEL,
+    SpeedOfSound = AL_SPEED_OF_SOUND,
+    DeferredUpdates = AL_DEFERRED_UPDATES_SOFT,
+    GainLimit = AL_GAIN_LIMIT_SOFT,
+    NumResamplers = AL_NUM_RESAMPLERS_SOFT,
+    DefaultResampler = AL_DEFAULT_RESAMPLER_SOFT,
+    DebugLoggedMessages = AL_DEBUG_LOGGED_MESSAGES_EXT,
+    DebugNextLoggedMessageLength = AL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_EXT,
+    MaxDebugMessageLength = AL_MAX_DEBUG_MESSAGE_LENGTH_EXT,
+    MaxDebugLoggedMessages = AL_MAX_DEBUG_LOGGED_MESSAGES_EXT,
+    MaxDebugGroupDepth = AL_MAX_DEBUG_GROUP_STACK_DEPTH_EXT,
+    MaxLabelLength = AL_MAX_LABEL_LENGTH_EXT,
+    ContextFlags = AL_CONTEXT_FLAGS_EXT,
+#ifdef ALSOFT_EAX
+    EaxRamSize = AL_EAX_RAM_SIZE,
+    EaxRamFree = AL_EAX_RAM_FREE,
+#endif
+};
+
+template<typename T>
+struct PropertyCastType {
+    template<typename U>
+    constexpr auto operator()(U&& value) const noexcept
+    { return static_cast<T>(std::forward<U>(value)); }
+};
+/* Special-case ALboolean to be an actual bool instead of a char type. */
+template<>
+struct PropertyCastType<ALboolean> {
+    template<typename U>
+    constexpr ALboolean operator()(U&& value) const noexcept
+    { return static_cast<bool>(std::forward<U>(value)) ? AL_TRUE : AL_FALSE; }
+};
+
+
+template<typename T>
+void GetValue(ALCcontext *context, ALenum pname, T *values)
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return AL_FALSE;
+    auto cast_value = PropertyCastType<T>{};
 
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    ALboolean value{AL_FALSE};
-    switch(pname)
+    switch(static_cast<PropertyValue>(pname))
     {
     case AL_DOPPLER_FACTOR:
-        if(context->mDopplerFactor != 0.0f)
-            value = AL_TRUE;
-        break;
+        *values = cast_value(context->mDopplerFactor);
+        return;
 
     case AL_DOPPLER_VELOCITY:
-        if(context->mDopplerVelocity != 0.0f)
-            value = AL_TRUE;
-        break;
-
-    case AL_DISTANCE_MODEL:
-        if(context->mDistanceModel == DistanceModel::Default)
-            value = AL_TRUE;
-        break;
+        if(context->mContextFlags.test(ContextFlags::DebugBit)) UNLIKELY
+            context->debugMessage(DebugSource::API, DebugType::DeprecatedBehavior, 0,
+                DebugSeverity::Medium,
+                "AL_DOPPLER_VELOCITY is deprecated in AL 1.1, use AL_SPEED_OF_SOUND; "
+                "AL_DOPPLER_VELOCITY -> AL_SPEED_OF_SOUND / 343.3f");
+        *values = cast_value(context->mDopplerVelocity);
+        return;
 
     case AL_SPEED_OF_SOUND:
-        if(context->mSpeedOfSound != 0.0f)
-            value = AL_TRUE;
-        break;
-
-    case AL_DEFERRED_UPDATES_SOFT:
-        if(context->mDeferUpdates)
-            value = AL_TRUE;
-        break;
+        *values = cast_value(context->mSpeedOfSound);
+        return;
 
     case AL_GAIN_LIMIT_SOFT:
-        if(GainMixMax/context->mGainBoost != 0.0f)
-            value = AL_TRUE;
-        break;
-
-    case AL_NUM_RESAMPLERS_SOFT:
-        /* Always non-0. */
-        value = AL_TRUE;
-        break;
-
-    case AL_DEFAULT_RESAMPLER_SOFT:
-        value = static_cast<int>(ResamplerDefault) ? AL_TRUE : AL_FALSE;
-        break;
-
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid boolean property 0x%04x", pname);
-    }
-
-    return value;
-}
-END_API_FUNC
-
-AL_API ALdouble AL_APIENTRY alGetDouble(ALenum pname)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return 0.0;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    ALdouble value{0.0};
-    switch(pname)
-    {
-    case AL_DOPPLER_FACTOR:
-        value = context->mDopplerFactor;
-        break;
-
-    case AL_DOPPLER_VELOCITY:
-        value = context->mDopplerVelocity;
-        break;
-
-    case AL_DISTANCE_MODEL:
-        value = static_cast<ALdouble>(ALenumFromDistanceModel(context->mDistanceModel));
-        break;
-
-    case AL_SPEED_OF_SOUND:
-        value = context->mSpeedOfSound;
-        break;
+        *values = cast_value(GainMixMax / context->mGainBoost);
+        return;
 
     case AL_DEFERRED_UPDATES_SOFT:
-        if(context->mDeferUpdates)
-            value = static_cast<ALdouble>(AL_TRUE);
-        break;
-
-    case AL_GAIN_LIMIT_SOFT:
-        value = ALdouble{GainMixMax}/context->mGainBoost;
-        break;
-
-    case AL_NUM_RESAMPLERS_SOFT:
-        value = static_cast<ALdouble>(Resampler::Max) + 1.0;
-        break;
-
-    case AL_DEFAULT_RESAMPLER_SOFT:
-        value = static_cast<ALdouble>(ResamplerDefault);
-        break;
-
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid double property 0x%04x", pname);
-    }
-
-    return value;
-}
-END_API_FUNC
-
-AL_API ALfloat AL_APIENTRY alGetFloat(ALenum pname)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return 0.0f;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    ALfloat value{0.0f};
-    switch(pname)
-    {
-    case AL_DOPPLER_FACTOR:
-        value = context->mDopplerFactor;
-        break;
-
-    case AL_DOPPLER_VELOCITY:
-        value = context->mDopplerVelocity;
-        break;
+        *values = cast_value(context->mDeferUpdates ? AL_TRUE : AL_FALSE);
+        return;
 
     case AL_DISTANCE_MODEL:
-        value = static_cast<ALfloat>(ALenumFromDistanceModel(context->mDistanceModel));
-        break;
-
-    case AL_SPEED_OF_SOUND:
-        value = context->mSpeedOfSound;
-        break;
-
-    case AL_DEFERRED_UPDATES_SOFT:
-        if(context->mDeferUpdates)
-            value = static_cast<ALfloat>(AL_TRUE);
-        break;
-
-    case AL_GAIN_LIMIT_SOFT:
-        value = GainMixMax/context->mGainBoost;
-        break;
+        *values = cast_value(ALenumFromDistanceModel(context->mDistanceModel));
+        return;
 
     case AL_NUM_RESAMPLERS_SOFT:
-        value = static_cast<ALfloat>(Resampler::Max) + 1.0f;
-        break;
+        *values = cast_value(al::to_underlying(Resampler::Max) + 1);
+        return;
 
     case AL_DEFAULT_RESAMPLER_SOFT:
-        value = static_cast<ALfloat>(ResamplerDefault);
-        break;
+        *values = cast_value(al::to_underlying(ResamplerDefault));
+        return;
 
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid float property 0x%04x", pname);
+    case AL_DEBUG_LOGGED_MESSAGES_EXT:
+    {
+        std::lock_guard<std::mutex> debuglock{context->mDebugCbLock};
+        *values = cast_value(context->mDebugLog.size());
+        return;
     }
 
-    return value;
-}
-END_API_FUNC
-
-AL_API ALint AL_APIENTRY alGetInteger(ALenum pname)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return 0;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    ALint value{0};
-    switch(pname)
+    case AL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_EXT:
     {
-    case AL_DOPPLER_FACTOR:
-        value = static_cast<ALint>(context->mDopplerFactor);
-        break;
-
-    case AL_DOPPLER_VELOCITY:
-        value = static_cast<ALint>(context->mDopplerVelocity);
-        break;
-
-    case AL_DISTANCE_MODEL:
-        value = ALenumFromDistanceModel(context->mDistanceModel);
-        break;
+        std::lock_guard<std::mutex> debuglock{context->mDebugCbLock};
+        *values = cast_value(context->mDebugLog.empty() ? 0_uz
+            : (context->mDebugLog.front().mMessage.size()+1));
+        return;
+    }
 
-    case AL_SPEED_OF_SOUND:
-        value = static_cast<ALint>(context->mSpeedOfSound);
-        break;
+    case AL_MAX_DEBUG_MESSAGE_LENGTH_EXT:
+        *values = cast_value(MaxDebugMessageLength);
+        return;
 
-    case AL_DEFERRED_UPDATES_SOFT:
-        if(context->mDeferUpdates)
-            value = AL_TRUE;
-        break;
+    case AL_MAX_DEBUG_LOGGED_MESSAGES_EXT:
+        *values = cast_value(MaxDebugLoggedMessages);
+        return;
 
-    case AL_GAIN_LIMIT_SOFT:
-        value = static_cast<ALint>(GainMixMax/context->mGainBoost);
-        break;
+    case AL_MAX_DEBUG_GROUP_STACK_DEPTH_EXT:
+        *values = cast_value(MaxDebugGroupDepth);
+        return;
 
-    case AL_NUM_RESAMPLERS_SOFT:
-        value = static_cast<int>(Resampler::Max) + 1;
-        break;
+    case AL_MAX_LABEL_LENGTH_EXT:
+        *values = cast_value(MaxObjectLabelLength);
+        return;
 
-    case AL_DEFAULT_RESAMPLER_SOFT:
-        value = static_cast<int>(ResamplerDefault);
-        break;
+    case AL_CONTEXT_FLAGS_EXT:
+        *values = cast_value(context->mContextFlags.to_ulong());
+        return;
 
 #ifdef ALSOFT_EAX
-
-#define EAX_ERROR "[alGetInteger] EAX not enabled."
+#define EAX_ERROR "[alGetInteger] EAX not enabled"
 
     case AL_EAX_RAM_SIZE:
-        if (eax_g_is_enabled)
-        {
-            value = eax_x_ram_max_size;
-        }
-        else
+        if(eax_g_is_enabled)
         {
-            context->setError(AL_INVALID_VALUE, EAX_ERROR);
+            *values = cast_value(eax_x_ram_max_size);
+            return;
         }
-
+        ERR(EAX_ERROR "\n");
         break;
 
     case AL_EAX_RAM_FREE:
-        if (eax_g_is_enabled)
+        if(eax_g_is_enabled)
         {
             auto device = context->mALDevice.get();
             std::lock_guard<std::mutex> device_lock{device->BufferLock};
-
-            value = static_cast<ALint>(device->eax_x_ram_free_size);
+            *values = cast_value(device->eax_x_ram_free_size);
+            return;
         }
-        else
-        {
-            context->setError(AL_INVALID_VALUE, EAX_ERROR);
-        }
-
+        ERR(EAX_ERROR "\n");
         break;
 
 #undef EAX_ERROR
-
 #endif // ALSOFT_EAX
-
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid integer property 0x%04x", pname);
     }
-
-    return value;
+    context->setError(AL_INVALID_ENUM, "Invalid context property 0x%04x", pname);
 }
-END_API_FUNC
 
-AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname)
-START_API_FUNC
-{
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return 0_i64;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    ALint64SOFT value{0};
-    switch(pname)
-    {
-    case AL_DOPPLER_FACTOR:
-        value = static_cast<ALint64SOFT>(context->mDopplerFactor);
-        break;
-
-    case AL_DOPPLER_VELOCITY:
-        value = static_cast<ALint64SOFT>(context->mDopplerVelocity);
-        break;
 
-    case AL_DISTANCE_MODEL:
-        value = ALenumFromDistanceModel(context->mDistanceModel);
-        break;
-
-    case AL_SPEED_OF_SOUND:
-        value = static_cast<ALint64SOFT>(context->mSpeedOfSound);
-        break;
-
-    case AL_DEFERRED_UPDATES_SOFT:
-        if(context->mDeferUpdates)
-            value = AL_TRUE;
-        break;
-
-    case AL_GAIN_LIMIT_SOFT:
-        value = static_cast<ALint64SOFT>(GainMixMax/context->mGainBoost);
-        break;
-
-    case AL_NUM_RESAMPLERS_SOFT:
-        value = static_cast<ALint64SOFT>(Resampler::Max) + 1;
-        break;
-
-    case AL_DEFAULT_RESAMPLER_SOFT:
-        value = static_cast<ALint64SOFT>(ResamplerDefault);
-        break;
-
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid integer64 property 0x%04x", pname);
-    }
-
-    return value;
-}
-END_API_FUNC
-
-AL_API ALvoid* AL_APIENTRY alGetPointerSOFT(ALenum pname)
-START_API_FUNC
+inline void UpdateProps(ALCcontext *context)
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return nullptr;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
-    void *value{nullptr};
-    switch(pname)
-    {
-    case AL_EVENT_CALLBACK_FUNCTION_SOFT:
-        value = reinterpret_cast<void*>(context->mEventCb);
-        break;
-
-    case AL_EVENT_CALLBACK_USER_PARAM_SOFT:
-        value = context->mEventParam;
-        break;
+    if(!context->mDeferUpdates)
+        UpdateContextProps(context);
+    else
+        context->mPropsDirty = true;
+}
 
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid pointer property 0x%04x", pname);
-    }
+} // namespace
 
-    return value;
+/* WARNING: Non-standard export! Not part of any extension, or exposed in the
+ * alcFunctions list.
+ */
+AL_API auto AL_APIENTRY alsoft_get_version() noexcept -> const ALchar*
+{
+    static const auto spoof = al::getenv("ALSOFT_SPOOF_VERSION");
+    if(spoof) return spoof->c_str();
+    return ALSOFT_VERSION;
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alGetBooleanv(ALenum pname, ALboolean *values)
-START_API_FUNC
+
+AL_API DECL_FUNC1(void, alEnable, ALenum,capability)
+FORCE_ALIGN void AL_APIENTRY alEnableDirect(ALCcontext *context, ALenum capability) noexcept
 {
-    if(values)
+    switch(capability)
     {
-        switch(pname)
+    case AL_SOURCE_DISTANCE_MODEL:
         {
-            case AL_DOPPLER_FACTOR:
-            case AL_DOPPLER_VELOCITY:
-            case AL_DISTANCE_MODEL:
-            case AL_SPEED_OF_SOUND:
-            case AL_DEFERRED_UPDATES_SOFT:
-            case AL_GAIN_LIMIT_SOFT:
-            case AL_NUM_RESAMPLERS_SOFT:
-            case AL_DEFAULT_RESAMPLER_SOFT:
-                values[0] = alGetBoolean(pname);
-                return;
+            std::lock_guard<std::mutex> proplock{context->mPropLock};
+            context->mSourceDistanceModel = true;
+            UpdateProps(context);
         }
-    }
+        return;
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+    case AL_DEBUG_OUTPUT_EXT:
+        context->mDebugEnabled.store(true);
+        return;
 
-    if(!values)
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(pname)
-    {
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid boolean-vector property 0x%04x", pname);
+    case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
+        context->setError(AL_INVALID_OPERATION, "Re-enabling AL_STOP_SOURCES_ON_DISCONNECT_SOFT not yet supported");
+        return;
     }
+    context->setError(AL_INVALID_VALUE, "Invalid enable property 0x%04x", capability);
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alGetDoublev(ALenum pname, ALdouble *values)
-START_API_FUNC
+AL_API DECL_FUNC1(void, alDisable, ALenum,capability)
+FORCE_ALIGN void AL_APIENTRY alDisableDirect(ALCcontext *context, ALenum capability) noexcept
 {
-    if(values)
+    switch(capability)
     {
-        switch(pname)
+    case AL_SOURCE_DISTANCE_MODEL:
         {
-            case AL_DOPPLER_FACTOR:
-            case AL_DOPPLER_VELOCITY:
-            case AL_DISTANCE_MODEL:
-            case AL_SPEED_OF_SOUND:
-            case AL_DEFERRED_UPDATES_SOFT:
-            case AL_GAIN_LIMIT_SOFT:
-            case AL_NUM_RESAMPLERS_SOFT:
-            case AL_DEFAULT_RESAMPLER_SOFT:
-                values[0] = alGetDouble(pname);
-                return;
+            std::lock_guard<std::mutex> proplock{context->mPropLock};
+            context->mSourceDistanceModel = false;
+            UpdateProps(context);
         }
-    }
+        return;
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+    case AL_DEBUG_OUTPUT_EXT:
+        context->mDebugEnabled.store(false);
+        return;
 
-    if(!values)
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(pname)
-    {
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid double-vector property 0x%04x", pname);
+    case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
+        context->mStopVoicesOnDisconnect.store(false);
+        return;
     }
+    context->setError(AL_INVALID_VALUE, "Invalid disable property 0x%04x", capability);
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alGetFloatv(ALenum pname, ALfloat *values)
-START_API_FUNC
+AL_API DECL_FUNC1(ALboolean, alIsEnabled, ALenum,capability)
+FORCE_ALIGN ALboolean AL_APIENTRY alIsEnabledDirect(ALCcontext *context, ALenum capability) noexcept
 {
-    if(values)
+    std::lock_guard<std::mutex> proplock{context->mPropLock};
+    switch(capability)
     {
-        switch(pname)
-        {
-            case AL_DOPPLER_FACTOR:
-            case AL_DOPPLER_VELOCITY:
-            case AL_DISTANCE_MODEL:
-            case AL_SPEED_OF_SOUND:
-            case AL_DEFERRED_UPDATES_SOFT:
-            case AL_GAIN_LIMIT_SOFT:
-            case AL_NUM_RESAMPLERS_SOFT:
-            case AL_DEFAULT_RESAMPLER_SOFT:
-                values[0] = alGetFloat(pname);
-                return;
-        }
+    case AL_SOURCE_DISTANCE_MODEL: return context->mSourceDistanceModel ? AL_TRUE : AL_FALSE;
+    case AL_DEBUG_OUTPUT_EXT: return context->mDebugEnabled ? AL_TRUE : AL_FALSE;
+    case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
+        return context->mStopVoicesOnDisconnect.load() ? AL_TRUE : AL_FALSE;
     }
+    context->setError(AL_INVALID_VALUE, "Invalid is enabled property 0x%04x", capability);
+    return AL_FALSE;
+}
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    if(!values)
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(pname)
-    {
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid float-vector property 0x%04x", pname);
-    }
+#define DECL_GETFUNC(R, Name, Ext)                                            \
+AL_API auto AL_APIENTRY Name##Ext(ALenum pname) noexcept -> R                 \
+{                                                                             \
+    R value{};                                                                \
+    auto context = GetContextRef();                                           \
+    if(!context) UNLIKELY return value;                                       \
+    Name##vDirect##Ext(GetContextRef().get(), pname, &value);                 \
+    return value;                                                             \
+}                                                                             \
+FORCE_ALIGN auto AL_APIENTRY Name##Direct##Ext(ALCcontext *context, ALenum pname) noexcept -> R \
+{                                                                             \
+    R value{};                                                                \
+    Name##vDirect##Ext(context, pname, &value);                               \
+    return value;                                                             \
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alGetIntegerv(ALenum pname, ALint *values)
-START_API_FUNC
-{
-    if(values)
-    {
-        switch(pname)
-        {
-            case AL_DOPPLER_FACTOR:
-            case AL_DOPPLER_VELOCITY:
-            case AL_DISTANCE_MODEL:
-            case AL_SPEED_OF_SOUND:
-            case AL_DEFERRED_UPDATES_SOFT:
-            case AL_GAIN_LIMIT_SOFT:
-            case AL_NUM_RESAMPLERS_SOFT:
-            case AL_DEFAULT_RESAMPLER_SOFT:
-                values[0] = alGetInteger(pname);
-                return;
-        }
-    }
+DECL_GETFUNC(ALboolean, alGetBoolean,)
+DECL_GETFUNC(ALdouble, alGetDouble,)
+DECL_GETFUNC(ALfloat, alGetFloat,)
+DECL_GETFUNC(ALint, alGetInteger,)
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+DECL_GETFUNC(ALint64SOFT, alGetInteger64,SOFT)
+DECL_GETFUNC(ALvoid*, alGetPointer,SOFT)
 
-    if(!values)
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(pname)
-    {
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid integer-vector property 0x%04x", pname);
-    }
-}
-END_API_FUNC
+#undef DECL_GETFUNC
 
-AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values)
-START_API_FUNC
-{
-    if(values)
-    {
-        switch(pname)
-        {
-            case AL_DOPPLER_FACTOR:
-            case AL_DOPPLER_VELOCITY:
-            case AL_DISTANCE_MODEL:
-            case AL_SPEED_OF_SOUND:
-            case AL_DEFERRED_UPDATES_SOFT:
-            case AL_GAIN_LIMIT_SOFT:
-            case AL_NUM_RESAMPLERS_SOFT:
-            case AL_DEFAULT_RESAMPLER_SOFT:
-                values[0] = alGetInteger64SOFT(pname);
-                return;
-        }
-    }
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+AL_API DECL_FUNC2(void, alGetBooleanv, ALenum,pname, ALboolean*,values)
+FORCE_ALIGN void AL_APIENTRY alGetBooleanvDirect(ALCcontext *context, ALenum pname, ALboolean *values) noexcept
+{
+    if(!values) UNLIKELY
+        return context->setError(AL_INVALID_VALUE, "NULL pointer");
+    GetValue(context, pname, values);
+}
 
-    if(!values)
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(pname)
-    {
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid integer64-vector property 0x%04x", pname);
-    }
+AL_API DECL_FUNC2(void, alGetDoublev, ALenum,pname, ALdouble*,values)
+FORCE_ALIGN void AL_APIENTRY alGetDoublevDirect(ALCcontext *context, ALenum pname, ALdouble *values) noexcept
+{
+    if(!values) UNLIKELY
+        return context->setError(AL_INVALID_VALUE, "NULL pointer");
+    GetValue(context, pname, values);
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alGetPointervSOFT(ALenum pname, ALvoid **values)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alGetFloatv, ALenum,pname, ALfloat*,values)
+FORCE_ALIGN void AL_APIENTRY alGetFloatvDirect(ALCcontext *context, ALenum pname, ALfloat *values) noexcept
 {
-    if(values)
-    {
-        switch(pname)
-        {
-            case AL_EVENT_CALLBACK_FUNCTION_SOFT:
-            case AL_EVENT_CALLBACK_USER_PARAM_SOFT:
-                values[0] = alGetPointerSOFT(pname);
-                return;
-        }
-    }
+    if(!values) UNLIKELY
+        return context->setError(AL_INVALID_VALUE, "NULL pointer");
+    GetValue(context, pname, values);
+}
 
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
+AL_API DECL_FUNC2(void, alGetIntegerv, ALenum,pname, ALint*,values)
+FORCE_ALIGN void AL_APIENTRY alGetIntegervDirect(ALCcontext *context, ALenum pname, ALint *values) noexcept
+{
+    if(!values) UNLIKELY
+        return context->setError(AL_INVALID_VALUE, "NULL pointer");
+    GetValue(context, pname, values);
+}
 
-    if(!values)
-        context->setError(AL_INVALID_VALUE, "NULL pointer");
-    else switch(pname)
-    {
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid pointer-vector property 0x%04x", pname);
-    }
+AL_API DECL_FUNCEXT2(void, alGetInteger64v,SOFT, ALenum,pname, ALint64SOFT*,values)
+FORCE_ALIGN void AL_APIENTRY alGetInteger64vDirectSOFT(ALCcontext *context, ALenum pname, ALint64SOFT *values) noexcept
+{
+    if(!values) UNLIKELY
+        return context->setError(AL_INVALID_VALUE, "NULL pointer");
+    GetValue(context, pname, values);
 }
-END_API_FUNC
 
-AL_API const ALchar* AL_APIENTRY alGetString(ALenum pname)
-START_API_FUNC
+AL_API DECL_FUNCEXT2(void, alGetPointerv,SOFT, ALenum,pname, ALvoid**,values)
+FORCE_ALIGN void AL_APIENTRY alGetPointervDirectSOFT(ALCcontext *context, ALenum pname, ALvoid **values) noexcept
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return nullptr;
+    if(!values) UNLIKELY
+        return context->setError(AL_INVALID_VALUE, "NULL pointer");
 
-    const ALchar *value{nullptr};
     switch(pname)
     {
-    case AL_VENDOR:
-        value = alVendor;
-        break;
-
-    case AL_VERSION:
-        value = alVersion;
-        break;
-
-    case AL_RENDERER:
-        value = alRenderer;
-        break;
-
-    case AL_EXTENSIONS:
-        value = context->mExtensionList;
-        break;
-
-    case AL_NO_ERROR:
-        value = alNoError;
-        break;
-
-    case AL_INVALID_NAME:
-        value = alErrInvalidName;
-        break;
-
-    case AL_INVALID_ENUM:
-        value = alErrInvalidEnum;
-        break;
-
-    case AL_INVALID_VALUE:
-        value = alErrInvalidValue;
-        break;
+    case AL_EVENT_CALLBACK_FUNCTION_SOFT:
+        *values = reinterpret_cast<void*>(context->mEventCb);
+        return;
 
-    case AL_INVALID_OPERATION:
-        value = alErrInvalidOp;
-        break;
+    case AL_EVENT_CALLBACK_USER_PARAM_SOFT:
+        *values = context->mEventParam;
+        return;
 
-    case AL_OUT_OF_MEMORY:
-        value = alErrOutOfMemory;
-        break;
+    case AL_DEBUG_CALLBACK_FUNCTION_EXT:
+        *values = reinterpret_cast<void*>(context->mDebugCb);
+        return;
 
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid string property 0x%04x", pname);
+    case AL_DEBUG_CALLBACK_USER_PARAM_EXT:
+        *values = context->mDebugParam;
+        return;
     }
-    return value;
+    context->setError(AL_INVALID_ENUM, "Invalid context pointer property 0x%04x", pname);
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alDopplerFactor(ALfloat value)
-START_API_FUNC
+AL_API DECL_FUNC1(const ALchar*, alGetString, ALenum,pname)
+FORCE_ALIGN const ALchar* AL_APIENTRY alGetStringDirect(ALCcontext *context, ALenum pname) noexcept
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    if(!(value >= 0.0f && std::isfinite(value)))
-        context->setError(AL_INVALID_VALUE, "Doppler factor %f out of range", value);
-    else
+    switch(pname)
     {
-        std::lock_guard<std::mutex> _{context->mPropLock};
-        context->mDopplerFactor = value;
-        DO_UPDATEPROPS();
-    }
+    case AL_VENDOR: return GetVendorString();
+    case AL_VERSION: return GetVersionString();
+    case AL_RENDERER: return GetRendererString();
+    case AL_EXTENSIONS: return context->mExtensionsString.c_str();
+    case AL_NO_ERROR: return GetNoErrorString();
+    case AL_INVALID_NAME: return GetInvalidNameString();
+    case AL_INVALID_ENUM: return GetInvalidEnumString();
+    case AL_INVALID_VALUE: return GetInvalidValueString();
+    case AL_INVALID_OPERATION: return GetInvalidOperationString();
+    case AL_OUT_OF_MEMORY: return GetOutOfMemoryString();
+    case AL_STACK_OVERFLOW_EXT: return GetStackOverflowString();
+    case AL_STACK_UNDERFLOW_EXT: return GetStackUnderflowString();
+    }
+    context->setError(AL_INVALID_VALUE, "Invalid string property 0x%04x", pname);
+    return nullptr;
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value)
-START_API_FUNC
+AL_API DECL_FUNC1(void, alDopplerFactor, ALfloat,value)
+FORCE_ALIGN void AL_APIENTRY alDopplerFactorDirect(ALCcontext *context, ALfloat value) noexcept
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
     if(!(value >= 0.0f && std::isfinite(value)))
-        context->setError(AL_INVALID_VALUE, "Doppler velocity %f out of range", value);
+        context->setError(AL_INVALID_VALUE, "Doppler factor %f out of range", value);
     else
     {
-        std::lock_guard<std::mutex> _{context->mPropLock};
-        context->mDopplerVelocity = value;
-        DO_UPDATEPROPS();
+        std::lock_guard<std::mutex> proplock{context->mPropLock};
+        context->mDopplerFactor = value;
+        UpdateProps(context);
     }
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alSpeedOfSound(ALfloat value)
-START_API_FUNC
+AL_API DECL_FUNC1(void, alSpeedOfSound, ALfloat,value)
+FORCE_ALIGN void AL_APIENTRY alSpeedOfSoundDirect(ALCcontext *context, ALfloat value) noexcept
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
     if(!(value > 0.0f && std::isfinite(value)))
         context->setError(AL_INVALID_VALUE, "Speed of sound %f out of range", value);
     else
     {
-        std::lock_guard<std::mutex> _{context->mPropLock};
+        std::lock_guard<std::mutex> proplock{context->mPropLock};
         context->mSpeedOfSound = value;
-        DO_UPDATEPROPS();
+        UpdateProps(context);
     }
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alDistanceModel(ALenum value)
-START_API_FUNC
+AL_API DECL_FUNC1(void, alDistanceModel, ALenum,value)
+FORCE_ALIGN void AL_APIENTRY alDistanceModelDirect(ALCcontext *context, ALenum value) noexcept
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
     if(auto model = DistanceModelFromALenum(value))
     {
-        std::lock_guard<std::mutex> _{context->mPropLock};
+        std::lock_guard<std::mutex> proplock{context->mPropLock};
         context->mDistanceModel = *model;
         if(!context->mSourceDistanceModel)
-            DO_UPDATEPROPS();
+            UpdateProps(context);
     }
     else
         context->setError(AL_INVALID_VALUE, "Distance model 0x%04x out of range", value);
 }
-END_API_FUNC
 
 
-AL_API void AL_APIENTRY alDeferUpdatesSOFT(void)
-START_API_FUNC
+AL_API DECL_FUNCEXT(void, alDeferUpdates,SOFT)
+FORCE_ALIGN void AL_APIENTRY alDeferUpdatesDirectSOFT(ALCcontext *context) noexcept
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
+    std::lock_guard<std::mutex> proplock{context->mPropLock};
     context->deferUpdates();
 }
-END_API_FUNC
 
-AL_API void AL_APIENTRY alProcessUpdatesSOFT(void)
-START_API_FUNC
+AL_API DECL_FUNCEXT(void, alProcessUpdates,SOFT)
+FORCE_ALIGN void AL_APIENTRY alProcessUpdatesDirectSOFT(ALCcontext *context) noexcept
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return;
-
-    std::lock_guard<std::mutex> _{context->mPropLock};
+    std::lock_guard<std::mutex> proplock{context->mPropLock};
     context->processUpdates();
 }
-END_API_FUNC
 
 
-AL_API const ALchar* AL_APIENTRY alGetStringiSOFT(ALenum pname, ALsizei index)
-START_API_FUNC
+AL_API DECL_FUNCEXT2(const ALchar*, alGetStringi,SOFT, ALenum,pname, ALsizei,index)
+FORCE_ALIGN const ALchar* AL_APIENTRY alGetStringiDirectSOFT(ALCcontext *context, ALenum pname, ALsizei index) noexcept
 {
-    ContextRef context{GetContextRef()};
-    if(!context) UNLIKELY return nullptr;
-
-    const ALchar *value{nullptr};
     switch(pname)
     {
     case AL_RESAMPLER_NAME_SOFT:
-        if(index < 0 || index > static_cast<ALint>(Resampler::Max))
-            context->setError(AL_INVALID_VALUE, "Resampler name index %d out of range", index);
-        else
-            value = GetResamplerName(static_cast<Resampler>(index));
-        break;
+        if(index >= 0 && index <= static_cast<ALint>(Resampler::Max))
+            return GetResamplerName(static_cast<Resampler>(index));
+        context->setError(AL_INVALID_VALUE, "Resampler name index %d out of range", index);
+        return nullptr;
+    }
+    context->setError(AL_INVALID_VALUE, "Invalid string indexed property");
+    return nullptr;
+}
+
 
-    default:
-        context->setError(AL_INVALID_VALUE, "Invalid string indexed property");
+AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value) noexcept
+{
+    ContextRef context{GetContextRef()};
+    if(!context) UNLIKELY return;
+
+    if(context->mContextFlags.test(ContextFlags::DebugBit)) UNLIKELY
+        context->debugMessage(DebugSource::API, DebugType::DeprecatedBehavior, 0,
+            DebugSeverity::Medium,
+            "alDopplerVelocity is deprecated in AL 1.1, use alSpeedOfSound; "
+            "alDopplerVelocity(x) -> alSpeedOfSound(343.3f * x)");
+
+    if(!(value >= 0.0f && std::isfinite(value)))
+        context->setError(AL_INVALID_VALUE, "Doppler velocity %f out of range", value);
+    else
+    {
+        std::lock_guard<std::mutex> proplock{context->mPropLock};
+        context->mDopplerVelocity = value;
+        UpdateProps(context.get());
     }
-    return value;
 }
-END_API_FUNC
 
 
 void UpdateContextProps(ALCcontext *context)
 {
-    /* Get an unused proprty container, or allocate a new one as needed. */
+    /* Get an unused property container, or allocate a new one as needed. */
     ContextProps *props{context->mFreeContextProps.load(std::memory_order_acquire)};
     if(!props)
-        props = new ContextProps{};
-    else
     {
-        ContextProps *next;
-        do {
-            next = props->next.load(std::memory_order_relaxed);
-        } while(context->mFreeContextProps.compare_exchange_weak(props, next,
-                std::memory_order_seq_cst, std::memory_order_acquire) == 0);
+        context->allocContextProps();
+        props = context->mFreeContextProps.load(std::memory_order_acquire);
     }
+    ContextProps *next;
+    do {
+        next = props->next.load(std::memory_order_relaxed);
+    } while(context->mFreeContextProps.compare_exchange_weak(props, next,
+        std::memory_order_acq_rel, std::memory_order_acquire) == false);
 
     /* Copy in current property values. */
-    ALlistener &listener = context->mListener;
+    const auto &listener = context->mListener;
     props->Position = listener.Position;
     props->Velocity = listener.Velocity;
     props->OrientAt = listener.OrientAt;

File diff suppressed because it is too large
+ 115 - 776
Engine/lib/openal-soft/alc/alc.cpp


+ 202 - 193
Engine/lib/openal-soft/alc/alconfig.cpp

@@ -22,9 +22,6 @@
 
 #include "alconfig.h"
 
-#include <cstdlib>
-#include <cctype>
-#include <cstring>
 #ifdef _WIN32
 #include <windows.h>
 #include <shlobj.h>
@@ -34,25 +31,47 @@
 #endif
 
 #include <algorithm>
-#include <cstdio>
+#include <array>
+#include <cctype>
+#include <cstdlib>
+#include <filesystem>
+#include <fstream>
+#include <istream>
+#include <limits>
 #include <string>
+#include <string_view>
 #include <utility>
+#include <vector>
 
-#include "alfstream.h"
+#include "almalloc.h"
 #include "alstring.h"
 #include "core/helpers.h"
 #include "core/logging.h"
 #include "strutils.h"
-#include "vector.h"
 
+#if defined(ALSOFT_UWP)
+#include <winrt/Windows.Media.Core.h> // !!This is important!!
+#include <winrt/Windows.Storage.h>
+#include <winrt/Windows.Foundation.h>
+#include <winrt/Windows.Foundation.Collections.h>
+using namespace winrt;
+#endif
 
 namespace {
 
+using namespace std::string_view_literals;
+
+#if defined(_WIN32) && !defined(_GAMING_XBOX) && !defined(ALSOFT_UWP)
+struct CoTaskMemDeleter {
+    void operator()(void *mem) const { CoTaskMemFree(mem); }
+};
+#endif
+
 struct ConfigEntry {
     std::string key;
     std::string value;
 };
-al::vector<ConfigEntry> ConfOpts;
+std::vector<ConfigEntry> ConfOpts;
 
 
 std::string &lstrip(std::string &line)
@@ -72,57 +91,48 @@ bool readline(std::istream &f, std::string &output)
     return std::getline(f, output) && !output.empty();
 }
 
-std::string expdup(const char *str)
+std::string expdup(std::string_view str)
 {
     std::string output;
 
-    std::string envval;
-    while(*str != '\0')
+    while(!str.empty())
     {
-        const char *addstr;
-        size_t addstrlen;
-
-        if(str[0] != '$')
+        if(auto nextpos = str.find('$'))
         {
-            const char *next = std::strchr(str, '$');
-            addstr = str;
-            addstrlen = next ? static_cast<size_t>(next-str) : std::strlen(str);
+            output += str.substr(0, nextpos);
+            if(nextpos == std::string_view::npos)
+                break;
 
-            str += addstrlen;
+            str.remove_prefix(nextpos);
         }
-        else
-        {
-            str++;
-            if(*str == '$')
-            {
-                const char *next = std::strchr(str+1, '$');
-                addstr = str;
-                addstrlen = next ? static_cast<size_t>(next-str) : std::strlen(str);
 
-                str += addstrlen;
-            }
-            else
-            {
-                const bool hasbraces{(*str == '{')};
+        str.remove_prefix(1);
+        if(str.empty())
+        {
+            output += '$';
+            break;
+        }
+        if(str.front() == '$')
+        {
+            output += '$';
+            str.remove_prefix(1);
+            continue;
+        }
 
-                if(hasbraces) str++;
-                const char *envstart = str;
-                while(std::isalnum(*str) || *str == '_')
-                    ++str;
-                if(hasbraces && *str != '}')
-                    continue;
-                const std::string envname{envstart, str};
-                if(hasbraces) str++;
+        const bool hasbraces{str.front() == '{'};
+        if(hasbraces) str.remove_prefix(1);
 
-                envval = al::getenv(envname.c_str()).value_or(std::string{});
-                addstr = envval.data();
-                addstrlen = envval.length();
-            }
-        }
-        if(addstrlen == 0)
+        size_t envend{0};
+        while(envend < str.size() && (std::isalnum(str[envend]) || str[envend] == '_'))
+            ++envend;
+        if(hasbraces && (envend == str.size() || str[envend] != '}'))
             continue;
+        const std::string envname{str.substr(0, envend)};
+        if(hasbraces) ++envend;
+        str.remove_prefix(envend);
 
-        output.append(addstr, addstrlen);
+        if(auto envval = al::getenv(envname.c_str()))
+            output += *envval;
     }
 
     return output;
@@ -140,44 +150,43 @@ void LoadConfigFromFile(std::istream &f)
 
         if(buffer[0] == '[')
         {
-            auto line = const_cast<char*>(buffer.data());
-            char *section = line+1;
-            char *endsection;
-
-            endsection = std::strchr(section, ']');
-            if(!endsection || section == endsection)
+            auto endpos = buffer.find(']', 1);
+            if(endpos == 1 || endpos == std::string::npos)
             {
-                ERR(" config parse error: bad line \"%s\"\n", line);
+                ERR(" config parse error: bad line \"%s\"\n", buffer.c_str());
                 continue;
             }
-            if(endsection[1] != 0)
+            if(buffer[endpos+1] != '\0')
             {
-                char *end = endsection+1;
-                while(std::isspace(*end))
-                    ++end;
-                if(*end != 0 && *end != '#')
+                size_t last{endpos+1};
+                while(last < buffer.size() && std::isspace(buffer[last]))
+                    ++last;
+
+                if(last < buffer.size() && buffer[last] != '#')
                 {
-                    ERR(" config parse error: bad line \"%s\"\n", line);
+                    ERR(" config parse error: bad line \"%s\"\n", buffer.c_str());
                     continue;
                 }
             }
-            *endsection = 0;
+
+            auto section = std::string_view{buffer}.substr(1, endpos-1);
 
             curSection.clear();
-            if(al::strcasecmp(section, "general") != 0)
+            if(al::case_compare(section, "general"sv) != 0)
             {
                 do {
-                    char *nextp = std::strchr(section, '%');
-                    if(!nextp)
+                    auto nextp = section.find('%');
+                    if(nextp == std::string_view::npos)
                     {
                         curSection += section;
                         break;
                     }
 
-                    curSection.append(section, nextp);
-                    section = nextp;
+                    curSection += section.substr(0, nextp);
+                    section.remove_prefix(nextp);
 
-                    if(((section[1] >= '0' && section[1] <= '9') ||
+                    if(section.size() > 2 &&
+                       ((section[1] >= '0' && section[1] <= '9') ||
                         (section[1] >= 'a' && section[1] <= 'f') ||
                         (section[1] >= 'A' && section[1] <= 'F')) &&
                        ((section[2] >= '0' && section[2] <= '9') ||
@@ -198,19 +207,19 @@ void LoadConfigFromFile(std::istream &f)
                         else if(section[2] >= 'A' && section[2] <= 'F')
                             b |= (section[2]-'A'+0x0a);
                         curSection += static_cast<char>(b);
-                        section += 3;
+                        section.remove_prefix(3);
                     }
-                    else if(section[1] == '%')
+                    else if(section.size() > 1 && section[1] == '%')
                     {
                         curSection += '%';
-                        section += 2;
+                        section.remove_prefix(2);
                     }
                     else
                     {
                         curSection += '%';
-                        section += 1;
+                        section.remove_prefix(1);
                     }
-                } while(*section != 0);
+                } while(!section.empty());
             }
 
             continue;
@@ -228,16 +237,17 @@ void LoadConfigFromFile(std::istream &f)
             ERR(" config parse error: malformed option line: \"%s\"\n", buffer.c_str());
             continue;
         }
-        auto keyend = sep++;
-        while(keyend > 0 && std::isspace(buffer[keyend-1]))
-            --keyend;
-        if(!keyend)
+        auto keypart = std::string_view{buffer}.substr(0, sep++);
+        while(!keypart.empty() && std::isspace(keypart.back()))
+            keypart.remove_suffix(1);
+        if(keypart.empty())
         {
             ERR(" config parse error: malformed option line: \"%s\"\n", buffer.c_str());
             continue;
         }
-        while(sep < buffer.size() && std::isspace(buffer[sep]))
-            sep++;
+        auto valpart = std::string_view{buffer}.substr(sep);
+        while(!valpart.empty() && std::isspace(valpart.front()))
+            valpart.remove_prefix(1);
 
         std::string fullKey;
         if(!curSection.empty())
@@ -245,20 +255,24 @@ void LoadConfigFromFile(std::istream &f)
             fullKey += curSection;
             fullKey += '/';
         }
-        fullKey += buffer.substr(0u, keyend);
+        fullKey += keypart;
 
-        std::string value{(sep < buffer.size()) ? buffer.substr(sep) : std::string{}};
-        if(value.size() > 1)
+        if(valpart.size() > size_t{std::numeric_limits<int>::max()})
+        {
+            ERR(" config parse error: value too long in line \"%s\"\n", buffer.c_str());
+            continue;
+        }
+        if(valpart.size() > 1)
         {
-            if((value.front() == '"' && value.back() == '"')
-                || (value.front() == '\'' && value.back() == '\''))
+            if((valpart.front() == '"' && valpart.back() == '"')
+                || (valpart.front() == '\'' && valpart.back() == '\''))
             {
-                value.pop_back();
-                value.erase(value.begin());
+                valpart.remove_prefix(1);
+                valpart.remove_suffix(1);
             }
         }
 
-        TRACE(" found '%s' = '%s'\n", fullKey.c_str(), value.c_str());
+        TRACE(" setting '%s' = '%.*s'\n", fullKey.c_str(), al::sizei(valpart), valpart.data());
 
         /* Check if we already have this option set */
         auto find_key = [&fullKey](const ConfigEntry &entry) -> bool
@@ -266,61 +280,49 @@ void LoadConfigFromFile(std::istream &f)
         auto ent = std::find_if(ConfOpts.begin(), ConfOpts.end(), find_key);
         if(ent != ConfOpts.end())
         {
-            if(!value.empty())
-                ent->value = expdup(value.c_str());
+            if(!valpart.empty())
+                ent->value = expdup(valpart);
             else
                 ConfOpts.erase(ent);
         }
-        else if(!value.empty())
-            ConfOpts.emplace_back(ConfigEntry{std::move(fullKey), expdup(value.c_str())});
+        else if(!valpart.empty())
+            ConfOpts.emplace_back(ConfigEntry{std::move(fullKey), expdup(valpart)});
     }
     ConfOpts.shrink_to_fit();
 }
 
-const char *GetConfigValue(const char *devName, const char *blockName, const char *keyName)
+const char *GetConfigValue(const std::string_view devName, const std::string_view blockName,
+    const std::string_view keyName)
 {
-    if(!keyName)
+    if(keyName.empty())
         return nullptr;
 
     std::string key;
-    if(blockName && al::strcasecmp(blockName, "general") != 0)
+    if(!blockName.empty() && al::case_compare(blockName, "general"sv) != 0)
     {
         key = blockName;
-        if(devName)
-        {
-            key += '/';
-            key += devName;
-        }
         key += '/';
-        key += keyName;
     }
-    else
+    if(!devName.empty())
     {
-        if(devName)
-        {
-            key = devName;
-            key += '/';
-        }
-        key += keyName;
+        key += devName;
+        key += '/';
     }
+    key += keyName;
 
     auto iter = std::find_if(ConfOpts.cbegin(), ConfOpts.cend(),
-        [&key](const ConfigEntry &entry) -> bool
-        { return entry.key == key; });
+        [&key](const ConfigEntry &entry) -> bool { return entry.key == key; });
     if(iter != ConfOpts.cend())
     {
-        TRACE("Found %s = \"%s\"\n", key.c_str(), iter->value.c_str());
+        TRACE("Found option %s = \"%s\"\n", key.c_str(), iter->value.c_str());
         if(!iter->value.empty())
             return iter->value.c_str();
         return nullptr;
     }
 
-    if(!devName)
-    {
-        TRACE("Key %s not found\n", key.c_str());
+    if(devName.empty())
         return nullptr;
-    }
-    return GetConfigValue(nullptr, blockName, keyName);
+    return GetConfigValue({}, blockName, keyName);
 }
 
 } // namespace
@@ -329,33 +331,48 @@ const char *GetConfigValue(const char *devName, const char *blockName, const cha
 #ifdef _WIN32
 void ReadALConfig()
 {
-    WCHAR buffer[MAX_PATH];
-    if(SHGetSpecialFolderPathW(nullptr, buffer, CSIDL_APPDATA, FALSE) != FALSE)
+    namespace fs = std::filesystem;
+    fs::path path;
+
+#if !defined(_GAMING_XBOX)
     {
-        std::string filepath{wstr_to_utf8(buffer)};
-        filepath += "\\alsoft.ini";
+#if !defined(ALSOFT_UWP)
+        std::unique_ptr<WCHAR,CoTaskMemDeleter> bufstore;
+        const HRESULT hr{SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_DONT_UNEXPAND,
+            nullptr, al::out_ptr(bufstore))};
+        if(SUCCEEDED(hr))
+        {
+            const std::wstring_view buffer{bufstore.get()};
+#else
+        winrt::Windows::Storage::ApplicationDataContainer localSettings = winrt::Windows::Storage::ApplicationData::Current().LocalSettings();
+        auto bufstore = Windows::Storage::ApplicationData::Current().RoamingFolder().Path();
+        std::wstring_view buffer{bufstore};
+        {
+#endif
+            path = fs::path{buffer};
+            path /= L"alsoft.ini";
 
-        TRACE("Loading config %s...\n", filepath.c_str());
-        al::ifstream f{filepath};
-        if(f.is_open())
-            LoadConfigFromFile(f);
+            TRACE("Loading config %s...\n", path.u8string().c_str());
+            if(std::ifstream f{path}; f.is_open())
+                LoadConfigFromFile(f);
+        }
     }
+#endif
 
-    std::string ppath{GetProcBinary().path};
-    if(!ppath.empty())
+    path = fs::u8path(GetProcBinary().path);
+    if(!path.empty())
     {
-        ppath += "\\alsoft.ini";
-        TRACE("Loading config %s...\n", ppath.c_str());
-        al::ifstream f{ppath};
-        if(f.is_open())
+        path /= "alsoft.ini";
+        TRACE("Loading config %s...\n", path.u8string().c_str());
+        if(std::ifstream f{path}; f.is_open())
             LoadConfigFromFile(f);
     }
 
     if(auto confpath = al::getenv(L"ALSOFT_CONF"))
     {
-        TRACE("Loading config %s...\n", wstr_to_utf8(confpath->c_str()).c_str());
-        al::ifstream f{*confpath};
-        if(f.is_open())
+        path = *confpath;
+        TRACE("Loading config %s...\n", path.u8string().c_str());
+        if(std::ifstream f{path}; f.is_open())
             LoadConfigFromFile(f);
     }
 }
@@ -364,13 +381,12 @@ void ReadALConfig()
 
 void ReadALConfig()
 {
-    const char *str{"/etc/openal/alsoft.conf"};
+    namespace fs = std::filesystem;
+    fs::path path{"/etc/openal/alsoft.conf"};
 
-    TRACE("Loading config %s...\n", str);
-    al::ifstream f{str};
-    if(f.is_open())
+    TRACE("Loading config %s...\n", path.u8string().c_str());
+    if(std::ifstream f{path}; f.is_open())
         LoadConfigFromFile(f);
-    f.close();
 
     std::string confpaths{al::getenv("XDG_CONFIG_DIRS").value_or("/etc/xdg")};
     /* Go through the list in reverse, since "the order of base directories
@@ -378,48 +394,43 @@ void ReadALConfig()
      * important". Ergo, we need to load the settings from the later dirs
      * first so that the settings in the earlier dirs override them.
      */
-    std::string fname;
     while(!confpaths.empty())
     {
-        auto next = confpaths.find_last_of(':');
+        auto next = confpaths.rfind(':');
         if(next < confpaths.length())
         {
-            fname = confpaths.substr(next+1);
+            path = fs::path{std::string_view{confpaths}.substr(next+1)}.lexically_normal();
             confpaths.erase(next);
         }
         else
         {
-            fname = confpaths;
+            path = fs::path{confpaths}.lexically_normal();
             confpaths.clear();
         }
 
-        if(fname.empty() || fname.front() != '/')
-            WARN("Ignoring XDG config dir: %s\n", fname.c_str());
+        if(!path.is_absolute())
+            WARN("Ignoring XDG config dir: %s\n", path.u8string().c_str());
         else
         {
-            if(fname.back() != '/') fname += "/alsoft.conf";
-            else fname += "alsoft.conf";
+            path /= "alsoft.conf";
 
-            TRACE("Loading config %s...\n", fname.c_str());
-            f = al::ifstream{fname};
-            if(f.is_open())
+            TRACE("Loading config %s...\n", path.u8string().c_str());
+            if(std::ifstream f{path}; f.is_open())
                 LoadConfigFromFile(f);
         }
-        fname.clear();
     }
 
 #ifdef __APPLE__
     CFBundleRef mainBundle = CFBundleGetMainBundle();
     if(mainBundle)
     {
-        unsigned char fileName[PATH_MAX];
-        CFURLRef configURL;
+        CFURLRef configURL{CFBundleCopyResourceURL(mainBundle, CFSTR(".alsoftrc"), CFSTR(""),
+            nullptr)};
 
-        if((configURL=CFBundleCopyResourceURL(mainBundle, CFSTR(".alsoftrc"), CFSTR(""), nullptr)) &&
-           CFURLGetFileSystemRepresentation(configURL, true, fileName, sizeof(fileName)))
+        std::array<unsigned char,PATH_MAX> fileName{};
+        if(configURL && CFURLGetFileSystemRepresentation(configURL, true, fileName.data(), fileName.size()))
         {
-            f = al::ifstream{reinterpret_cast<char*>(fileName)};
-            if(f.is_open())
+            if(std::ifstream f{reinterpret_cast<char*>(fileName.data())}; f.is_open())
                 LoadConfigFromFile(f);
         }
     }
@@ -427,102 +438,100 @@ void ReadALConfig()
 
     if(auto homedir = al::getenv("HOME"))
     {
-        fname = *homedir;
-        if(fname.back() != '/') fname += "/.alsoftrc";
-        else fname += ".alsoftrc";
+        path = *homedir;
+        path /= ".alsoftrc";
 
-        TRACE("Loading config %s...\n", fname.c_str());
-        f = al::ifstream{fname};
-        if(f.is_open())
+        TRACE("Loading config %s...\n", path.u8string().c_str());
+        if(std::ifstream f{path}; f.is_open())
             LoadConfigFromFile(f);
     }
 
     if(auto configdir = al::getenv("XDG_CONFIG_HOME"))
     {
-        fname = *configdir;
-        if(fname.back() != '/') fname += "/alsoft.conf";
-        else fname += "alsoft.conf";
+        path = *configdir;
+        path /= "alsoft.conf";
     }
     else
     {
-        fname.clear();
+        path.clear();
         if(auto homedir = al::getenv("HOME"))
         {
-            fname = *homedir;
-            if(fname.back() != '/') fname += "/.config/alsoft.conf";
-            else fname += ".config/alsoft.conf";
+            path = *homedir;
+            path /= ".config/alsoft.conf";
         }
     }
-    if(!fname.empty())
+    if(!path.empty())
     {
-        TRACE("Loading config %s...\n", fname.c_str());
-        f = al::ifstream{fname};
-        if(f.is_open())
+        TRACE("Loading config %s...\n", path.u8string().c_str());
+        if(std::ifstream f{path}; f.is_open())
             LoadConfigFromFile(f);
     }
 
-    std::string ppath{GetProcBinary().path};
-    if(!ppath.empty())
+    path = GetProcBinary().path;
+    if(!path.empty())
     {
-        if(ppath.back() != '/') ppath += "/alsoft.conf";
-        else ppath += "alsoft.conf";
+        path /= "alsoft.conf";
 
-        TRACE("Loading config %s...\n", ppath.c_str());
-        f = al::ifstream{ppath};
-        if(f.is_open())
+        TRACE("Loading config %s...\n", path.u8string().c_str());
+        if(std::ifstream f{path}; f.is_open())
             LoadConfigFromFile(f);
     }
 
     if(auto confname = al::getenv("ALSOFT_CONF"))
     {
         TRACE("Loading config %s...\n", confname->c_str());
-        f = al::ifstream{*confname};
-        if(f.is_open())
+        if(std::ifstream f{*confname}; f.is_open())
             LoadConfigFromFile(f);
     }
 }
 #endif
 
-al::optional<std::string> ConfigValueStr(const char *devName, const char *blockName, const char *keyName)
+std::optional<std::string> ConfigValueStr(const std::string_view devName,
+    const std::string_view blockName, const std::string_view keyName)
 {
     if(const char *val{GetConfigValue(devName, blockName, keyName)})
         return val;
-    return al::nullopt;
+    return std::nullopt;
 }
 
-al::optional<int> ConfigValueInt(const char *devName, const char *blockName, const char *keyName)
+std::optional<int> ConfigValueInt(const std::string_view devName, const std::string_view blockName,
+    const std::string_view keyName)
 {
     if(const char *val{GetConfigValue(devName, blockName, keyName)})
         return static_cast<int>(std::strtol(val, nullptr, 0));
-    return al::nullopt;
+    return std::nullopt;
 }
 
-al::optional<unsigned int> ConfigValueUInt(const char *devName, const char *blockName, const char *keyName)
+std::optional<unsigned int> ConfigValueUInt(const std::string_view devName,
+    const std::string_view blockName, const std::string_view keyName)
 {
     if(const char *val{GetConfigValue(devName, blockName, keyName)})
         return static_cast<unsigned int>(std::strtoul(val, nullptr, 0));
-    return al::nullopt;
+    return std::nullopt;
 }
 
-al::optional<float> ConfigValueFloat(const char *devName, const char *blockName, const char *keyName)
+std::optional<float> ConfigValueFloat(const std::string_view devName,
+    const std::string_view blockName, const std::string_view keyName)
 {
     if(const char *val{GetConfigValue(devName, blockName, keyName)})
         return std::strtof(val, nullptr);
-    return al::nullopt;
+    return std::nullopt;
 }
 
-al::optional<bool> ConfigValueBool(const char *devName, const char *blockName, const char *keyName)
+std::optional<bool> ConfigValueBool(const std::string_view devName,
+    const std::string_view blockName, const std::string_view keyName)
 {
     if(const char *val{GetConfigValue(devName, blockName, keyName)})
         return al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0
-            || al::strcasecmp(val, "true")==0 || atoi(val) != 0;
-    return al::nullopt;
+            || al::strcasecmp(val, "true") == 0 || atoi(val) != 0;
+    return std::nullopt;
 }
 
-bool GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, bool def)
+bool GetConfigValueBool(const std::string_view devName, const std::string_view blockName,
+    const std::string_view keyName, bool def)
 {
     if(const char *val{GetConfigValue(devName, blockName, keyName)})
-        return (al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0
-            || al::strcasecmp(val, "true") == 0 || atoi(val) != 0);
+        return al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0
+            || al::strcasecmp(val, "true") == 0 || atoi(val) != 0;
     return def;
 }

+ 14 - 7
Engine/lib/openal-soft/alc/alconfig.h

@@ -1,18 +1,25 @@
 #ifndef ALCONFIG_H
 #define ALCONFIG_H
 
+#include <optional>
 #include <string>
+#include <string_view>
 
-#include "aloptional.h"
 
 void ReadALConfig();
 
-bool GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, bool def);
+bool GetConfigValueBool(const std::string_view devName, const std::string_view blockName,
+    const std::string_view keyName, bool def);
 
-al::optional<std::string> ConfigValueStr(const char *devName, const char *blockName, const char *keyName);
-al::optional<int> ConfigValueInt(const char *devName, const char *blockName, const char *keyName);
-al::optional<unsigned int> ConfigValueUInt(const char *devName, const char *blockName, const char *keyName);
-al::optional<float> ConfigValueFloat(const char *devName, const char *blockName, const char *keyName);
-al::optional<bool> ConfigValueBool(const char *devName, const char *blockName, const char *keyName);
+std::optional<std::string> ConfigValueStr(const std::string_view devName,
+    const std::string_view blockName, const std::string_view keyName);
+std::optional<int> ConfigValueInt(const std::string_view devName, const std::string_view blockName,
+    const std::string_view keyName);
+std::optional<unsigned int> ConfigValueUInt(const std::string_view devName,
+    const std::string_view blockName, const std::string_view keyName);
+std::optional<float> ConfigValueFloat(const std::string_view devName,
+    const std::string_view blockName, const std::string_view keyName);
+std::optional<bool> ConfigValueBool(const std::string_view devName,
+    const std::string_view blockName, const std::string_view keyName);
 
 #endif /* ALCONFIG_H */

File diff suppressed because it is too large
+ 373 - 252
Engine/lib/openal-soft/alc/alu.cpp


+ 5 - 5
Engine/lib/openal-soft/alc/alu.h

@@ -2,20 +2,20 @@
 #define ALU_H
 
 #include <bitset>
-
-#include "aloptional.h"
+#include <cstdint>
+#include <optional>
 
 struct ALCcontext;
 struct ALCdevice;
 struct EffectSlot;
 
-enum class StereoEncoding : unsigned char;
+enum class StereoEncoding : std::uint8_t;
 
 
 constexpr float GainMixMax{1000.0f}; /* +60dB */
 
 
-enum CompatFlags : uint8_t {
+enum CompatFlags : std::uint8_t {
     ReverseX,
     ReverseY,
     ReverseZ,
@@ -31,7 +31,7 @@ void aluInit(CompatFlagBitset flags, const float nfcscale);
  * Set up the appropriate panning method and mixing method given the device
  * properties.
  */
-void aluInitRenderer(ALCdevice *device, int hrtf_id, al::optional<StereoEncoding> stereomode);
+void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional<StereoEncoding> stereomode);
 
 void aluInitEffectPanning(EffectSlot *slot, ALCcontext *context);
 

Some files were not shown because too many files changed in this diff