joonsrenderer 1.1-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +53 -0
  3. data/.mvn/extensions.xml +8 -0
  4. data/CHANGELOG.md +2 -0
  5. data/Gemfile +6 -0
  6. data/LICENSE +674 -0
  7. data/README.md +2 -0
  8. data/Rakefile +43 -0
  9. data/docs/.gitignore +6 -0
  10. data/docs/_config.yml +20 -0
  11. data/docs/_includes/footer.html +38 -0
  12. data/docs/_includes/head.html +15 -0
  13. data/docs/_includes/header.html +27 -0
  14. data/docs/_includes/icon-github.html +1 -0
  15. data/docs/_includes/icon-github.svg +1 -0
  16. data/docs/_includes/icon-twitter.html +1 -0
  17. data/docs/_includes/icon-twitter.svg +1 -0
  18. data/docs/_layouts/default.html +20 -0
  19. data/docs/_layouts/page.html +14 -0
  20. data/docs/_layouts/post.html +15 -0
  21. data/docs/_posts/2017-01-08-animated_ray_tracing.md +72 -0
  22. data/docs/_posts/2017-01-08-welcome.md +78 -0
  23. data/docs/_sass/_base.scss +206 -0
  24. data/docs/_sass/_layout.scss +242 -0
  25. data/docs/_sass/_syntax-highlighting.scss +71 -0
  26. data/docs/about.md +12 -0
  27. data/docs/assets/Animation.ogv +0 -0
  28. data/docs/assets/Animation.png +0 -0
  29. data/docs/assets/basic.png +0 -0
  30. data/docs/assets/basic_traced.png +0 -0
  31. data/docs/css/main.scss +38 -0
  32. data/docs/favicon.ico +0 -0
  33. data/docs/feed.xml +30 -0
  34. data/docs/index.html +38 -0
  35. data/joonsrenderer.gemspec +23 -0
  36. data/lib/joonsrenderer.rb +12 -0
  37. data/lib/joonsrenderer/version.rb +3 -0
  38. data/pom.rb +75 -0
  39. data/pom.xml +163 -0
  40. data/src/main/java/SunflowGUI.java +1354 -0
  41. data/src/main/java/joons/JRFiller.java +79 -0
  42. data/src/main/java/joons/JRImagePanel.java +141 -0
  43. data/src/main/java/joons/JRRecorder.java +183 -0
  44. data/src/main/java/joons/JRStatics.java +199 -0
  45. data/src/main/java/joons/JoonsRenderer.java +837 -0
  46. data/src/main/java/org/sunflow/AsciiFileSunflowAPI.java +98 -0
  47. data/src/main/java/org/sunflow/Benchmark.java +313 -0
  48. data/src/main/java/org/sunflow/BinaryFileSunflowAPI.java +228 -0
  49. data/src/main/java/org/sunflow/FileSunflowAPI.java +354 -0
  50. data/src/main/java/org/sunflow/PluginRegistry.java +322 -0
  51. data/src/main/java/org/sunflow/RealtimeBenchmark.java +125 -0
  52. data/src/main/java/org/sunflow/RenderObjectMap.java +344 -0
  53. data/src/main/java/org/sunflow/SunflowAPI.java +762 -0
  54. data/src/main/java/org/sunflow/SunflowAPIInterface.java +277 -0
  55. data/src/main/java/org/sunflow/core/AccelerationStructure.java +20 -0
  56. data/src/main/java/org/sunflow/core/AccelerationStructureFactory.java +36 -0
  57. data/src/main/java/org/sunflow/core/BucketOrder.java +21 -0
  58. data/src/main/java/org/sunflow/core/Camera.java +125 -0
  59. data/src/main/java/org/sunflow/core/CameraLens.java +29 -0
  60. data/src/main/java/org/sunflow/core/CausticPhotonMapInterface.java +15 -0
  61. data/src/main/java/org/sunflow/core/Display.java +78 -0
  62. data/src/main/java/org/sunflow/core/Filter.java +27 -0
  63. data/src/main/java/org/sunflow/core/GIEngine.java +42 -0
  64. data/src/main/java/org/sunflow/core/Geometry.java +157 -0
  65. data/src/main/java/org/sunflow/core/GlobalPhotonMapInterface.java +21 -0
  66. data/src/main/java/org/sunflow/core/ImageSampler.java +26 -0
  67. data/src/main/java/org/sunflow/core/Instance.java +224 -0
  68. data/src/main/java/org/sunflow/core/InstanceList.java +83 -0
  69. data/src/main/java/org/sunflow/core/IntersectionState.java +120 -0
  70. data/src/main/java/org/sunflow/core/LightSample.java +104 -0
  71. data/src/main/java/org/sunflow/core/LightServer.java +382 -0
  72. data/src/main/java/org/sunflow/core/LightSource.java +67 -0
  73. data/src/main/java/org/sunflow/core/Modifier.java +16 -0
  74. data/src/main/java/org/sunflow/core/Options.java +20 -0
  75. data/src/main/java/org/sunflow/core/ParameterList.java +758 -0
  76. data/src/main/java/org/sunflow/core/PhotonStore.java +62 -0
  77. data/src/main/java/org/sunflow/core/PrimitiveList.java +70 -0
  78. data/src/main/java/org/sunflow/core/Ray.java +219 -0
  79. data/src/main/java/org/sunflow/core/RenderObject.java +25 -0
  80. data/src/main/java/org/sunflow/core/Scene.java +377 -0
  81. data/src/main/java/org/sunflow/core/SceneParser.java +58 -0
  82. data/src/main/java/org/sunflow/core/Shader.java +30 -0
  83. data/src/main/java/org/sunflow/core/ShadingCache.java +84 -0
  84. data/src/main/java/org/sunflow/core/ShadingState.java +939 -0
  85. data/src/main/java/org/sunflow/core/Statistics.java +85 -0
  86. data/src/main/java/org/sunflow/core/Tesselatable.java +36 -0
  87. data/src/main/java/org/sunflow/core/Texture.java +128 -0
  88. data/src/main/java/org/sunflow/core/TextureCache.java +48 -0
  89. data/src/main/java/org/sunflow/core/accel/BoundingIntervalHierarchy.java +652 -0
  90. data/src/main/java/org/sunflow/core/accel/KDTree.java +833 -0
  91. data/src/main/java/org/sunflow/core/accel/NullAccelerator.java +30 -0
  92. data/src/main/java/org/sunflow/core/accel/UniformGrid.java +329 -0
  93. data/src/main/java/org/sunflow/core/bucket/BucketOrderFactory.java +26 -0
  94. data/src/main/java/org/sunflow/core/bucket/ColumnBucketOrder.java +21 -0
  95. data/src/main/java/org/sunflow/core/bucket/DiagonalBucketOrder.java +28 -0
  96. data/src/main/java/org/sunflow/core/bucket/HilbertBucketOrder.java +65 -0
  97. data/src/main/java/org/sunflow/core/bucket/InvertedBucketOrder.java +28 -0
  98. data/src/main/java/org/sunflow/core/bucket/RandomBucketOrder.java +49 -0
  99. data/src/main/java/org/sunflow/core/bucket/RowBucketOrder.java +21 -0
  100. data/src/main/java/org/sunflow/core/bucket/SpiralBucketOrder.java +43 -0
  101. data/src/main/java/org/sunflow/core/camera/FisheyeLens.java +25 -0
  102. data/src/main/java/org/sunflow/core/camera/PinholeLens.java +43 -0
  103. data/src/main/java/org/sunflow/core/camera/SphericalLens.java +22 -0
  104. data/src/main/java/org/sunflow/core/camera/ThinLens.java +107 -0
  105. data/src/main/java/org/sunflow/core/display/FastDisplay.java +119 -0
  106. data/src/main/java/org/sunflow/core/display/FileDisplay.java +83 -0
  107. data/src/main/java/org/sunflow/core/display/FrameDisplay.java +97 -0
  108. data/src/main/java/org/sunflow/core/display/ImgPipeDisplay.java +109 -0
  109. data/src/main/java/org/sunflow/core/filter/BlackmanHarrisFilter.java +28 -0
  110. data/src/main/java/org/sunflow/core/filter/BoxFilter.java +16 -0
  111. data/src/main/java/org/sunflow/core/filter/CatmullRomFilter.java +29 -0
  112. data/src/main/java/org/sunflow/core/filter/CubicBSpline.java +32 -0
  113. data/src/main/java/org/sunflow/core/filter/GaussianFilter.java +24 -0
  114. data/src/main/java/org/sunflow/core/filter/LanczosFilter.java +30 -0
  115. data/src/main/java/org/sunflow/core/filter/MitchellFilter.java +28 -0
  116. data/src/main/java/org/sunflow/core/filter/SincFilter.java +25 -0
  117. data/src/main/java/org/sunflow/core/filter/TriangleFilter.java +16 -0
  118. data/src/main/java/org/sunflow/core/gi/AmbientOcclusionGIEngine.java +57 -0
  119. data/src/main/java/org/sunflow/core/gi/FakeGIEngine.java +48 -0
  120. data/src/main/java/org/sunflow/core/gi/InstantGI.java +194 -0
  121. data/src/main/java/org/sunflow/core/gi/IrradianceCacheGIEngine.java +268 -0
  122. data/src/main/java/org/sunflow/core/gi/PathTracingGIEngine.java +65 -0
  123. data/src/main/java/org/sunflow/core/light/DirectionalSpotlight.java +103 -0
  124. data/src/main/java/org/sunflow/core/light/ImageBasedLight.java +303 -0
  125. data/src/main/java/org/sunflow/core/light/PointLight.java +72 -0
  126. data/src/main/java/org/sunflow/core/light/SphereLight.java +166 -0
  127. data/src/main/java/org/sunflow/core/light/SunSkyLight.java +362 -0
  128. data/src/main/java/org/sunflow/core/light/TriangleMeshLight.java +296 -0
  129. data/src/main/java/org/sunflow/core/modifiers/BumpMappingModifier.java +37 -0
  130. data/src/main/java/org/sunflow/core/modifiers/NormalMapModifier.java +34 -0
  131. data/src/main/java/org/sunflow/core/modifiers/PerlinModifier.java +80 -0
  132. data/src/main/java/org/sunflow/core/parser/Keyword.java +39 -0
  133. data/src/main/java/org/sunflow/core/parser/RA2Parser.java +107 -0
  134. data/src/main/java/org/sunflow/core/parser/RA3Parser.java +68 -0
  135. data/src/main/java/org/sunflow/core/parser/SCAbstractParser.java +299 -0
  136. data/src/main/java/org/sunflow/core/parser/SCAsciiParser.java +251 -0
  137. data/src/main/java/org/sunflow/core/parser/SCBinaryParser.java +156 -0
  138. data/src/main/java/org/sunflow/core/parser/SCParser.java +1403 -0
  139. data/src/main/java/org/sunflow/core/parser/ShaveRibParser.java +174 -0
  140. data/src/main/java/org/sunflow/core/parser/TriParser.java +79 -0
  141. data/src/main/java/org/sunflow/core/photonmap/CausticPhotonMap.java +429 -0
  142. data/src/main/java/org/sunflow/core/photonmap/GlobalPhotonMap.java +530 -0
  143. data/src/main/java/org/sunflow/core/photonmap/GridPhotonMap.java +308 -0
  144. data/src/main/java/org/sunflow/core/primitive/Background.java +55 -0
  145. data/src/main/java/org/sunflow/core/primitive/BanchoffSurface.java +100 -0
  146. data/src/main/java/org/sunflow/core/primitive/Box.java +210 -0
  147. data/src/main/java/org/sunflow/core/primitive/CornellBox.java +476 -0
  148. data/src/main/java/org/sunflow/core/primitive/CubeGrid.java +318 -0
  149. data/src/main/java/org/sunflow/core/primitive/Cylinder.java +104 -0
  150. data/src/main/java/org/sunflow/core/primitive/Hair.java +275 -0
  151. data/src/main/java/org/sunflow/core/primitive/JuliaFractal.java +266 -0
  152. data/src/main/java/org/sunflow/core/primitive/ParticleSurface.java +114 -0
  153. data/src/main/java/org/sunflow/core/primitive/Plane.java +163 -0
  154. data/src/main/java/org/sunflow/core/primitive/QuadMesh.java +413 -0
  155. data/src/main/java/org/sunflow/core/primitive/Sphere.java +101 -0
  156. data/src/main/java/org/sunflow/core/primitive/SphereFlake.java +234 -0
  157. data/src/main/java/org/sunflow/core/primitive/Torus.java +145 -0
  158. data/src/main/java/org/sunflow/core/primitive/TriangleMesh.java +849 -0
  159. data/src/main/java/org/sunflow/core/renderer/BucketRenderer.java +491 -0
  160. data/src/main/java/org/sunflow/core/renderer/MultipassRenderer.java +237 -0
  161. data/src/main/java/org/sunflow/core/renderer/ProgressiveRenderer.java +171 -0
  162. data/src/main/java/org/sunflow/core/renderer/SimpleRenderer.java +106 -0
  163. data/src/main/java/org/sunflow/core/shader/AmbientOcclusionShader.java +53 -0
  164. data/src/main/java/org/sunflow/core/shader/AnisotropicWardShader.java +216 -0
  165. data/src/main/java/org/sunflow/core/shader/ConstantShader.java +31 -0
  166. data/src/main/java/org/sunflow/core/shader/DiffuseShader.java +65 -0
  167. data/src/main/java/org/sunflow/core/shader/GlassShader.java +147 -0
  168. data/src/main/java/org/sunflow/core/shader/IDShader.java +27 -0
  169. data/src/main/java/org/sunflow/core/shader/MirrorShader.java +68 -0
  170. data/src/main/java/org/sunflow/core/shader/NormalShader.java +32 -0
  171. data/src/main/java/org/sunflow/core/shader/PhongShader.java +89 -0
  172. data/src/main/java/org/sunflow/core/shader/PrimIDShader.java +30 -0
  173. data/src/main/java/org/sunflow/core/shader/QuickGrayShader.java +63 -0
  174. data/src/main/java/org/sunflow/core/shader/ShinyDiffuseShader.java +98 -0
  175. data/src/main/java/org/sunflow/core/shader/SimpleShader.java +24 -0
  176. data/src/main/java/org/sunflow/core/shader/TexturedAmbientOcclusionShader.java +31 -0
  177. data/src/main/java/org/sunflow/core/shader/TexturedDiffuseShader.java +31 -0
  178. data/src/main/java/org/sunflow/core/shader/TexturedPhongShader.java +31 -0
  179. data/src/main/java/org/sunflow/core/shader/TexturedShinyDiffuseShader.java +31 -0
  180. data/src/main/java/org/sunflow/core/shader/TexturedWardShader.java +31 -0
  181. data/src/main/java/org/sunflow/core/shader/UVShader.java +27 -0
  182. data/src/main/java/org/sunflow/core/shader/UberShader.java +149 -0
  183. data/src/main/java/org/sunflow/core/shader/ViewCausticsShader.java +33 -0
  184. data/src/main/java/org/sunflow/core/shader/ViewGlobalPhotonsShader.java +25 -0
  185. data/src/main/java/org/sunflow/core/shader/ViewIrradianceShader.java +25 -0
  186. data/src/main/java/org/sunflow/core/shader/WireframeShader.java +83 -0
  187. data/src/main/java/org/sunflow/core/tesselatable/BezierMesh.java +254 -0
  188. data/src/main/java/org/sunflow/core/tesselatable/FileMesh.java +251 -0
  189. data/src/main/java/org/sunflow/core/tesselatable/Gumbo.java +1147 -0
  190. data/src/main/java/org/sunflow/core/tesselatable/Teapot.java +237 -0
  191. data/src/main/java/org/sunflow/image/Bitmap.java +15 -0
  192. data/src/main/java/org/sunflow/image/BitmapReader.java +39 -0
  193. data/src/main/java/org/sunflow/image/BitmapWriter.java +79 -0
  194. data/src/main/java/org/sunflow/image/BlackbodySpectrum.java +16 -0
  195. data/src/main/java/org/sunflow/image/ChromaticitySpectrum.java +55 -0
  196. data/src/main/java/org/sunflow/image/Color.java +374 -0
  197. data/src/main/java/org/sunflow/image/ColorEncoder.java +94 -0
  198. data/src/main/java/org/sunflow/image/ColorFactory.java +122 -0
  199. data/src/main/java/org/sunflow/image/ConstantSpectralCurve.java +21 -0
  200. data/src/main/java/org/sunflow/image/IrregularSpectralCurve.java +57 -0
  201. data/src/main/java/org/sunflow/image/RGBSpace.java +207 -0
  202. data/src/main/java/org/sunflow/image/RegularSpectralCurve.java +30 -0
  203. data/src/main/java/org/sunflow/image/SpectralCurve.java +118 -0
  204. data/src/main/java/org/sunflow/image/XYZColor.java +50 -0
  205. data/src/main/java/org/sunflow/image/formats/BitmapBlack.java +27 -0
  206. data/src/main/java/org/sunflow/image/formats/BitmapG8.java +36 -0
  207. data/src/main/java/org/sunflow/image/formats/BitmapGA8.java +30 -0
  208. data/src/main/java/org/sunflow/image/formats/BitmapRGB8.java +40 -0
  209. data/src/main/java/org/sunflow/image/formats/BitmapRGBA8.java +40 -0
  210. data/src/main/java/org/sunflow/image/formats/BitmapRGBE.java +60 -0
  211. data/src/main/java/org/sunflow/image/formats/BitmapXYZ.java +38 -0
  212. data/src/main/java/org/sunflow/image/formats/GenericBitmap.java +73 -0
  213. data/src/main/java/org/sunflow/image/readers/BMPBitmapReader.java +39 -0
  214. data/src/main/java/org/sunflow/image/readers/HDRBitmapReader.java +155 -0
  215. data/src/main/java/org/sunflow/image/readers/IGIBitmapReader.java +104 -0
  216. data/src/main/java/org/sunflow/image/readers/JPGBitmapReader.java +39 -0
  217. data/src/main/java/org/sunflow/image/readers/PNGBitmapReader.java +40 -0
  218. data/src/main/java/org/sunflow/image/readers/TGABitmapReader.java +141 -0
  219. data/src/main/java/org/sunflow/image/writers/EXRBitmapWriter.java +395 -0
  220. data/src/main/java/org/sunflow/image/writers/HDRBitmapWriter.java +54 -0
  221. data/src/main/java/org/sunflow/image/writers/IGIBitmapWriter.java +75 -0
  222. data/src/main/java/org/sunflow/image/writers/PNGBitmapWriter.java +39 -0
  223. data/src/main/java/org/sunflow/image/writers/TGABitmapWriter.java +63 -0
  224. data/src/main/java/org/sunflow/math/BoundingBox.java +340 -0
  225. data/src/main/java/org/sunflow/math/MathUtils.java +159 -0
  226. data/src/main/java/org/sunflow/math/Matrix4.java +573 -0
  227. data/src/main/java/org/sunflow/math/MovingMatrix4.java +119 -0
  228. data/src/main/java/org/sunflow/math/OrthoNormalBasis.java +110 -0
  229. data/src/main/java/org/sunflow/math/PerlinScalar.java +331 -0
  230. data/src/main/java/org/sunflow/math/PerlinVector.java +132 -0
  231. data/src/main/java/org/sunflow/math/Point2.java +36 -0
  232. data/src/main/java/org/sunflow/math/Point3.java +133 -0
  233. data/src/main/java/org/sunflow/math/QMC.java +209 -0
  234. data/src/main/java/org/sunflow/math/Solvers.java +142 -0
  235. data/src/main/java/org/sunflow/math/Vector3.java +197 -0
  236. data/src/main/java/org/sunflow/system/BenchmarkFramework.java +73 -0
  237. data/src/main/java/org/sunflow/system/BenchmarkTest.java +17 -0
  238. data/src/main/java/org/sunflow/system/ByteUtil.java +119 -0
  239. data/src/main/java/org/sunflow/system/FileUtils.java +27 -0
  240. data/src/main/java/org/sunflow/system/ImagePanel.java +282 -0
  241. data/src/main/java/org/sunflow/system/Memory.java +18 -0
  242. data/src/main/java/org/sunflow/system/Parser.java +162 -0
  243. data/src/main/java/org/sunflow/system/Plugins.java +142 -0
  244. data/src/main/java/org/sunflow/system/RenderGlobalsPanel.java +209 -0
  245. data/src/main/java/org/sunflow/system/SearchPath.java +67 -0
  246. data/src/main/java/org/sunflow/system/Timer.java +53 -0
  247. data/src/main/java/org/sunflow/system/UI.java +112 -0
  248. data/src/main/java/org/sunflow/system/UserInterface.java +46 -0
  249. data/src/main/java/org/sunflow/system/ui/ConsoleInterface.java +48 -0
  250. data/src/main/java/org/sunflow/system/ui/SilentInterface.java +28 -0
  251. data/src/main/java/org/sunflow/util/FastHashMap.java +220 -0
  252. data/src/main/java/org/sunflow/util/FloatArray.java +77 -0
  253. data/src/main/java/org/sunflow/util/IntArray.java +77 -0
  254. data/src/test/java/a_maintest.java +129 -0
  255. metadata +300 -0
@@ -0,0 +1,833 @@
1
+ package org.sunflow.core.accel;
2
+
3
+ import java.io.FileWriter;
4
+ import java.io.IOException;
5
+ import java.util.logging.Level;
6
+ import java.util.logging.Logger;
7
+
8
+ import org.sunflow.core.AccelerationStructure;
9
+ import org.sunflow.core.IntersectionState;
10
+ import org.sunflow.core.PrimitiveList;
11
+ import org.sunflow.core.Ray;
12
+ import org.sunflow.image.Color;
13
+ import org.sunflow.math.BoundingBox;
14
+ import org.sunflow.math.Point3;
15
+ import org.sunflow.system.Memory;
16
+ import org.sunflow.system.Timer;
17
+ import org.sunflow.system.UI;
18
+ import org.sunflow.system.UI.Module;
19
+ import org.sunflow.util.IntArray;
20
+
21
+ public class KDTree implements AccelerationStructure {
22
+
23
+ private int[] tree;
24
+ private int[] primitives;
25
+ private PrimitiveList primitiveList;
26
+ private BoundingBox bounds;
27
+ private int maxPrims;
28
+ private static final float INTERSECT_COST = 0.5f;
29
+ private static final float TRAVERSAL_COST = 1;
30
+ private static final float EMPTY_BONUS = 0.2f;
31
+ private static final int MAX_DEPTH = 64;
32
+ private static boolean dump = false;
33
+ private static String dumpPrefix = "kdtree";
34
+
35
+ public KDTree() {
36
+ this(0);
37
+ }
38
+
39
+ public KDTree(int maxPrims) {
40
+ this.maxPrims = maxPrims;
41
+ }
42
+
43
+ private static class BuildStats {
44
+
45
+ private int numNodes;
46
+ private int numLeaves;
47
+ private int sumObjects;
48
+ private int minObjects;
49
+ private int maxObjects;
50
+ private int sumDepth;
51
+ private int minDepth;
52
+ private int maxDepth;
53
+ private int numLeaves0;
54
+ private int numLeaves1;
55
+ private int numLeaves2;
56
+ private int numLeaves3;
57
+ private int numLeaves4;
58
+ private int numLeaves4p;
59
+
60
+ BuildStats() {
61
+ numNodes = numLeaves = 0;
62
+ sumObjects = 0;
63
+ minObjects = Integer.MAX_VALUE;
64
+ maxObjects = Integer.MIN_VALUE;
65
+ sumDepth = 0;
66
+ minDepth = Integer.MAX_VALUE;
67
+ maxDepth = Integer.MIN_VALUE;
68
+ numLeaves0 = 0;
69
+ numLeaves1 = 0;
70
+ numLeaves2 = 0;
71
+ numLeaves3 = 0;
72
+ numLeaves4 = 0;
73
+ numLeaves4p = 0;
74
+ }
75
+
76
+ void updateInner() {
77
+ numNodes++;
78
+ }
79
+
80
+ void updateLeaf(int depth, int n) {
81
+ numLeaves++;
82
+ minDepth = Math.min(depth, minDepth);
83
+ maxDepth = Math.max(depth, maxDepth);
84
+ sumDepth += depth;
85
+ minObjects = Math.min(n, minObjects);
86
+ maxObjects = Math.max(n, maxObjects);
87
+ sumObjects += n;
88
+ switch (n) {
89
+ case 0:
90
+ numLeaves0++;
91
+ break;
92
+ case 1:
93
+ numLeaves1++;
94
+ break;
95
+ case 2:
96
+ numLeaves2++;
97
+ break;
98
+ case 3:
99
+ numLeaves3++;
100
+ break;
101
+ case 4:
102
+ numLeaves4++;
103
+ break;
104
+ default:
105
+ numLeaves4p++;
106
+ break;
107
+ }
108
+ }
109
+
110
+ void printStats() {
111
+ UI.printDetailed(Module.ACCEL, "KDTree stats:");
112
+ UI.printDetailed(Module.ACCEL, " * Nodes: %d", numNodes);
113
+ UI.printDetailed(Module.ACCEL, " * Leaves: %d", numLeaves);
114
+ UI.printDetailed(Module.ACCEL, " * Objects: min %d", minObjects);
115
+ UI.printDetailed(Module.ACCEL, " avg %.2f", (float) sumObjects / numLeaves);
116
+ UI.printDetailed(Module.ACCEL, " avg(n>0) %.2f", (float) sumObjects / (numLeaves - numLeaves0));
117
+ UI.printDetailed(Module.ACCEL, " max %d", maxObjects);
118
+ UI.printDetailed(Module.ACCEL, " * Depth: min %d", minDepth);
119
+ UI.printDetailed(Module.ACCEL, " avg %.2f", (float) sumDepth / numLeaves);
120
+ UI.printDetailed(Module.ACCEL, " max %d", maxDepth);
121
+ UI.printDetailed(Module.ACCEL, " * Leaves w/: N=0 %3d%%", 100 * numLeaves0 / numLeaves);
122
+ UI.printDetailed(Module.ACCEL, " N=1 %3d%%", 100 * numLeaves1 / numLeaves);
123
+ UI.printDetailed(Module.ACCEL, " N=2 %3d%%", 100 * numLeaves2 / numLeaves);
124
+ UI.printDetailed(Module.ACCEL, " N=3 %3d%%", 100 * numLeaves3 / numLeaves);
125
+ UI.printDetailed(Module.ACCEL, " N=4 %3d%%", 100 * numLeaves4 / numLeaves);
126
+ UI.printDetailed(Module.ACCEL, " N>4 %3d%%", 100 * numLeaves4p / numLeaves);
127
+ }
128
+ }
129
+
130
+ public static void setDumpMode(boolean dump, String prefix) {
131
+ KDTree.dump = dump;
132
+ KDTree.dumpPrefix = prefix;
133
+ }
134
+
135
+ @Override
136
+ public void build(PrimitiveList primitives) {
137
+ UI.printDetailed(Module.ACCEL, "KDTree settings");
138
+ UI.printDetailed(Module.ACCEL, " * Max Leaf Size: %d", maxPrims);
139
+ UI.printDetailed(Module.ACCEL, " * Max Depth: %d", MAX_DEPTH);
140
+ UI.printDetailed(Module.ACCEL, " * Traversal cost: %.2f", TRAVERSAL_COST);
141
+ UI.printDetailed(Module.ACCEL, " * Intersect cost: %.2f", INTERSECT_COST);
142
+ UI.printDetailed(Module.ACCEL, " * Empty bonus: %.2f", EMPTY_BONUS);
143
+ UI.printDetailed(Module.ACCEL, " * Dump leaves: %s", dump ? "enabled" : "disabled");
144
+ Timer total = new Timer();
145
+ total.start();
146
+ primitiveList = primitives;
147
+ // get the object space bounds
148
+ bounds = primitives.getWorldBounds(null);
149
+ int nPrim = primitiveList.getNumPrimitives(), nSplits = 0;
150
+ BuildTask task = new BuildTask(nPrim);
151
+ Timer prepare = new Timer();
152
+ prepare.start();
153
+ for (int i = 0; i < nPrim; i++) {
154
+ for (int axis = 0; axis < 3; axis++) {
155
+ float ls = primitiveList.getPrimitiveBound(i, 2 * axis + 0);
156
+ float rs = primitiveList.getPrimitiveBound(i, 2 * axis + 1);
157
+ if (ls == rs) {
158
+ // flat in this dimension
159
+ task.splits[nSplits] = pack(ls, PLANAR, axis, i);
160
+ nSplits++;
161
+ } else {
162
+ task.splits[nSplits + 0] = pack(ls, OPENED, axis, i);
163
+ task.splits[nSplits + 1] = pack(rs, CLOSED, axis, i);
164
+ nSplits += 2;
165
+ }
166
+ }
167
+ }
168
+ task.n = nSplits;
169
+ prepare.end();
170
+ Timer t = new Timer();
171
+ IntArray tempTree = new IntArray();
172
+ IntArray tempList = new IntArray();
173
+ tempTree.add(0);
174
+ tempTree.add(1);
175
+ t.start();
176
+ // sort it
177
+ Timer sorting = new Timer();
178
+ sorting.start();
179
+ radix12(task.splits, task.n);
180
+ sorting.end();
181
+ // build the actual tree
182
+ BuildStats stats = new BuildStats();
183
+ buildTree(bounds.getMinimum().x, bounds.getMaximum().x, bounds.getMinimum().y, bounds.getMaximum().y, bounds.getMinimum().z, bounds.getMaximum().z, task, 1, tempTree, 0, tempList, stats);
184
+ t.end();
185
+ // write out final arrays
186
+ // free some memory
187
+ task = null;
188
+ tree = tempTree.trim();
189
+ tempTree = null;
190
+ this.primitives = tempList.trim();
191
+ tempList = null;
192
+ total.end();
193
+ // display some extra info
194
+ stats.printStats();
195
+ UI.printDetailed(Module.ACCEL, " * Node memory: %s", Memory.sizeof(tree));
196
+ UI.printDetailed(Module.ACCEL, " * Object memory: %s", Memory.sizeof(this.primitives));
197
+ UI.printDetailed(Module.ACCEL, " * Prepare time: %s", prepare);
198
+ UI.printDetailed(Module.ACCEL, " * Sorting time: %s", sorting);
199
+ UI.printDetailed(Module.ACCEL, " * Tree creation: %s", t);
200
+ UI.printDetailed(Module.ACCEL, " * Build time: %s", total);
201
+ if (dump) {
202
+ try {
203
+ UI.printInfo(Module.ACCEL, "Dumping mtls to %s.mtl ...", dumpPrefix);
204
+ FileWriter mtlFile = new FileWriter(dumpPrefix + ".mtl");
205
+ int maxN = stats.maxObjects;
206
+ for (int n = 0; n <= maxN; n++) {
207
+ float blend = (float) n / (float) maxN;
208
+ Color nc;
209
+ if (blend < 0.25) {
210
+ nc = Color.blend(Color.BLUE, Color.GREEN, blend / 0.25f);
211
+ } else if (blend < 0.5) {
212
+ nc = Color.blend(Color.GREEN, Color.YELLOW, (blend - 0.25f) / 0.25f);
213
+ } else if (blend < 0.75) {
214
+ nc = Color.blend(Color.YELLOW, Color.RED, (blend - 0.50f) / 0.25f);
215
+ } else {
216
+ nc = Color.MAGENTA;
217
+ }
218
+ mtlFile.write(String.format("newmtl mtl%d\n", n));
219
+ float[] rgb = nc.getRGB();
220
+ mtlFile.write("Ka 0.1 0.1 0.1\n");
221
+ mtlFile.write(String.format("Kd %.12g %.12g %.12g\n", rgb[0], rgb[1], rgb[2]));
222
+ mtlFile.write("illum 1\n\n");
223
+ }
224
+ FileWriter objFile = new FileWriter(dumpPrefix + ".obj");
225
+ UI.printInfo(Module.ACCEL, "Dumping tree to %s.obj ...", dumpPrefix);
226
+ dumpObj(0, 0, maxN, new BoundingBox(bounds), objFile, mtlFile);
227
+ objFile.close();
228
+ mtlFile.close();
229
+ } catch (IOException e) {
230
+ Logger.getLogger(KDTree.class.getName()).log(Level.SEVERE, null, e);
231
+ }
232
+ }
233
+ }
234
+
235
+ private int dumpObj(int offset, int vertOffset, int maxN, BoundingBox bounds, FileWriter file, FileWriter mtlFile) throws IOException {
236
+ if (offset == 0) {
237
+ file.write(String.format("mtllib %s.mtl\n", dumpPrefix));
238
+ }
239
+ int nextOffset = tree[offset];
240
+ String FACE_FORMAT = "f %d %d %d %d\n";
241
+ String VERTEX_FORMAT = "v %g %g %g\n";
242
+ if ((nextOffset & (3 << 30)) == (3 << 30)) {
243
+ // leaf
244
+ int n = tree[offset + 1];
245
+ if (n > 0) {
246
+ // output the current voxel to the file
247
+ Point3 min = bounds.getMinimum();
248
+ Point3 max = bounds.getMaximum();
249
+ file.write(String.format("o node%d\n", offset));
250
+ file.write(String.format(VERTEX_FORMAT, max.x, max.y, min.z));
251
+ file.write(String.format(VERTEX_FORMAT, max.x, min.y, min.z));
252
+ file.write(String.format(VERTEX_FORMAT, min.x, min.y, min.z));
253
+ file.write(String.format(VERTEX_FORMAT, min.x, max.y, min.z));
254
+ file.write(String.format(VERTEX_FORMAT, max.x, max.y, max.z));
255
+ file.write(String.format(VERTEX_FORMAT, max.x, min.y, max.z));
256
+ file.write(String.format(VERTEX_FORMAT, min.x, min.y, max.z));
257
+ file.write(String.format(VERTEX_FORMAT, min.x, max.y, max.z));
258
+ int v0 = vertOffset;
259
+ file.write(String.format("usemtl mtl%d\n", n));
260
+ file.write("s off\n");
261
+ file.write(String.format(FACE_FORMAT, v0 + 1, v0 + 2, v0 + 3, v0 + 4));
262
+ file.write(String.format(FACE_FORMAT, v0 + 5, v0 + 8, v0 + 7, v0 + 6));
263
+ file.write(String.format(FACE_FORMAT, v0 + 1, v0 + 5, v0 + 6, v0 + 2));
264
+ file.write(String.format(FACE_FORMAT, v0 + 2, v0 + 6, v0 + 7, v0 + 3));
265
+ file.write(String.format(FACE_FORMAT, v0 + 3, v0 + 7, v0 + 8, v0 + 4));
266
+ file.write(String.format(FACE_FORMAT, v0 + 5, v0 + 1, v0 + 4, v0 + 8));
267
+ vertOffset += 8;
268
+ }
269
+ return vertOffset;
270
+ } else {
271
+ // node, recurse
272
+ int axis = nextOffset & (3 << 30), v0;
273
+ float split = Float.intBitsToFloat(tree[offset + 1]), min, max;
274
+ nextOffset &= ~(3 << 30);
275
+ switch (axis) {
276
+ case 0:
277
+ max = bounds.getMaximum().x;
278
+ bounds.getMaximum().x = split;
279
+ v0 = dumpObj(nextOffset, vertOffset, maxN, bounds, file, mtlFile);
280
+ // restore and go to other side
281
+ bounds.getMaximum().x = max;
282
+ min = bounds.getMinimum().x;
283
+ bounds.getMinimum().x = split;
284
+ v0 = dumpObj(nextOffset + 2, v0, maxN, bounds, file, mtlFile);
285
+ bounds.getMinimum().x = min;
286
+ break;
287
+ case 1 << 30:
288
+ max = bounds.getMaximum().y;
289
+ bounds.getMaximum().y = split;
290
+ v0 = dumpObj(nextOffset, vertOffset, maxN, bounds, file, mtlFile);
291
+ // restore and go to other side
292
+ bounds.getMaximum().y = max;
293
+ min = bounds.getMinimum().y;
294
+ bounds.getMinimum().y = split;
295
+ v0 = dumpObj(nextOffset + 2, v0, maxN, bounds, file, mtlFile);
296
+ bounds.getMinimum().y = min;
297
+ break;
298
+ case 2 << 30:
299
+ max = bounds.getMaximum().z;
300
+ bounds.getMaximum().z = split;
301
+ v0 = dumpObj(nextOffset, vertOffset, maxN, bounds, file, mtlFile);
302
+ // restore and go to other side
303
+ bounds.getMaximum().z = max;
304
+ min = bounds.getMinimum().z;
305
+ bounds.getMinimum().z = split;
306
+ v0 = dumpObj(nextOffset + 2, v0, maxN, bounds, file, mtlFile);
307
+ // restore and go to other side
308
+ bounds.getMinimum().z = min;
309
+ break;
310
+ default:
311
+ v0 = vertOffset;
312
+ break;
313
+ }
314
+ return v0;
315
+ }
316
+ }
317
+ // type is encoded as 2 shifted bits
318
+ private static final long CLOSED = 0L << 30;
319
+ private static final long PLANAR = 1L << 30;
320
+ private static final long OPENED = 2L << 30;
321
+ private static final long TYPE_MASK = 3L << 30;
322
+
323
+ // pack split values into a 64bit integer
324
+ private static long pack(float split, long type, int axis, int object) {
325
+ // pack float in sortable form
326
+ int f = Float.floatToRawIntBits(split);
327
+ int top = f ^ ((f >> 31) | 0x80000000);
328
+ long p = (top & 0xFFFFFFFFL) << 32;
329
+ p |= type; // encode type as 2 bits
330
+ p |= ((long) axis) << 28; // encode axis as 2 bits
331
+ p |= (object & 0xFFFFFFFL); // pack object number
332
+ return p;
333
+ }
334
+
335
+ private static int unpackObject(long p) {
336
+ return (int) (p & 0xFFFFFFFL);
337
+ }
338
+
339
+ private static int unpackAxis(long p) {
340
+ return (int) (p >>> 28) & 3;
341
+ }
342
+
343
+ private static long unpackSplitType(long p) {
344
+ return p & TYPE_MASK;
345
+ }
346
+
347
+ private static float unpackSplit(long p) {
348
+ int f = (int) ((p >>> 32) & 0xFFFFFFFFL);
349
+ int m = ((f >>> 31) - 1) | 0x80000000;
350
+ return Float.intBitsToFloat(f ^ m);
351
+ }
352
+
353
+ // radix sort on top 36 bits - returns sorted result
354
+ private static void radix12(long[] splits, int n) {
355
+ // allocate working memory
356
+ final int[] hist = new int[2048];
357
+ final long[] sorted = new long[n];
358
+ // parallel histogramming pass
359
+ for (int i = 0; i < n; i++) {
360
+ long pi = splits[i];
361
+ hist[0x000 + ((int) (pi >>> 28) & 0x1FF)]++;
362
+ hist[0x200 + ((int) (pi >>> 37) & 0x1FF)]++;
363
+ hist[0x400 + ((int) (pi >>> 46) & 0x1FF)]++;
364
+ hist[0x600 + ((int) (pi >>> 55))]++;
365
+ }
366
+
367
+ // sum the histograms - each histogram entry records the number of
368
+ // values preceding itself.
369
+ {
370
+ int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0;
371
+ int tsum;
372
+ for (int i = 0; i < 512; i++) {
373
+ tsum = hist[0x000 + i] + sum0;
374
+ hist[0x000 + i] = sum0 - 1;
375
+ sum0 = tsum;
376
+ tsum = hist[0x200 + i] + sum1;
377
+ hist[0x200 + i] = sum1 - 1;
378
+ sum1 = tsum;
379
+ tsum = hist[0x400 + i] + sum2;
380
+ hist[0x400 + i] = sum2 - 1;
381
+ sum2 = tsum;
382
+ tsum = hist[0x600 + i] + sum3;
383
+ hist[0x600 + i] = sum3 - 1;
384
+ sum3 = tsum;
385
+ }
386
+ }
387
+
388
+ // read/write histogram passes
389
+ for (int i = 0; i < n; i++) {
390
+ long pi = splits[i];
391
+ int pos = (int) (pi >>> 28) & 0x1FF;
392
+ sorted[++hist[0x000 + pos]] = pi;
393
+ }
394
+ for (int i = 0; i < n; i++) {
395
+ long pi = sorted[i];
396
+ int pos = (int) (pi >>> 37) & 0x1FF;
397
+ splits[++hist[0x200 + pos]] = pi;
398
+ }
399
+ for (int i = 0; i < n; i++) {
400
+ long pi = splits[i];
401
+ int pos = (int) (pi >>> 46) & 0x1FF;
402
+ sorted[++hist[0x400 + pos]] = pi;
403
+ }
404
+ for (int i = 0; i < n; i++) {
405
+ long pi = sorted[i];
406
+ int pos = (int) (pi >>> 55);
407
+ splits[++hist[0x600 + pos]] = pi;
408
+ }
409
+ }
410
+
411
+ private static class BuildTask {
412
+
413
+ long[] splits;
414
+ int numObjects;
415
+ int n;
416
+ byte[] leftRightTable;
417
+
418
+ BuildTask(int numObjects) {
419
+ splits = new long[6 * numObjects];
420
+ this.numObjects = numObjects;
421
+ n = 0;
422
+ // 2 bits per object
423
+ leftRightTable = new byte[(numObjects + 3) / 4];
424
+ }
425
+
426
+ BuildTask(int numObjects, BuildTask parent) {
427
+ splits = new long[6 * numObjects];
428
+ this.numObjects = numObjects;
429
+ n = 0;
430
+ leftRightTable = parent.leftRightTable;
431
+ }
432
+ }
433
+
434
+ private void buildTree(float minx, float maxx, float miny, float maxy, float minz, float maxz, BuildTask task, int depth, IntArray tempTree, int offset, IntArray tempList, BuildStats stats) {
435
+ // get node bounding box extents
436
+ if (task.numObjects > maxPrims && depth < MAX_DEPTH) {
437
+ float dx = maxx - minx;
438
+ float dy = maxy - miny;
439
+ float dz = maxz - minz;
440
+ // search for best possible split
441
+ float bestCost = INTERSECT_COST * task.numObjects;
442
+ int bestAxis = -1;
443
+ int bestOffsetStart = -1;
444
+ int bestOffsetEnd = -1;
445
+ float bestSplit = 0;
446
+ boolean bestPlanarLeft = false;
447
+ int bnl = 0, bnr = 0;
448
+ // inverse area of the bounding box (factor of 2 ommitted)
449
+ float area = (dx * dy + dy * dz + dz * dx);
450
+ float ISECT_COST = INTERSECT_COST / area;
451
+ // setup counts for each axis
452
+ int[] nl = {0, 0, 0};
453
+ int[] nr = {task.numObjects, task.numObjects, task.numObjects};
454
+ // setup bounds for each axis
455
+ float[] dp = {dy * dz, dz * dx, dx * dy};
456
+ float[] ds = {dy + dz, dz + dx, dx + dy};
457
+ float[] nodeMin = {minx, miny, minz};
458
+ float[] nodeMax = {maxx, maxy, maxz};
459
+ // search for best cost
460
+ int nSplits = task.n;
461
+ long[] splits = task.splits;
462
+ byte[] lrtable = task.leftRightTable;
463
+ for (int i = 0; i < nSplits;) {
464
+ // extract current split
465
+ long ptr = splits[i];
466
+ float split = unpackSplit(ptr);
467
+ int axis = unpackAxis(ptr);
468
+ // mark current position
469
+ int currentOffset = i;
470
+ // count number of primitives start/stopping/lying on the
471
+ // current plane
472
+ int pClosed = 0, pPlanar = 0, pOpened = 0;
473
+ long ptrMasked = ptr & (~TYPE_MASK & 0xFFFFFFFFF0000000L);
474
+ long ptrClosed = ptrMasked | CLOSED;
475
+ long ptrPlanar = ptrMasked | PLANAR;
476
+ long ptrOpened = ptrMasked | OPENED;
477
+ while (i < nSplits && (splits[i] & 0xFFFFFFFFF0000000L) == ptrClosed) {
478
+ int obj = unpackObject(splits[i]);
479
+ lrtable[obj >>> 2] = 0;
480
+ pClosed++;
481
+ i++;
482
+ }
483
+ while (i < nSplits && (splits[i] & 0xFFFFFFFFF0000000L) == ptrPlanar) {
484
+ int obj = unpackObject(splits[i]);
485
+ lrtable[obj >>> 2] = 0;
486
+ pPlanar++;
487
+ i++;
488
+ }
489
+ while (i < nSplits && (splits[i] & 0xFFFFFFFFF0000000L) == ptrOpened) {
490
+ int obj = unpackObject(splits[i]);
491
+ lrtable[obj >>> 2] = 0;
492
+ pOpened++;
493
+ i++;
494
+ }
495
+ // now we have summed all contributions from this plane
496
+ nr[axis] -= pPlanar + pClosed;
497
+ // compute cost
498
+ if (split >= nodeMin[axis] && split <= nodeMax[axis]) {
499
+ // left and right surface area (factor of 2 ommitted)
500
+ float dl = split - nodeMin[axis];
501
+ float dr = nodeMax[axis] - split;
502
+ float lp = dp[axis] + dl * ds[axis];
503
+ float rp = dp[axis] + dr * ds[axis];
504
+ // planar prims go to smallest cell always
505
+ boolean planarLeft = dl < dr;
506
+ int numLeft = nl[axis] + (planarLeft ? pPlanar : 0);
507
+ int numRight = nr[axis] + (planarLeft ? 0 : pPlanar);
508
+ float eb = ((numLeft == 0 && dl > 0) || (numRight == 0 && dr > 0)) ? EMPTY_BONUS : 0;
509
+ float cost = TRAVERSAL_COST + ISECT_COST * (1 - eb) * (lp * numLeft + rp * numRight);
510
+ if (cost < bestCost) {
511
+ bestCost = cost;
512
+ bestAxis = axis;
513
+ bestSplit = split;
514
+ bestOffsetStart = currentOffset;
515
+ bestOffsetEnd = i;
516
+ bnl = numLeft;
517
+ bnr = numRight;
518
+ bestPlanarLeft = planarLeft;
519
+ }
520
+ }
521
+ // move objects left
522
+ nl[axis] += pOpened + pPlanar;
523
+ }
524
+ // debug check for correctness of the scan
525
+ for (int axis = 0; axis < 3; axis++) {
526
+ int numLeft = nl[axis];
527
+ int numRight = nr[axis];
528
+ if (numLeft != task.numObjects || numRight != 0) {
529
+ UI.printError(Module.ACCEL, "Didn't scan full range of objects @depth=%d. Left overs for axis %d: [L: %d] [R: %d]", depth, axis, numLeft, numRight);
530
+ }
531
+ }
532
+ // found best split?
533
+ if (bestAxis != -1) {
534
+ // allocate space for child nodes
535
+ BuildTask taskL = new BuildTask(bnl, task);
536
+ BuildTask taskR = new BuildTask(bnr, task);
537
+ int lk = 0, rk = 0;
538
+ for (int i = 0; i < bestOffsetStart; i++) {
539
+ long ptr = splits[i];
540
+ if (unpackAxis(ptr) == bestAxis) {
541
+ if (unpackSplitType(ptr) != CLOSED) {
542
+ int obj = unpackObject(ptr);
543
+ lrtable[obj >>> 2] |= 1 << ((obj & 3) << 1);
544
+ lk++;
545
+ }
546
+ }
547
+ }
548
+ for (int i = bestOffsetStart; i < bestOffsetEnd; i++) {
549
+ long ptr = splits[i];
550
+ assert unpackAxis(ptr) == bestAxis;
551
+ if (unpackSplitType(ptr) == PLANAR) {
552
+ if (bestPlanarLeft) {
553
+ int obj = unpackObject(ptr);
554
+ lrtable[obj >>> 2] |= 1 << ((obj & 3) << 1);
555
+ lk++;
556
+ } else {
557
+ int obj = unpackObject(ptr);
558
+ lrtable[obj >>> 2] |= 2 << ((obj & 3) << 1);
559
+ rk++;
560
+ }
561
+ }
562
+ }
563
+ for (int i = bestOffsetEnd; i < nSplits; i++) {
564
+ long ptr = splits[i];
565
+ if (unpackAxis(ptr) == bestAxis) {
566
+ if (unpackSplitType(ptr) != OPENED) {
567
+ int obj = unpackObject(ptr);
568
+ lrtable[obj >>> 2] |= 2 << ((obj & 3) << 1);
569
+ rk++;
570
+ }
571
+ }
572
+ }
573
+ // output new splits while maintaining order
574
+ long[] splitsL = taskL.splits;
575
+ long[] splitsR = taskR.splits;
576
+ int nsl = 0, nsr = 0;
577
+ for (int i = 0; i < nSplits; i++) {
578
+ long ptr = splits[i];
579
+ int obj = unpackObject(ptr);
580
+ int idx = obj >>> 2;
581
+ int mask = 1 << ((obj & 3) << 1);
582
+ if ((lrtable[idx] & mask) != 0) {
583
+ splitsL[nsl] = ptr;
584
+ nsl++;
585
+ }
586
+ if ((lrtable[idx] & (mask << 1)) != 0) {
587
+ splitsR[nsr] = ptr;
588
+ nsr++;
589
+ }
590
+ }
591
+ taskL.n = nsl;
592
+ taskR.n = nsr;
593
+ // free more memory
594
+ task.splits = splits = splitsL = splitsR = null;
595
+ task = null;
596
+ // allocate child nodes
597
+ int nextOffset = tempTree.getSize();
598
+ tempTree.add(0);
599
+ tempTree.add(0);
600
+ tempTree.add(0);
601
+ tempTree.add(0);
602
+ // create current node
603
+ tempTree.set(offset + 0, (bestAxis << 30) | nextOffset);
604
+ tempTree.set(offset + 1, Float.floatToRawIntBits(bestSplit));
605
+ // recurse for child nodes - free object arrays after each step
606
+ stats.updateInner();
607
+ switch (bestAxis) {
608
+ case 0:
609
+ buildTree(minx, bestSplit, miny, maxy, minz, maxz, taskL, depth + 1, tempTree, nextOffset, tempList, stats);
610
+ taskL = null;
611
+ buildTree(bestSplit, maxx, miny, maxy, minz, maxz, taskR, depth + 1, tempTree, nextOffset + 2, tempList, stats);
612
+ taskR = null;
613
+ return;
614
+ case 1:
615
+ buildTree(minx, maxx, miny, bestSplit, minz, maxz, taskL, depth + 1, tempTree, nextOffset, tempList, stats);
616
+ taskL = null;
617
+ buildTree(minx, maxx, bestSplit, maxy, minz, maxz, taskR, depth + 1, tempTree, nextOffset + 2, tempList, stats);
618
+ taskR = null;
619
+ return;
620
+ case 2:
621
+ buildTree(minx, maxx, miny, maxy, minz, bestSplit, taskL, depth + 1, tempTree, nextOffset, tempList, stats);
622
+ taskL = null;
623
+ buildTree(minx, maxx, miny, maxy, bestSplit, maxz, taskR, depth + 1, tempTree, nextOffset + 2, tempList, stats);
624
+ taskR = null;
625
+ return;
626
+ default:
627
+ assert false;
628
+ }
629
+ }
630
+ }
631
+ // create leaf node
632
+ int listOffset = tempList.getSize();
633
+ int n = 0;
634
+ for (int i = 0; i < task.n; i++) {
635
+ long ptr = task.splits[i];
636
+ if (unpackAxis(ptr) == 0 && unpackSplitType(ptr) != CLOSED) {
637
+ tempList.add(unpackObject(ptr));
638
+ n++;
639
+ }
640
+ }
641
+ stats.updateLeaf(depth, n);
642
+ if (n != task.numObjects) {
643
+ UI.printError(Module.ACCEL, "Error creating leaf node - expecting %d found %d", task.numObjects, n);
644
+ }
645
+ tempTree.set(offset + 0, (3 << 30) | listOffset);
646
+ tempTree.set(offset + 1, task.numObjects);
647
+ // free some memory
648
+ task.splits = null;
649
+ }
650
+
651
+ @Override
652
+ public void intersect(Ray r, IntersectionState state) {
653
+ float intervalMin = r.getMin();
654
+ float intervalMax = r.getMax();
655
+ float orgX = r.ox;
656
+ float dirX = r.dx, invDirX = 1 / dirX;
657
+ float t1, t2;
658
+ t1 = (bounds.getMinimum().x - orgX) * invDirX;
659
+ t2 = (bounds.getMaximum().x - orgX) * invDirX;
660
+ if (invDirX > 0) {
661
+ if (t1 > intervalMin) {
662
+ intervalMin = t1;
663
+ }
664
+ if (t2 < intervalMax) {
665
+ intervalMax = t2;
666
+ }
667
+ } else {
668
+ if (t2 > intervalMin) {
669
+ intervalMin = t2;
670
+ }
671
+ if (t1 < intervalMax) {
672
+ intervalMax = t1;
673
+ }
674
+ }
675
+ if (intervalMin > intervalMax) {
676
+ return;
677
+ }
678
+ float orgY = r.oy;
679
+ float dirY = r.dy, invDirY = 1 / dirY;
680
+ t1 = (bounds.getMinimum().y - orgY) * invDirY;
681
+ t2 = (bounds.getMaximum().y - orgY) * invDirY;
682
+ if (invDirY > 0) {
683
+ if (t1 > intervalMin) {
684
+ intervalMin = t1;
685
+ }
686
+ if (t2 < intervalMax) {
687
+ intervalMax = t2;
688
+ }
689
+ } else {
690
+ if (t2 > intervalMin) {
691
+ intervalMin = t2;
692
+ }
693
+ if (t1 < intervalMax) {
694
+ intervalMax = t1;
695
+ }
696
+ }
697
+ if (intervalMin > intervalMax) {
698
+ return;
699
+ }
700
+ float orgZ = r.oz;
701
+ float dirZ = r.dz, invDirZ = 1 / dirZ;
702
+ t1 = (bounds.getMinimum().z - orgZ) * invDirZ;
703
+ t2 = (bounds.getMaximum().z - orgZ) * invDirZ;
704
+ if (invDirZ > 0) {
705
+ if (t1 > intervalMin) {
706
+ intervalMin = t1;
707
+ }
708
+ if (t2 < intervalMax) {
709
+ intervalMax = t2;
710
+ }
711
+ } else {
712
+ if (t2 > intervalMin) {
713
+ intervalMin = t2;
714
+ }
715
+ if (t1 < intervalMax) {
716
+ intervalMax = t1;
717
+ }
718
+ }
719
+ if (intervalMin > intervalMax) {
720
+ return;
721
+ }
722
+
723
+ // compute custom offsets from direction sign bit
724
+ int offsetXFront = (Float.floatToRawIntBits(dirX) & (1 << 31)) >>> 30;
725
+ int offsetYFront = (Float.floatToRawIntBits(dirY) & (1 << 31)) >>> 30;
726
+ int offsetZFront = (Float.floatToRawIntBits(dirZ) & (1 << 31)) >>> 30;
727
+
728
+ int offsetXBack = offsetXFront ^ 2;
729
+ int offsetYBack = offsetYFront ^ 2;
730
+ int offsetZBack = offsetZFront ^ 2;
731
+
732
+ IntersectionState.StackNode[] stack = state.getStack();
733
+ int stackPos = 0;
734
+ int node = 0;
735
+
736
+ while (true) {
737
+ int tn = tree[node];
738
+ int axis = tn & (3 << 30);
739
+ int offset = tn & ~(3 << 30);
740
+ switch (axis) {
741
+ case 0: {
742
+ float d = (Float.intBitsToFloat(tree[node + 1]) - orgX) * invDirX;
743
+ int back = offset + offsetXBack;
744
+ node = back;
745
+ if (d < intervalMin) {
746
+ continue;
747
+ }
748
+ node = offset + offsetXFront; // front
749
+ if (d > intervalMax) {
750
+ continue;
751
+ }
752
+ // push back node
753
+ stack[stackPos].node = back;
754
+ stack[stackPos].near = (d >= intervalMin) ? d : intervalMin;
755
+ stack[stackPos].far = intervalMax;
756
+ stackPos++;
757
+ // update ray interval for front node
758
+ intervalMax = (d <= intervalMax) ? d : intervalMax;
759
+ continue;
760
+ }
761
+ case 1 << 30: {
762
+ // y axis
763
+ float d = (Float.intBitsToFloat(tree[node + 1]) - orgY) * invDirY;
764
+ int back = offset + offsetYBack;
765
+ node = back;
766
+ if (d < intervalMin) {
767
+ continue;
768
+ }
769
+ node = offset + offsetYFront; // front
770
+ if (d > intervalMax) {
771
+ continue;
772
+ }
773
+ // push back node
774
+ stack[stackPos].node = back;
775
+ stack[stackPos].near = (d >= intervalMin) ? d : intervalMin;
776
+ stack[stackPos].far = intervalMax;
777
+ stackPos++;
778
+ // update ray interval for front node
779
+ intervalMax = (d <= intervalMax) ? d : intervalMax;
780
+ continue;
781
+ }
782
+ case 2 << 30: {
783
+ // z axis
784
+ float d = (Float.intBitsToFloat(tree[node + 1]) - orgZ) * invDirZ;
785
+ int back = offset + offsetZBack;
786
+ node = back;
787
+ if (d < intervalMin) {
788
+ continue;
789
+ }
790
+ node = offset + offsetZFront; // front
791
+ if (d > intervalMax) {
792
+ continue;
793
+ }
794
+ // push back node
795
+ stack[stackPos].node = back;
796
+ stack[stackPos].near = (d >= intervalMin) ? d : intervalMin;
797
+ stack[stackPos].far = intervalMax;
798
+ stackPos++;
799
+ // update ray interval for front node
800
+ intervalMax = (d <= intervalMax) ? d : intervalMax;
801
+ continue;
802
+ }
803
+ default: {
804
+ // leaf - test some objects
805
+ int n = tree[node + 1];
806
+ while (n > 0) {
807
+ primitiveList.intersectPrimitive(r, primitives[offset], state);
808
+ n--;
809
+ offset++;
810
+ }
811
+ if (r.getMax() < intervalMax) {
812
+ return;
813
+ }
814
+ do {
815
+ // stack is empty?
816
+ if (stackPos == 0) {
817
+ return;
818
+ }
819
+ // move back up the stack
820
+ stackPos--;
821
+ intervalMin = stack[stackPos].near;
822
+ if (r.getMax() < intervalMin) {
823
+ continue;
824
+ }
825
+ node = stack[stackPos].node;
826
+ intervalMax = stack[stackPos].far;
827
+ break;
828
+ } while (true);
829
+ }
830
+ } // switch
831
+ } // traversal loop
832
+ }
833
+ }