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,85 @@
1
+ package org.sunflow.core;
2
+
3
+ import org.sunflow.system.UI;
4
+ import org.sunflow.system.UI.Module;
5
+
6
+ public class Statistics {
7
+ // raytracing
8
+
9
+ private long numEyeRays;
10
+ private long numShadowRays;
11
+ private long numReflectionRays;
12
+ private long numGlossyRays;
13
+ private long numRefractionRays;
14
+ private long numRays;
15
+ private long numPixels;
16
+ // shading cache
17
+ private long cacheHits;
18
+ private long cacheMisses;
19
+ private long cacheSumDepth;
20
+ private long cacheNumCaches;
21
+
22
+ Statistics() {
23
+ reset();
24
+ }
25
+
26
+ final void reset() {
27
+ numEyeRays = 0;
28
+ numShadowRays = 0;
29
+ numReflectionRays = 0;
30
+ numGlossyRays = 0;
31
+ numRefractionRays = 0;
32
+ numRays = 0;
33
+ numPixels = 0;
34
+ cacheHits = 0;
35
+ cacheMisses = 0;
36
+ cacheSumDepth = 0;
37
+ cacheNumCaches = 0;
38
+ }
39
+
40
+ void accumulate(IntersectionState state) {
41
+ numEyeRays += state.numEyeRays;
42
+ numShadowRays += state.numShadowRays;
43
+ numReflectionRays += state.numReflectionRays;
44
+ numGlossyRays += state.numGlossyRays;
45
+ numRefractionRays += state.numRefractionRays;
46
+ numRays += state.numRays;
47
+ }
48
+
49
+ void accumulate(ShadingCache cache) {
50
+ cacheHits += cache.hits;
51
+ cacheMisses += cache.misses;
52
+ cacheSumDepth += cache.sumDepth;
53
+ cacheNumCaches += cache.numCaches;
54
+ }
55
+
56
+ void setResolution(int w, int h) {
57
+ numPixels = w * h;
58
+ }
59
+
60
+ void displayStats() {
61
+ // display raytracing stats
62
+ UI.printInfo(Module.SCENE, "Raytracing stats:");
63
+ UI.printInfo(Module.SCENE, " * Rays traced: (per pixel) (per eye ray) (percentage)", numRays);
64
+ printRayTypeStats("eye", numEyeRays);
65
+ printRayTypeStats("shadow", numShadowRays);
66
+ printRayTypeStats("reflection", numReflectionRays);
67
+ printRayTypeStats("glossy", numGlossyRays);
68
+ printRayTypeStats("refraction", numRefractionRays);
69
+ printRayTypeStats("other", numRays - numEyeRays - numShadowRays - numReflectionRays - numGlossyRays - numRefractionRays);
70
+ printRayTypeStats("total", numRays);
71
+ if (cacheHits + cacheMisses > 0) {
72
+ UI.printInfo(Module.LIGHT, "Shading cache stats:");
73
+ UI.printInfo(Module.LIGHT, " * Lookups: %d", cacheHits + cacheMisses);
74
+ UI.printInfo(Module.LIGHT, " * Hits: %d", cacheHits);
75
+ UI.printInfo(Module.LIGHT, " * Hit rate: %d%%", (100 * cacheHits) / (cacheHits + cacheMisses));
76
+ UI.printInfo(Module.LIGHT, " * Average cache depth: %.2f", (double) cacheSumDepth / (double) cacheNumCaches);
77
+ }
78
+ }
79
+
80
+ private void printRayTypeStats(String name, long n) {
81
+ if (n > 0) {
82
+ UI.printInfo(Module.SCENE, " %-10s %11d %7.2f %7.2f %6.2f%%", name, n, (double) n / (double) numPixels, (double) n / (double) numEyeRays, (double) (n * 100) / (double) numRays);
83
+ }
84
+ }
85
+ }
@@ -0,0 +1,36 @@
1
+ package org.sunflow.core;
2
+
3
+ import org.sunflow.core.primitive.TriangleMesh;
4
+ import org.sunflow.math.BoundingBox;
5
+ import org.sunflow.math.Matrix4;
6
+
7
+ /**
8
+ * Represents an object which can be tesselated into a list of primitives such
9
+ * as a {@link TriangleMesh}.
10
+ */
11
+ public interface Tesselatable extends RenderObject {
12
+
13
+ /**
14
+ * Tesselate this object into a {@link PrimitiveList}. This may return
15
+ * <code>null</code> if tesselation fails.
16
+ *
17
+ * @return a list of primitives generated by the tesselation
18
+ */
19
+ public PrimitiveList tesselate();
20
+
21
+ /**
22
+ * Compute a bounding box of this object in world space, using the specified
23
+ * object-to-world transformation matrix. The bounds should be as exact as
24
+ * possible, if they are difficult or expensive to compute exactly, you may
25
+ * use {@link Matrix4#transform(BoundingBox)}. If the matrix is
26
+ * <code>null</code> no transformation is needed, and object space is
27
+ * equivalent to world space. This method may return
28
+ * <code>null</code> if these bounds are difficult or impossible to compute,
29
+ * in which case the tesselation will be executed right away and the bounds
30
+ * of the resulting primitives will be used.
31
+ *
32
+ * @param o2w object to world transformation matrix
33
+ * @return object bounding box in world space
34
+ */
35
+ public BoundingBox getWorldBounds(Matrix4 o2w);
36
+ }
@@ -0,0 +1,128 @@
1
+ package org.sunflow.core;
2
+
3
+ import java.io.IOException;
4
+
5
+ import org.sunflow.PluginRegistry;
6
+ import org.sunflow.image.Bitmap;
7
+ import org.sunflow.image.BitmapReader;
8
+ import org.sunflow.image.Color;
9
+ import org.sunflow.image.BitmapReader.BitmapFormatException;
10
+ import org.sunflow.image.formats.BitmapBlack;
11
+ import org.sunflow.math.MathUtils;
12
+ import org.sunflow.math.OrthoNormalBasis;
13
+ import org.sunflow.math.Vector3;
14
+ import org.sunflow.system.FileUtils;
15
+ import org.sunflow.system.UI;
16
+ import org.sunflow.system.UI.Module;
17
+
18
+ /**
19
+ * Represents a 2D texture, typically used by {@link Shader shaders}.
20
+ */
21
+ public class Texture {
22
+
23
+ private String filename;
24
+ private boolean isLinear;
25
+ private Bitmap bitmap;
26
+ private int loaded;
27
+
28
+ /**
29
+ * Creates a new texture from the specfied file.
30
+ *
31
+ * @param filename image file to load
32
+ * @param isLinear is the texture gamma corrected already?
33
+ */
34
+ Texture(String filename, boolean isLinear) {
35
+ this.filename = filename;
36
+ this.isLinear = isLinear;
37
+ loaded = 0;
38
+ }
39
+
40
+ private synchronized void load() {
41
+ if (loaded != 0) {
42
+ return;
43
+ }
44
+ String extension = FileUtils.getExtension(filename);
45
+ try {
46
+ UI.printInfo(Module.TEX, "Reading texture bitmap from: \"%s\" ...", filename);
47
+ BitmapReader reader = PluginRegistry.BITMAP_READER_PLUGINS.createObject(extension);
48
+ if (reader != null) {
49
+ bitmap = reader.load(filename, isLinear);
50
+ if (bitmap.getWidth() == 0 || bitmap.getHeight() == 0) {
51
+ bitmap = null;
52
+ }
53
+ }
54
+ if (bitmap == null) {
55
+ UI.printError(Module.TEX, "Bitmap reading failed");
56
+ bitmap = new BitmapBlack();
57
+ } else {
58
+ UI.printDetailed(Module.TEX, "Texture bitmap reading complete: %dx%d pixels found", bitmap.getWidth(), bitmap.getHeight());
59
+ }
60
+ } catch (IOException e) {
61
+ UI.printError(Module.TEX, "%s", e.getMessage());
62
+ } catch (BitmapFormatException e) {
63
+ UI.printError(Module.TEX, "%s format error: %s", extension, e.getMessage());
64
+ }
65
+ loaded = 1;
66
+ }
67
+
68
+ public Bitmap getBitmap() {
69
+ if (loaded == 0) {
70
+ load();
71
+ }
72
+ return bitmap;
73
+ }
74
+
75
+ /**
76
+ * Gets the color at location (x,y) in the texture. The lookup is performed
77
+ * using the fractional component of the coordinates, treating the texture
78
+ * as a unit square tiled in both directions. Bicubic filtering is performed
79
+ * on the four nearest pixels to the lookup point.
80
+ *
81
+ * @param x x coordinate into the texture
82
+ * @param y y coordinate into the texture
83
+ * @return filtered color at location (x,y)
84
+ */
85
+ public Color getPixel(float x, float y) {
86
+ Bitmap bitmapc = getBitmap();
87
+ x = MathUtils.frac(x);
88
+ y = MathUtils.frac(y);
89
+ float dx = x * (bitmapc.getWidth() - 1);
90
+ float dy = y * (bitmapc.getHeight() - 1);
91
+ int ix0 = (int) dx;
92
+ int iy0 = (int) dy;
93
+ int ix1 = (ix0 + 1) % bitmapc.getWidth();
94
+ int iy1 = (iy0 + 1) % bitmapc.getHeight();
95
+ float u = dx - ix0;
96
+ float v = dy - iy0;
97
+ u = u * u * (3.0f - (2.0f * u));
98
+ v = v * v * (3.0f - (2.0f * v));
99
+ float k00 = (1.0f - u) * (1.0f - v);
100
+ Color c00 = bitmapc.readColor(ix0, iy0);
101
+ float k01 = (1.0f - u) * v;
102
+ Color c01 = bitmapc.readColor(ix0, iy1);
103
+ float k10 = u * (1.0f - v);
104
+ Color c10 = bitmapc.readColor(ix1, iy0);
105
+ float k11 = u * v;
106
+ Color c11 = bitmapc.readColor(ix1, iy1);
107
+ Color c = Color.mul(k00, c00);
108
+ c.madd(k01, c01);
109
+ c.madd(k10, c10);
110
+ c.madd(k11, c11);
111
+ return c;
112
+ }
113
+
114
+ public Vector3 getNormal(float x, float y, OrthoNormalBasis basis) {
115
+ float[] rgb = getPixel(x, y).getRGB();
116
+ return basis.transform(new Vector3(2 * rgb[0] - 1, 2 * rgb[1] - 1, 2 * rgb[2] - 1)).normalize();
117
+ }
118
+
119
+ public Vector3 getBump(float x, float y, OrthoNormalBasis basis, float scale) {
120
+ Bitmap bitmapv = getBitmap();
121
+ float dx = 1.0f / bitmapv.getWidth();
122
+ float dy = 1.0f / bitmapv.getHeight();
123
+ float b0 = getPixel(x, y).getLuminance();
124
+ float bx = getPixel(x + dx, y).getLuminance();
125
+ float by = getPixel(x, y + dy).getLuminance();
126
+ return basis.transform(new Vector3(scale * (b0 - bx), scale * (b0 - by), 1)).normalize();
127
+ }
128
+ }
@@ -0,0 +1,48 @@
1
+ package org.sunflow.core;
2
+
3
+ import java.util.HashMap;
4
+
5
+ import org.sunflow.system.UI;
6
+ import org.sunflow.system.UI.Module;
7
+
8
+ /**
9
+ * Maintains a cache of all loaded texture maps. This is usefull if the same
10
+ * texture might be used more than once in your scene.
11
+ */
12
+ public final class TextureCache {
13
+
14
+ private static HashMap<String, Texture> textures = new HashMap<String, Texture>();
15
+
16
+ private TextureCache() {
17
+ }
18
+
19
+ /**
20
+ * Gets a reference to the texture specified by the given filename. If the
21
+ * texture has already been loaded the previous reference is returned,
22
+ * otherwise, a new texture is created.
23
+ *
24
+ * @param filename image file to load
25
+ * @param isLinear is the texture gamma corrected?
26
+ * @return texture object
27
+ * @see Texture
28
+ */
29
+ public synchronized static Texture getTexture(String filename, boolean isLinear) {
30
+ if (textures.containsKey(filename)) {
31
+ UI.printInfo(Module.TEX, "Using cached copy for file \"%s\" ...", filename);
32
+ return textures.get(filename);
33
+ }
34
+ UI.printInfo(Module.TEX, "Using file \"%s\" ...", filename);
35
+ Texture t = new Texture(filename, isLinear);
36
+ textures.put(filename, t);
37
+ return t;
38
+ }
39
+
40
+ /**
41
+ * Flush all textures from the cache, this will cause them to be reloaded
42
+ * anew the next time they are accessed.
43
+ */
44
+ public synchronized static void flush() {
45
+ UI.printInfo(Module.TEX, "Flushing texture cache");
46
+ textures.clear();
47
+ }
48
+ }
@@ -0,0 +1,652 @@
1
+ package org.sunflow.core.accel;
2
+
3
+ import org.sunflow.core.AccelerationStructure;
4
+ import org.sunflow.core.IntersectionState;
5
+ import org.sunflow.core.PrimitiveList;
6
+ import org.sunflow.core.Ray;
7
+ import org.sunflow.math.BoundingBox;
8
+ import org.sunflow.system.Memory;
9
+ import org.sunflow.system.Timer;
10
+ import org.sunflow.system.UI;
11
+ import org.sunflow.system.UI.Module;
12
+ import org.sunflow.util.IntArray;
13
+
14
+ public class BoundingIntervalHierarchy implements AccelerationStructure {
15
+
16
+ private int[] tree;
17
+ private int[] objects;
18
+ private PrimitiveList primitives;
19
+ private BoundingBox bounds;
20
+ private int maxPrims;
21
+
22
+ public BoundingIntervalHierarchy() {
23
+ maxPrims = 2;
24
+ }
25
+
26
+ @Override
27
+ public void build(PrimitiveList primitives) {
28
+ this.primitives = primitives;
29
+ int n = primitives.getNumPrimitives();
30
+ UI.printDetailed(Module.ACCEL, "Getting bounding box ...");
31
+ bounds = primitives.getWorldBounds(null);
32
+ objects = new int[n];
33
+ for (int i = 0; i < n; i++) {
34
+ objects[i] = i;
35
+ }
36
+ UI.printDetailed(Module.ACCEL, "Creating tree ...");
37
+ int initialSize = 3 * (2 * 6 * n + 1);
38
+ IntArray tempTree = new IntArray((initialSize + 3) / 4);
39
+ BuildStats stats = new BuildStats();
40
+ Timer t = new Timer();
41
+ t.start();
42
+ buildHierarchy(tempTree, objects, stats);
43
+ t.end();
44
+ UI.printDetailed(Module.ACCEL, "Trimming tree ...");
45
+ tree = tempTree.trim();
46
+ // display stats
47
+ stats.printStats();
48
+ UI.printDetailed(Module.ACCEL, " * Creation time: %s", t);
49
+ UI.printDetailed(Module.ACCEL, " * Usage of init: %6.2f%%", (double) (100.0 * tree.length) / initialSize);
50
+ UI.printDetailed(Module.ACCEL, " * Tree memory: %s", Memory.sizeof(tree));
51
+ UI.printDetailed(Module.ACCEL, " * Indices memory: %s", Memory.sizeof(objects));
52
+ }
53
+
54
+ private static class BuildStats {
55
+
56
+ private int numNodes;
57
+ private int numLeaves;
58
+ private int sumObjects;
59
+ private int minObjects;
60
+ private int maxObjects;
61
+ private int sumDepth;
62
+ private int minDepth;
63
+ private int maxDepth;
64
+ private int numLeaves0;
65
+ private int numLeaves1;
66
+ private int numLeaves2;
67
+ private int numLeaves3;
68
+ private int numLeaves4;
69
+ private int numLeaves4p;
70
+ private int numBVH2;
71
+
72
+ BuildStats() {
73
+ numNodes = numLeaves = 0;
74
+ sumObjects = 0;
75
+ minObjects = Integer.MAX_VALUE;
76
+ maxObjects = Integer.MIN_VALUE;
77
+ sumDepth = 0;
78
+ minDepth = Integer.MAX_VALUE;
79
+ maxDepth = Integer.MIN_VALUE;
80
+ numLeaves0 = 0;
81
+ numLeaves1 = 0;
82
+ numLeaves2 = 0;
83
+ numLeaves3 = 0;
84
+ numLeaves4 = 0;
85
+ numLeaves4p = 0;
86
+ numBVH2 = 0;
87
+ }
88
+
89
+ void updateInner() {
90
+ numNodes++;
91
+ }
92
+
93
+ void updateBVH2() {
94
+ numBVH2++;
95
+ }
96
+
97
+ void updateLeaf(int depth, int n) {
98
+ numLeaves++;
99
+ minDepth = Math.min(depth, minDepth);
100
+ maxDepth = Math.max(depth, maxDepth);
101
+ sumDepth += depth;
102
+ minObjects = Math.min(n, minObjects);
103
+ maxObjects = Math.max(n, maxObjects);
104
+ sumObjects += n;
105
+ switch (n) {
106
+ case 0:
107
+ numLeaves0++;
108
+ break;
109
+ case 1:
110
+ numLeaves1++;
111
+ break;
112
+ case 2:
113
+ numLeaves2++;
114
+ break;
115
+ case 3:
116
+ numLeaves3++;
117
+ break;
118
+ case 4:
119
+ numLeaves4++;
120
+ break;
121
+ default:
122
+ numLeaves4p++;
123
+ break;
124
+ }
125
+ }
126
+
127
+ void printStats() {
128
+ UI.printDetailed(Module.ACCEL, "Tree stats:");
129
+ UI.printDetailed(Module.ACCEL, " * Nodes: %d", numNodes);
130
+ UI.printDetailed(Module.ACCEL, " * Leaves: %d", numLeaves);
131
+ UI.printDetailed(Module.ACCEL, " * Objects: min %d", minObjects);
132
+ UI.printDetailed(Module.ACCEL, " avg %.2f", (float) sumObjects / numLeaves);
133
+ UI.printDetailed(Module.ACCEL, " avg(n>0) %.2f", (float) sumObjects / (numLeaves - numLeaves0));
134
+ UI.printDetailed(Module.ACCEL, " max %d", maxObjects);
135
+ UI.printDetailed(Module.ACCEL, " * Depth: min %d", minDepth);
136
+ UI.printDetailed(Module.ACCEL, " avg %.2f", (float) sumDepth / numLeaves);
137
+ UI.printDetailed(Module.ACCEL, " max %d", maxDepth);
138
+ UI.printDetailed(Module.ACCEL, " * Leaves w/: N=0 %3d%%", 100 * numLeaves0 / numLeaves);
139
+ UI.printDetailed(Module.ACCEL, " N=1 %3d%%", 100 * numLeaves1 / numLeaves);
140
+ UI.printDetailed(Module.ACCEL, " N=2 %3d%%", 100 * numLeaves2 / numLeaves);
141
+ UI.printDetailed(Module.ACCEL, " N=3 %3d%%", 100 * numLeaves3 / numLeaves);
142
+ UI.printDetailed(Module.ACCEL, " N=4 %3d%%", 100 * numLeaves4 / numLeaves);
143
+ UI.printDetailed(Module.ACCEL, " N>4 %3d%%", 100 * numLeaves4p / numLeaves);
144
+ UI.printDetailed(Module.ACCEL, " * BVH2 nodes: %d (%3d%%)", numBVH2, 100 * numBVH2 / (numNodes + numLeaves - 2 * numBVH2));
145
+ }
146
+ }
147
+
148
+ private void buildHierarchy(IntArray tempTree, int[] indices, BuildStats stats) {
149
+ // create space for the first node
150
+ tempTree.add(3 << 30); // dummy leaf
151
+ tempTree.add(0);
152
+ tempTree.add(0);
153
+ if (objects.length == 0) {
154
+ return;
155
+ }
156
+ // seed bbox
157
+ float[] gridBox = {bounds.getMinimum().x, bounds.getMaximum().x,
158
+ bounds.getMinimum().y, bounds.getMaximum().y,
159
+ bounds.getMinimum().z, bounds.getMaximum().z};
160
+ float[] nodeBox = {bounds.getMinimum().x, bounds.getMaximum().x,
161
+ bounds.getMinimum().y, bounds.getMaximum().y,
162
+ bounds.getMinimum().z, bounds.getMaximum().z};
163
+ // seed subdivide function
164
+ subdivide(0, objects.length - 1, tempTree, indices, gridBox, nodeBox, 0, 1, stats);
165
+ }
166
+
167
+ private void createNode(IntArray tempTree, int nodeIndex, int left, int right) {
168
+ // write leaf node
169
+ tempTree.set(nodeIndex + 0, (3 << 30) | left);
170
+ tempTree.set(nodeIndex + 1, right - left + 1);
171
+ }
172
+
173
+ private void subdivide(int left, int right, IntArray tempTree, int[] indices, float[] gridBox, float[] nodeBox, int nodeIndex, int depth, BuildStats stats) {
174
+ if ((right - left + 1) <= maxPrims || depth >= 64) {
175
+ // write leaf node
176
+ stats.updateLeaf(depth, right - left + 1);
177
+ createNode(tempTree, nodeIndex, left, right);
178
+ return;
179
+ }
180
+ // calculate extents
181
+ int axis = -1, prevAxis, rightOrig;
182
+ float clipL = Float.NaN, clipR = Float.NaN, prevClip = Float.NaN;
183
+ float split = Float.NaN, prevSplit;
184
+ boolean wasLeft = true;
185
+ while (true) {
186
+ prevAxis = axis;
187
+ prevSplit = split;
188
+ // perform quick consistency checks
189
+ float d[] = {gridBox[1] - gridBox[0], gridBox[3] - gridBox[2],
190
+ gridBox[5] - gridBox[4]};
191
+ if (d[0] < 0 || d[1] < 0 || d[2] < 0) {
192
+ throw new IllegalStateException("negative node extents");
193
+ }
194
+ for (int i = 0; i < 3; i++) {
195
+ if (nodeBox[2 * i + 1] < gridBox[2 * i] || nodeBox[2 * i] > gridBox[2 * i + 1]) {
196
+ UI.printError(Module.ACCEL, "Reached tree area in error - discarding node with: %d objects", right - left + 1);
197
+ throw new IllegalStateException("invalid node overlap");
198
+ }
199
+ }
200
+ // find longest axis
201
+ if (d[0] > d[1] && d[0] > d[2]) {
202
+ axis = 0;
203
+ } else if (d[1] > d[2]) {
204
+ axis = 1;
205
+ } else {
206
+ axis = 2;
207
+ }
208
+ split = 0.5f * (gridBox[2 * axis] + gridBox[2 * axis + 1]);
209
+ // partition L/R subsets
210
+ clipL = Float.NEGATIVE_INFINITY;
211
+ clipR = Float.POSITIVE_INFINITY;
212
+ rightOrig = right; // save this for later
213
+ float nodeL = Float.POSITIVE_INFINITY;
214
+ float nodeR = Float.NEGATIVE_INFINITY;
215
+ for (int i = left; i <= right;) {
216
+ int obj = indices[i];
217
+ float minb = primitives.getPrimitiveBound(obj, 2 * axis + 0);
218
+ float maxb = primitives.getPrimitiveBound(obj, 2 * axis + 1);
219
+ float center = (minb + maxb) * 0.5f;
220
+ if (center <= split) {
221
+ // stay left
222
+ i++;
223
+ if (clipL < maxb) {
224
+ clipL = maxb;
225
+ }
226
+ } else {
227
+ // move to the right most
228
+ int t = indices[i];
229
+ indices[i] = indices[right];
230
+ indices[right] = t;
231
+ right--;
232
+ if (clipR > minb) {
233
+ clipR = minb;
234
+ }
235
+ }
236
+ if (nodeL > minb) {
237
+ nodeL = minb;
238
+ }
239
+ if (nodeR < maxb) {
240
+ nodeR = maxb;
241
+ }
242
+ }
243
+ // check for empty space
244
+ if (nodeL > nodeBox[2 * axis + 0] && nodeR < nodeBox[2 * axis + 1]) {
245
+ float nodeBoxW = nodeBox[2 * axis + 1] - nodeBox[2 * axis + 0];
246
+ float nodeNewW = nodeR - nodeL;
247
+ // node box is too big compare to space occupied by primitives?
248
+ if (1.3f * nodeNewW < nodeBoxW) {
249
+ stats.updateBVH2();
250
+ int nextIndex = tempTree.getSize();
251
+ // allocate child
252
+ tempTree.add(0);
253
+ tempTree.add(0);
254
+ tempTree.add(0);
255
+ // write bvh2 clip node
256
+ stats.updateInner();
257
+ tempTree.set(nodeIndex + 0, (axis << 30) | (1 << 29) | nextIndex);
258
+ tempTree.set(nodeIndex + 1, Float.floatToRawIntBits(nodeL));
259
+ tempTree.set(nodeIndex + 2, Float.floatToRawIntBits(nodeR));
260
+ // update nodebox and recurse
261
+ nodeBox[2 * axis + 0] = nodeL;
262
+ nodeBox[2 * axis + 1] = nodeR;
263
+ subdivide(left, rightOrig, tempTree, indices, gridBox, nodeBox, nextIndex, depth + 1, stats);
264
+ return;
265
+ }
266
+ }
267
+ // ensure we are making progress in the subdivision
268
+ if (right == rightOrig) {
269
+ // all left
270
+ if (clipL <= split) {
271
+ // keep looping on left half
272
+ gridBox[2 * axis + 1] = split;
273
+ prevClip = clipL;
274
+ wasLeft = true;
275
+ continue;
276
+ }
277
+ if (prevAxis == axis && prevSplit == split) {
278
+ // we are stuck here - create a leaf
279
+ stats.updateLeaf(depth, right - left + 1);
280
+ createNode(tempTree, nodeIndex, left, right);
281
+ return;
282
+ }
283
+ gridBox[2 * axis + 1] = split;
284
+ prevClip = Float.NaN;
285
+ } else if (left > right) {
286
+ // all right
287
+ right = rightOrig;
288
+ if (clipR >= split) {
289
+ // keep looping on right half
290
+ gridBox[2 * axis + 0] = split;
291
+ prevClip = clipR;
292
+ wasLeft = false;
293
+ continue;
294
+ }
295
+ if (prevAxis == axis && prevSplit == split) {
296
+ // we are stuck here - create a leaf
297
+ stats.updateLeaf(depth, right - left + 1);
298
+ createNode(tempTree, nodeIndex, left, right);
299
+ return;
300
+ }
301
+ gridBox[2 * axis + 0] = split;
302
+ prevClip = Float.NaN;
303
+ } else {
304
+ // we are actually splitting stuff
305
+ if (prevAxis != -1 && !Float.isNaN(prevClip)) {
306
+ // second time through - lets create the previous split
307
+ // since it produced empty space
308
+ int nextIndex = tempTree.getSize();
309
+ // allocate child node
310
+ tempTree.add(0);
311
+ tempTree.add(0);
312
+ tempTree.add(0);
313
+ if (wasLeft) {
314
+ // create a node with a left child
315
+ // write leaf node
316
+ stats.updateInner();
317
+ tempTree.set(nodeIndex + 0, (prevAxis << 30) | nextIndex);
318
+ tempTree.set(nodeIndex + 1, Float.floatToRawIntBits(prevClip));
319
+ tempTree.set(nodeIndex + 2, Float.floatToRawIntBits(Float.POSITIVE_INFINITY));
320
+ } else {
321
+ // create a node with a right child
322
+ // write leaf node
323
+ stats.updateInner();
324
+ tempTree.set(nodeIndex + 0, (prevAxis << 30) | (nextIndex - 3));
325
+ tempTree.set(nodeIndex + 1, Float.floatToRawIntBits(Float.NEGATIVE_INFINITY));
326
+ tempTree.set(nodeIndex + 2, Float.floatToRawIntBits(prevClip));
327
+ }
328
+ // count stats for the unused leaf
329
+ depth++;
330
+ stats.updateLeaf(depth, 0);
331
+ // now we keep going as we are, with a new nodeIndex:
332
+ nodeIndex = nextIndex;
333
+ }
334
+ break;
335
+ }
336
+ }
337
+ // compute index of child nodes
338
+ int nextIndex = tempTree.getSize();
339
+ // allocate left node
340
+ int nl = right - left + 1;
341
+ int nr = rightOrig - (right + 1) + 1;
342
+ if (nl > 0) {
343
+ tempTree.add(0);
344
+ tempTree.add(0);
345
+ tempTree.add(0);
346
+ } else {
347
+ nextIndex -= 3;
348
+ }
349
+ // allocate right node
350
+ if (nr > 0) {
351
+ tempTree.add(0);
352
+ tempTree.add(0);
353
+ tempTree.add(0);
354
+ }
355
+ // write leaf node
356
+ stats.updateInner();
357
+ tempTree.set(nodeIndex + 0, (axis << 30) | nextIndex);
358
+ tempTree.set(nodeIndex + 1, Float.floatToRawIntBits(clipL));
359
+ tempTree.set(nodeIndex + 2, Float.floatToRawIntBits(clipR));
360
+ // prepare L/R child boxes
361
+ float[] gridBoxL = new float[6];
362
+ float[] gridBoxR = new float[6];
363
+ float[] nodeBoxL = new float[6];
364
+ float[] nodeBoxR = new float[6];
365
+ for (int i = 0; i < 6; i++) {
366
+ gridBoxL[i] = gridBoxR[i] = gridBox[i];
367
+ nodeBoxL[i] = nodeBoxR[i] = nodeBox[i];
368
+ }
369
+ gridBoxL[2 * axis + 1] = gridBoxR[2 * axis] = split;
370
+ nodeBoxL[2 * axis + 1] = clipL;
371
+ nodeBoxR[2 * axis + 0] = clipR;
372
+ // free memory
373
+ gridBox = nodeBox = null;
374
+ // recurse
375
+ if (nl > 0) {
376
+ subdivide(left, right, tempTree, indices, gridBoxL, nodeBoxL, nextIndex, depth + 1, stats);
377
+ } else {
378
+ stats.updateLeaf(depth + 1, 0);
379
+ }
380
+ if (nr > 0) {
381
+ subdivide(right + 1, rightOrig, tempTree, indices, gridBoxR, nodeBoxR, nextIndex + 3, depth + 1, stats);
382
+ } else {
383
+ stats.updateLeaf(depth + 1, 0);
384
+ }
385
+ }
386
+
387
+ @Override
388
+ public void intersect(Ray r, IntersectionState state) {
389
+ float intervalMin = r.getMin();
390
+ float intervalMax = r.getMax();
391
+ float orgX = r.ox;
392
+ float dirX = r.dx, invDirX = 1 / dirX;
393
+ float t1, t2;
394
+ t1 = (bounds.getMinimum().x - orgX) * invDirX;
395
+ t2 = (bounds.getMaximum().x - orgX) * invDirX;
396
+ if (invDirX > 0) {
397
+ if (t1 > intervalMin) {
398
+ intervalMin = t1;
399
+ }
400
+ if (t2 < intervalMax) {
401
+ intervalMax = t2;
402
+ }
403
+ } else {
404
+ if (t2 > intervalMin) {
405
+ intervalMin = t2;
406
+ }
407
+ if (t1 < intervalMax) {
408
+ intervalMax = t1;
409
+ }
410
+ }
411
+ if (intervalMin > intervalMax) {
412
+ return;
413
+ }
414
+ float orgY = r.oy;
415
+ float dirY = r.dy, invDirY = 1 / dirY;
416
+ t1 = (bounds.getMinimum().y - orgY) * invDirY;
417
+ t2 = (bounds.getMaximum().y - orgY) * invDirY;
418
+ if (invDirY > 0) {
419
+ if (t1 > intervalMin) {
420
+ intervalMin = t1;
421
+ }
422
+ if (t2 < intervalMax) {
423
+ intervalMax = t2;
424
+ }
425
+ } else {
426
+ if (t2 > intervalMin) {
427
+ intervalMin = t2;
428
+ }
429
+ if (t1 < intervalMax) {
430
+ intervalMax = t1;
431
+ }
432
+ }
433
+ if (intervalMin > intervalMax) {
434
+ return;
435
+ }
436
+ float orgZ = r.oz;
437
+ float dirZ = r.dz, invDirZ = 1 / dirZ;
438
+ t1 = (bounds.getMinimum().z - orgZ) * invDirZ;
439
+ t2 = (bounds.getMaximum().z - orgZ) * invDirZ;
440
+ if (invDirZ > 0) {
441
+ if (t1 > intervalMin) {
442
+ intervalMin = t1;
443
+ }
444
+ if (t2 < intervalMax) {
445
+ intervalMax = t2;
446
+ }
447
+ } else {
448
+ if (t2 > intervalMin) {
449
+ intervalMin = t2;
450
+ }
451
+ if (t1 < intervalMax) {
452
+ intervalMax = t1;
453
+ }
454
+ }
455
+ if (intervalMin > intervalMax) {
456
+ return;
457
+ }
458
+
459
+ // compute custom offsets from direction sign bit
460
+
461
+ int offsetXFront = Float.floatToRawIntBits(dirX) >>> 31;
462
+ int offsetYFront = Float.floatToRawIntBits(dirY) >>> 31;
463
+ int offsetZFront = Float.floatToRawIntBits(dirZ) >>> 31;
464
+
465
+ int offsetXBack = offsetXFront ^ 1;
466
+ int offsetYBack = offsetYFront ^ 1;
467
+ int offsetZBack = offsetZFront ^ 1;
468
+
469
+ int offsetXFront3 = offsetXFront * 3;
470
+ int offsetYFront3 = offsetYFront * 3;
471
+ int offsetZFront3 = offsetZFront * 3;
472
+
473
+ int offsetXBack3 = offsetXBack * 3;
474
+ int offsetYBack3 = offsetYBack * 3;
475
+ int offsetZBack3 = offsetZBack * 3;
476
+
477
+ // avoid always adding 1 during the inner loop
478
+ offsetXFront++;
479
+ offsetYFront++;
480
+ offsetZFront++;
481
+ offsetXBack++;
482
+ offsetYBack++;
483
+ offsetZBack++;
484
+
485
+ IntersectionState.StackNode[] stack = state.getStack();
486
+ int stackPos = 0;
487
+ int node = 0;
488
+
489
+ while (true) {
490
+ pushloop:
491
+ while (true) {
492
+ int tn = tree[node];
493
+ int axis = tn & (7 << 29);
494
+ int offset = tn & ~(7 << 29);
495
+ switch (axis) {
496
+ case 0: {
497
+ // x axis
498
+ float tf = (Float.intBitsToFloat(tree[node + offsetXFront]) - orgX) * invDirX;
499
+ float tb = (Float.intBitsToFloat(tree[node + offsetXBack]) - orgX) * invDirX;
500
+ // ray passes between clip zones
501
+ if (tf < intervalMin && tb > intervalMax) {
502
+ break pushloop;
503
+ }
504
+ int back = offset + offsetXBack3;
505
+ node = back;
506
+ // ray passes through far node only
507
+ if (tf < intervalMin) {
508
+ intervalMin = (tb >= intervalMin) ? tb : intervalMin;
509
+ continue;
510
+ }
511
+ node = offset + offsetXFront3; // front
512
+ // ray passes through near node only
513
+ if (tb > intervalMax) {
514
+ intervalMax = (tf <= intervalMax) ? tf : intervalMax;
515
+ continue;
516
+ }
517
+ // ray passes through both nodes
518
+ // push back node
519
+ stack[stackPos].node = back;
520
+ stack[stackPos].near = (tb >= intervalMin) ? tb : intervalMin;
521
+ stack[stackPos].far = intervalMax;
522
+ stackPos++;
523
+ // update ray interval for front node
524
+ intervalMax = (tf <= intervalMax) ? tf : intervalMax;
525
+ continue;
526
+ }
527
+ case 1 << 30: {
528
+ float tf = (Float.intBitsToFloat(tree[node + offsetYFront]) - orgY) * invDirY;
529
+ float tb = (Float.intBitsToFloat(tree[node + offsetYBack]) - orgY) * invDirY;
530
+ // ray passes between clip zones
531
+ if (tf < intervalMin && tb > intervalMax) {
532
+ break pushloop;
533
+ }
534
+ int back = offset + offsetYBack3;
535
+ node = back;
536
+ // ray passes through far node only
537
+ if (tf < intervalMin) {
538
+ intervalMin = (tb >= intervalMin) ? tb : intervalMin;
539
+ continue;
540
+ }
541
+ node = offset + offsetYFront3; // front
542
+ // ray passes through near node only
543
+ if (tb > intervalMax) {
544
+ intervalMax = (tf <= intervalMax) ? tf : intervalMax;
545
+ continue;
546
+ }
547
+ // ray passes through both nodes
548
+ // push back node
549
+ stack[stackPos].node = back;
550
+ stack[stackPos].near = (tb >= intervalMin) ? tb : intervalMin;
551
+ stack[stackPos].far = intervalMax;
552
+ stackPos++;
553
+ // update ray interval for front node
554
+ intervalMax = (tf <= intervalMax) ? tf : intervalMax;
555
+ continue;
556
+ }
557
+ case 2 << 30: {
558
+ // z axis
559
+ float tf = (Float.intBitsToFloat(tree[node + offsetZFront]) - orgZ) * invDirZ;
560
+ float tb = (Float.intBitsToFloat(tree[node + offsetZBack]) - orgZ) * invDirZ;
561
+ // ray passes between clip zones
562
+ if (tf < intervalMin && tb > intervalMax) {
563
+ break pushloop;
564
+ }
565
+ int back = offset + offsetZBack3;
566
+ node = back;
567
+ // ray passes through far node only
568
+ if (tf < intervalMin) {
569
+ intervalMin = (tb >= intervalMin) ? tb : intervalMin;
570
+ continue;
571
+ }
572
+ node = offset + offsetZFront3; // front
573
+ // ray passes through near node only
574
+ if (tb > intervalMax) {
575
+ intervalMax = (tf <= intervalMax) ? tf : intervalMax;
576
+ continue;
577
+ }
578
+ // ray passes through both nodes
579
+ // push back node
580
+ stack[stackPos].node = back;
581
+ stack[stackPos].near = (tb >= intervalMin) ? tb : intervalMin;
582
+ stack[stackPos].far = intervalMax;
583
+ stackPos++;
584
+ // update ray interval for front node
585
+ intervalMax = (tf <= intervalMax) ? tf : intervalMax;
586
+ continue;
587
+ }
588
+ case 3 << 30: {
589
+ // leaf - test some objects
590
+ int n = tree[node + 1];
591
+ while (n > 0) {
592
+ primitives.intersectPrimitive(r, objects[offset], state);
593
+ n--;
594
+ offset++;
595
+ }
596
+ break pushloop;
597
+ }
598
+ case 1 << 29: {
599
+ float tf = (Float.intBitsToFloat(tree[node + offsetXFront]) - orgX) * invDirX;
600
+ float tb = (Float.intBitsToFloat(tree[node + offsetXBack]) - orgX) * invDirX;
601
+ node = offset;
602
+ intervalMin = (tf >= intervalMin) ? tf : intervalMin;
603
+ intervalMax = (tb <= intervalMax) ? tb : intervalMax;
604
+ if (intervalMin > intervalMax) {
605
+ break pushloop;
606
+ }
607
+ continue;
608
+ }
609
+ case 3 << 29: {
610
+ float tf = (Float.intBitsToFloat(tree[node + offsetYFront]) - orgY) * invDirY;
611
+ float tb = (Float.intBitsToFloat(tree[node + offsetYBack]) - orgY) * invDirY;
612
+ node = offset;
613
+ intervalMin = (tf >= intervalMin) ? tf : intervalMin;
614
+ intervalMax = (tb <= intervalMax) ? tb : intervalMax;
615
+ if (intervalMin > intervalMax) {
616
+ break pushloop;
617
+ }
618
+ continue;
619
+ }
620
+ case 5 << 29: {
621
+ float tf = (Float.intBitsToFloat(tree[node + offsetZFront]) - orgZ) * invDirZ;
622
+ float tb = (Float.intBitsToFloat(tree[node + offsetZBack]) - orgZ) * invDirZ;
623
+ node = offset;
624
+ intervalMin = (tf >= intervalMin) ? tf : intervalMin;
625
+ intervalMax = (tb <= intervalMax) ? tb : intervalMax;
626
+ if (intervalMin > intervalMax) {
627
+ break pushloop;
628
+ }
629
+ continue;
630
+ }
631
+ default:
632
+ return; // should not happen
633
+ } // switch
634
+ } // traversal loop
635
+ do {
636
+ // stack is empty?
637
+ if (stackPos == 0) {
638
+ return;
639
+ }
640
+ // move back up the stack
641
+ stackPos--;
642
+ intervalMin = stack[stackPos].near;
643
+ if (r.getMax() < intervalMin) {
644
+ continue;
645
+ }
646
+ node = stack[stackPos].node;
647
+ intervalMax = stack[stackPos].far;
648
+ break;
649
+ } while (true);
650
+ }
651
+ }
652
+ }