joonsrenderer 1.1-java

Sign up to get free protection for your applications and to get access to all the features.
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
+ }