joonsrenderer 1.1-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +53 -0
  3. data/.mvn/extensions.xml +8 -0
  4. data/CHANGELOG.md +2 -0
  5. data/Gemfile +6 -0
  6. data/LICENSE +674 -0
  7. data/README.md +2 -0
  8. data/Rakefile +43 -0
  9. data/docs/.gitignore +6 -0
  10. data/docs/_config.yml +20 -0
  11. data/docs/_includes/footer.html +38 -0
  12. data/docs/_includes/head.html +15 -0
  13. data/docs/_includes/header.html +27 -0
  14. data/docs/_includes/icon-github.html +1 -0
  15. data/docs/_includes/icon-github.svg +1 -0
  16. data/docs/_includes/icon-twitter.html +1 -0
  17. data/docs/_includes/icon-twitter.svg +1 -0
  18. data/docs/_layouts/default.html +20 -0
  19. data/docs/_layouts/page.html +14 -0
  20. data/docs/_layouts/post.html +15 -0
  21. data/docs/_posts/2017-01-08-animated_ray_tracing.md +72 -0
  22. data/docs/_posts/2017-01-08-welcome.md +78 -0
  23. data/docs/_sass/_base.scss +206 -0
  24. data/docs/_sass/_layout.scss +242 -0
  25. data/docs/_sass/_syntax-highlighting.scss +71 -0
  26. data/docs/about.md +12 -0
  27. data/docs/assets/Animation.ogv +0 -0
  28. data/docs/assets/Animation.png +0 -0
  29. data/docs/assets/basic.png +0 -0
  30. data/docs/assets/basic_traced.png +0 -0
  31. data/docs/css/main.scss +38 -0
  32. data/docs/favicon.ico +0 -0
  33. data/docs/feed.xml +30 -0
  34. data/docs/index.html +38 -0
  35. data/joonsrenderer.gemspec +23 -0
  36. data/lib/joonsrenderer.rb +12 -0
  37. data/lib/joonsrenderer/version.rb +3 -0
  38. data/pom.rb +75 -0
  39. data/pom.xml +163 -0
  40. data/src/main/java/SunflowGUI.java +1354 -0
  41. data/src/main/java/joons/JRFiller.java +79 -0
  42. data/src/main/java/joons/JRImagePanel.java +141 -0
  43. data/src/main/java/joons/JRRecorder.java +183 -0
  44. data/src/main/java/joons/JRStatics.java +199 -0
  45. data/src/main/java/joons/JoonsRenderer.java +837 -0
  46. data/src/main/java/org/sunflow/AsciiFileSunflowAPI.java +98 -0
  47. data/src/main/java/org/sunflow/Benchmark.java +313 -0
  48. data/src/main/java/org/sunflow/BinaryFileSunflowAPI.java +228 -0
  49. data/src/main/java/org/sunflow/FileSunflowAPI.java +354 -0
  50. data/src/main/java/org/sunflow/PluginRegistry.java +322 -0
  51. data/src/main/java/org/sunflow/RealtimeBenchmark.java +125 -0
  52. data/src/main/java/org/sunflow/RenderObjectMap.java +344 -0
  53. data/src/main/java/org/sunflow/SunflowAPI.java +762 -0
  54. data/src/main/java/org/sunflow/SunflowAPIInterface.java +277 -0
  55. data/src/main/java/org/sunflow/core/AccelerationStructure.java +20 -0
  56. data/src/main/java/org/sunflow/core/AccelerationStructureFactory.java +36 -0
  57. data/src/main/java/org/sunflow/core/BucketOrder.java +21 -0
  58. data/src/main/java/org/sunflow/core/Camera.java +125 -0
  59. data/src/main/java/org/sunflow/core/CameraLens.java +29 -0
  60. data/src/main/java/org/sunflow/core/CausticPhotonMapInterface.java +15 -0
  61. data/src/main/java/org/sunflow/core/Display.java +78 -0
  62. data/src/main/java/org/sunflow/core/Filter.java +27 -0
  63. data/src/main/java/org/sunflow/core/GIEngine.java +42 -0
  64. data/src/main/java/org/sunflow/core/Geometry.java +157 -0
  65. data/src/main/java/org/sunflow/core/GlobalPhotonMapInterface.java +21 -0
  66. data/src/main/java/org/sunflow/core/ImageSampler.java +26 -0
  67. data/src/main/java/org/sunflow/core/Instance.java +224 -0
  68. data/src/main/java/org/sunflow/core/InstanceList.java +83 -0
  69. data/src/main/java/org/sunflow/core/IntersectionState.java +120 -0
  70. data/src/main/java/org/sunflow/core/LightSample.java +104 -0
  71. data/src/main/java/org/sunflow/core/LightServer.java +382 -0
  72. data/src/main/java/org/sunflow/core/LightSource.java +67 -0
  73. data/src/main/java/org/sunflow/core/Modifier.java +16 -0
  74. data/src/main/java/org/sunflow/core/Options.java +20 -0
  75. data/src/main/java/org/sunflow/core/ParameterList.java +758 -0
  76. data/src/main/java/org/sunflow/core/PhotonStore.java +62 -0
  77. data/src/main/java/org/sunflow/core/PrimitiveList.java +70 -0
  78. data/src/main/java/org/sunflow/core/Ray.java +219 -0
  79. data/src/main/java/org/sunflow/core/RenderObject.java +25 -0
  80. data/src/main/java/org/sunflow/core/Scene.java +377 -0
  81. data/src/main/java/org/sunflow/core/SceneParser.java +58 -0
  82. data/src/main/java/org/sunflow/core/Shader.java +30 -0
  83. data/src/main/java/org/sunflow/core/ShadingCache.java +84 -0
  84. data/src/main/java/org/sunflow/core/ShadingState.java +939 -0
  85. data/src/main/java/org/sunflow/core/Statistics.java +85 -0
  86. data/src/main/java/org/sunflow/core/Tesselatable.java +36 -0
  87. data/src/main/java/org/sunflow/core/Texture.java +128 -0
  88. data/src/main/java/org/sunflow/core/TextureCache.java +48 -0
  89. data/src/main/java/org/sunflow/core/accel/BoundingIntervalHierarchy.java +652 -0
  90. data/src/main/java/org/sunflow/core/accel/KDTree.java +833 -0
  91. data/src/main/java/org/sunflow/core/accel/NullAccelerator.java +30 -0
  92. data/src/main/java/org/sunflow/core/accel/UniformGrid.java +329 -0
  93. data/src/main/java/org/sunflow/core/bucket/BucketOrderFactory.java +26 -0
  94. data/src/main/java/org/sunflow/core/bucket/ColumnBucketOrder.java +21 -0
  95. data/src/main/java/org/sunflow/core/bucket/DiagonalBucketOrder.java +28 -0
  96. data/src/main/java/org/sunflow/core/bucket/HilbertBucketOrder.java +65 -0
  97. data/src/main/java/org/sunflow/core/bucket/InvertedBucketOrder.java +28 -0
  98. data/src/main/java/org/sunflow/core/bucket/RandomBucketOrder.java +49 -0
  99. data/src/main/java/org/sunflow/core/bucket/RowBucketOrder.java +21 -0
  100. data/src/main/java/org/sunflow/core/bucket/SpiralBucketOrder.java +43 -0
  101. data/src/main/java/org/sunflow/core/camera/FisheyeLens.java +25 -0
  102. data/src/main/java/org/sunflow/core/camera/PinholeLens.java +43 -0
  103. data/src/main/java/org/sunflow/core/camera/SphericalLens.java +22 -0
  104. data/src/main/java/org/sunflow/core/camera/ThinLens.java +107 -0
  105. data/src/main/java/org/sunflow/core/display/FastDisplay.java +119 -0
  106. data/src/main/java/org/sunflow/core/display/FileDisplay.java +83 -0
  107. data/src/main/java/org/sunflow/core/display/FrameDisplay.java +97 -0
  108. data/src/main/java/org/sunflow/core/display/ImgPipeDisplay.java +109 -0
  109. data/src/main/java/org/sunflow/core/filter/BlackmanHarrisFilter.java +28 -0
  110. data/src/main/java/org/sunflow/core/filter/BoxFilter.java +16 -0
  111. data/src/main/java/org/sunflow/core/filter/CatmullRomFilter.java +29 -0
  112. data/src/main/java/org/sunflow/core/filter/CubicBSpline.java +32 -0
  113. data/src/main/java/org/sunflow/core/filter/GaussianFilter.java +24 -0
  114. data/src/main/java/org/sunflow/core/filter/LanczosFilter.java +30 -0
  115. data/src/main/java/org/sunflow/core/filter/MitchellFilter.java +28 -0
  116. data/src/main/java/org/sunflow/core/filter/SincFilter.java +25 -0
  117. data/src/main/java/org/sunflow/core/filter/TriangleFilter.java +16 -0
  118. data/src/main/java/org/sunflow/core/gi/AmbientOcclusionGIEngine.java +57 -0
  119. data/src/main/java/org/sunflow/core/gi/FakeGIEngine.java +48 -0
  120. data/src/main/java/org/sunflow/core/gi/InstantGI.java +194 -0
  121. data/src/main/java/org/sunflow/core/gi/IrradianceCacheGIEngine.java +268 -0
  122. data/src/main/java/org/sunflow/core/gi/PathTracingGIEngine.java +65 -0
  123. data/src/main/java/org/sunflow/core/light/DirectionalSpotlight.java +103 -0
  124. data/src/main/java/org/sunflow/core/light/ImageBasedLight.java +303 -0
  125. data/src/main/java/org/sunflow/core/light/PointLight.java +72 -0
  126. data/src/main/java/org/sunflow/core/light/SphereLight.java +166 -0
  127. data/src/main/java/org/sunflow/core/light/SunSkyLight.java +362 -0
  128. data/src/main/java/org/sunflow/core/light/TriangleMeshLight.java +296 -0
  129. data/src/main/java/org/sunflow/core/modifiers/BumpMappingModifier.java +37 -0
  130. data/src/main/java/org/sunflow/core/modifiers/NormalMapModifier.java +34 -0
  131. data/src/main/java/org/sunflow/core/modifiers/PerlinModifier.java +80 -0
  132. data/src/main/java/org/sunflow/core/parser/Keyword.java +39 -0
  133. data/src/main/java/org/sunflow/core/parser/RA2Parser.java +107 -0
  134. data/src/main/java/org/sunflow/core/parser/RA3Parser.java +68 -0
  135. data/src/main/java/org/sunflow/core/parser/SCAbstractParser.java +299 -0
  136. data/src/main/java/org/sunflow/core/parser/SCAsciiParser.java +251 -0
  137. data/src/main/java/org/sunflow/core/parser/SCBinaryParser.java +156 -0
  138. data/src/main/java/org/sunflow/core/parser/SCParser.java +1403 -0
  139. data/src/main/java/org/sunflow/core/parser/ShaveRibParser.java +174 -0
  140. data/src/main/java/org/sunflow/core/parser/TriParser.java +79 -0
  141. data/src/main/java/org/sunflow/core/photonmap/CausticPhotonMap.java +429 -0
  142. data/src/main/java/org/sunflow/core/photonmap/GlobalPhotonMap.java +530 -0
  143. data/src/main/java/org/sunflow/core/photonmap/GridPhotonMap.java +308 -0
  144. data/src/main/java/org/sunflow/core/primitive/Background.java +55 -0
  145. data/src/main/java/org/sunflow/core/primitive/BanchoffSurface.java +100 -0
  146. data/src/main/java/org/sunflow/core/primitive/Box.java +210 -0
  147. data/src/main/java/org/sunflow/core/primitive/CornellBox.java +476 -0
  148. data/src/main/java/org/sunflow/core/primitive/CubeGrid.java +318 -0
  149. data/src/main/java/org/sunflow/core/primitive/Cylinder.java +104 -0
  150. data/src/main/java/org/sunflow/core/primitive/Hair.java +275 -0
  151. data/src/main/java/org/sunflow/core/primitive/JuliaFractal.java +266 -0
  152. data/src/main/java/org/sunflow/core/primitive/ParticleSurface.java +114 -0
  153. data/src/main/java/org/sunflow/core/primitive/Plane.java +163 -0
  154. data/src/main/java/org/sunflow/core/primitive/QuadMesh.java +413 -0
  155. data/src/main/java/org/sunflow/core/primitive/Sphere.java +101 -0
  156. data/src/main/java/org/sunflow/core/primitive/SphereFlake.java +234 -0
  157. data/src/main/java/org/sunflow/core/primitive/Torus.java +145 -0
  158. data/src/main/java/org/sunflow/core/primitive/TriangleMesh.java +849 -0
  159. data/src/main/java/org/sunflow/core/renderer/BucketRenderer.java +491 -0
  160. data/src/main/java/org/sunflow/core/renderer/MultipassRenderer.java +237 -0
  161. data/src/main/java/org/sunflow/core/renderer/ProgressiveRenderer.java +171 -0
  162. data/src/main/java/org/sunflow/core/renderer/SimpleRenderer.java +106 -0
  163. data/src/main/java/org/sunflow/core/shader/AmbientOcclusionShader.java +53 -0
  164. data/src/main/java/org/sunflow/core/shader/AnisotropicWardShader.java +216 -0
  165. data/src/main/java/org/sunflow/core/shader/ConstantShader.java +31 -0
  166. data/src/main/java/org/sunflow/core/shader/DiffuseShader.java +65 -0
  167. data/src/main/java/org/sunflow/core/shader/GlassShader.java +147 -0
  168. data/src/main/java/org/sunflow/core/shader/IDShader.java +27 -0
  169. data/src/main/java/org/sunflow/core/shader/MirrorShader.java +68 -0
  170. data/src/main/java/org/sunflow/core/shader/NormalShader.java +32 -0
  171. data/src/main/java/org/sunflow/core/shader/PhongShader.java +89 -0
  172. data/src/main/java/org/sunflow/core/shader/PrimIDShader.java +30 -0
  173. data/src/main/java/org/sunflow/core/shader/QuickGrayShader.java +63 -0
  174. data/src/main/java/org/sunflow/core/shader/ShinyDiffuseShader.java +98 -0
  175. data/src/main/java/org/sunflow/core/shader/SimpleShader.java +24 -0
  176. data/src/main/java/org/sunflow/core/shader/TexturedAmbientOcclusionShader.java +31 -0
  177. data/src/main/java/org/sunflow/core/shader/TexturedDiffuseShader.java +31 -0
  178. data/src/main/java/org/sunflow/core/shader/TexturedPhongShader.java +31 -0
  179. data/src/main/java/org/sunflow/core/shader/TexturedShinyDiffuseShader.java +31 -0
  180. data/src/main/java/org/sunflow/core/shader/TexturedWardShader.java +31 -0
  181. data/src/main/java/org/sunflow/core/shader/UVShader.java +27 -0
  182. data/src/main/java/org/sunflow/core/shader/UberShader.java +149 -0
  183. data/src/main/java/org/sunflow/core/shader/ViewCausticsShader.java +33 -0
  184. data/src/main/java/org/sunflow/core/shader/ViewGlobalPhotonsShader.java +25 -0
  185. data/src/main/java/org/sunflow/core/shader/ViewIrradianceShader.java +25 -0
  186. data/src/main/java/org/sunflow/core/shader/WireframeShader.java +83 -0
  187. data/src/main/java/org/sunflow/core/tesselatable/BezierMesh.java +254 -0
  188. data/src/main/java/org/sunflow/core/tesselatable/FileMesh.java +251 -0
  189. data/src/main/java/org/sunflow/core/tesselatable/Gumbo.java +1147 -0
  190. data/src/main/java/org/sunflow/core/tesselatable/Teapot.java +237 -0
  191. data/src/main/java/org/sunflow/image/Bitmap.java +15 -0
  192. data/src/main/java/org/sunflow/image/BitmapReader.java +39 -0
  193. data/src/main/java/org/sunflow/image/BitmapWriter.java +79 -0
  194. data/src/main/java/org/sunflow/image/BlackbodySpectrum.java +16 -0
  195. data/src/main/java/org/sunflow/image/ChromaticitySpectrum.java +55 -0
  196. data/src/main/java/org/sunflow/image/Color.java +374 -0
  197. data/src/main/java/org/sunflow/image/ColorEncoder.java +94 -0
  198. data/src/main/java/org/sunflow/image/ColorFactory.java +122 -0
  199. data/src/main/java/org/sunflow/image/ConstantSpectralCurve.java +21 -0
  200. data/src/main/java/org/sunflow/image/IrregularSpectralCurve.java +57 -0
  201. data/src/main/java/org/sunflow/image/RGBSpace.java +207 -0
  202. data/src/main/java/org/sunflow/image/RegularSpectralCurve.java +30 -0
  203. data/src/main/java/org/sunflow/image/SpectralCurve.java +118 -0
  204. data/src/main/java/org/sunflow/image/XYZColor.java +50 -0
  205. data/src/main/java/org/sunflow/image/formats/BitmapBlack.java +27 -0
  206. data/src/main/java/org/sunflow/image/formats/BitmapG8.java +36 -0
  207. data/src/main/java/org/sunflow/image/formats/BitmapGA8.java +30 -0
  208. data/src/main/java/org/sunflow/image/formats/BitmapRGB8.java +40 -0
  209. data/src/main/java/org/sunflow/image/formats/BitmapRGBA8.java +40 -0
  210. data/src/main/java/org/sunflow/image/formats/BitmapRGBE.java +60 -0
  211. data/src/main/java/org/sunflow/image/formats/BitmapXYZ.java +38 -0
  212. data/src/main/java/org/sunflow/image/formats/GenericBitmap.java +73 -0
  213. data/src/main/java/org/sunflow/image/readers/BMPBitmapReader.java +39 -0
  214. data/src/main/java/org/sunflow/image/readers/HDRBitmapReader.java +155 -0
  215. data/src/main/java/org/sunflow/image/readers/IGIBitmapReader.java +104 -0
  216. data/src/main/java/org/sunflow/image/readers/JPGBitmapReader.java +39 -0
  217. data/src/main/java/org/sunflow/image/readers/PNGBitmapReader.java +40 -0
  218. data/src/main/java/org/sunflow/image/readers/TGABitmapReader.java +141 -0
  219. data/src/main/java/org/sunflow/image/writers/EXRBitmapWriter.java +395 -0
  220. data/src/main/java/org/sunflow/image/writers/HDRBitmapWriter.java +54 -0
  221. data/src/main/java/org/sunflow/image/writers/IGIBitmapWriter.java +75 -0
  222. data/src/main/java/org/sunflow/image/writers/PNGBitmapWriter.java +39 -0
  223. data/src/main/java/org/sunflow/image/writers/TGABitmapWriter.java +63 -0
  224. data/src/main/java/org/sunflow/math/BoundingBox.java +340 -0
  225. data/src/main/java/org/sunflow/math/MathUtils.java +159 -0
  226. data/src/main/java/org/sunflow/math/Matrix4.java +573 -0
  227. data/src/main/java/org/sunflow/math/MovingMatrix4.java +119 -0
  228. data/src/main/java/org/sunflow/math/OrthoNormalBasis.java +110 -0
  229. data/src/main/java/org/sunflow/math/PerlinScalar.java +331 -0
  230. data/src/main/java/org/sunflow/math/PerlinVector.java +132 -0
  231. data/src/main/java/org/sunflow/math/Point2.java +36 -0
  232. data/src/main/java/org/sunflow/math/Point3.java +133 -0
  233. data/src/main/java/org/sunflow/math/QMC.java +209 -0
  234. data/src/main/java/org/sunflow/math/Solvers.java +142 -0
  235. data/src/main/java/org/sunflow/math/Vector3.java +197 -0
  236. data/src/main/java/org/sunflow/system/BenchmarkFramework.java +73 -0
  237. data/src/main/java/org/sunflow/system/BenchmarkTest.java +17 -0
  238. data/src/main/java/org/sunflow/system/ByteUtil.java +119 -0
  239. data/src/main/java/org/sunflow/system/FileUtils.java +27 -0
  240. data/src/main/java/org/sunflow/system/ImagePanel.java +282 -0
  241. data/src/main/java/org/sunflow/system/Memory.java +18 -0
  242. data/src/main/java/org/sunflow/system/Parser.java +162 -0
  243. data/src/main/java/org/sunflow/system/Plugins.java +142 -0
  244. data/src/main/java/org/sunflow/system/RenderGlobalsPanel.java +209 -0
  245. data/src/main/java/org/sunflow/system/SearchPath.java +67 -0
  246. data/src/main/java/org/sunflow/system/Timer.java +53 -0
  247. data/src/main/java/org/sunflow/system/UI.java +112 -0
  248. data/src/main/java/org/sunflow/system/UserInterface.java +46 -0
  249. data/src/main/java/org/sunflow/system/ui/ConsoleInterface.java +48 -0
  250. data/src/main/java/org/sunflow/system/ui/SilentInterface.java +28 -0
  251. data/src/main/java/org/sunflow/util/FastHashMap.java +220 -0
  252. data/src/main/java/org/sunflow/util/FloatArray.java +77 -0
  253. data/src/main/java/org/sunflow/util/IntArray.java +77 -0
  254. data/src/test/java/a_maintest.java +129 -0
  255. metadata +300 -0
@@ -0,0 +1,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
+ }