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.
- checksums.yaml +7 -0
- data/.gitignore +53 -0
- data/.mvn/extensions.xml +8 -0
- data/CHANGELOG.md +2 -0
- data/Gemfile +6 -0
- data/LICENSE +674 -0
- data/README.md +2 -0
- data/Rakefile +43 -0
- data/docs/.gitignore +6 -0
- data/docs/_config.yml +20 -0
- data/docs/_includes/footer.html +38 -0
- data/docs/_includes/head.html +15 -0
- data/docs/_includes/header.html +27 -0
- data/docs/_includes/icon-github.html +1 -0
- data/docs/_includes/icon-github.svg +1 -0
- data/docs/_includes/icon-twitter.html +1 -0
- data/docs/_includes/icon-twitter.svg +1 -0
- data/docs/_layouts/default.html +20 -0
- data/docs/_layouts/page.html +14 -0
- data/docs/_layouts/post.html +15 -0
- data/docs/_posts/2017-01-08-animated_ray_tracing.md +72 -0
- data/docs/_posts/2017-01-08-welcome.md +78 -0
- data/docs/_sass/_base.scss +206 -0
- data/docs/_sass/_layout.scss +242 -0
- data/docs/_sass/_syntax-highlighting.scss +71 -0
- data/docs/about.md +12 -0
- data/docs/assets/Animation.ogv +0 -0
- data/docs/assets/Animation.png +0 -0
- data/docs/assets/basic.png +0 -0
- data/docs/assets/basic_traced.png +0 -0
- data/docs/css/main.scss +38 -0
- data/docs/favicon.ico +0 -0
- data/docs/feed.xml +30 -0
- data/docs/index.html +38 -0
- data/joonsrenderer.gemspec +23 -0
- data/lib/joonsrenderer.rb +12 -0
- data/lib/joonsrenderer/version.rb +3 -0
- data/pom.rb +75 -0
- data/pom.xml +163 -0
- data/src/main/java/SunflowGUI.java +1354 -0
- data/src/main/java/joons/JRFiller.java +79 -0
- data/src/main/java/joons/JRImagePanel.java +141 -0
- data/src/main/java/joons/JRRecorder.java +183 -0
- data/src/main/java/joons/JRStatics.java +199 -0
- data/src/main/java/joons/JoonsRenderer.java +837 -0
- data/src/main/java/org/sunflow/AsciiFileSunflowAPI.java +98 -0
- data/src/main/java/org/sunflow/Benchmark.java +313 -0
- data/src/main/java/org/sunflow/BinaryFileSunflowAPI.java +228 -0
- data/src/main/java/org/sunflow/FileSunflowAPI.java +354 -0
- data/src/main/java/org/sunflow/PluginRegistry.java +322 -0
- data/src/main/java/org/sunflow/RealtimeBenchmark.java +125 -0
- data/src/main/java/org/sunflow/RenderObjectMap.java +344 -0
- data/src/main/java/org/sunflow/SunflowAPI.java +762 -0
- data/src/main/java/org/sunflow/SunflowAPIInterface.java +277 -0
- data/src/main/java/org/sunflow/core/AccelerationStructure.java +20 -0
- data/src/main/java/org/sunflow/core/AccelerationStructureFactory.java +36 -0
- data/src/main/java/org/sunflow/core/BucketOrder.java +21 -0
- data/src/main/java/org/sunflow/core/Camera.java +125 -0
- data/src/main/java/org/sunflow/core/CameraLens.java +29 -0
- data/src/main/java/org/sunflow/core/CausticPhotonMapInterface.java +15 -0
- data/src/main/java/org/sunflow/core/Display.java +78 -0
- data/src/main/java/org/sunflow/core/Filter.java +27 -0
- data/src/main/java/org/sunflow/core/GIEngine.java +42 -0
- data/src/main/java/org/sunflow/core/Geometry.java +157 -0
- data/src/main/java/org/sunflow/core/GlobalPhotonMapInterface.java +21 -0
- data/src/main/java/org/sunflow/core/ImageSampler.java +26 -0
- data/src/main/java/org/sunflow/core/Instance.java +224 -0
- data/src/main/java/org/sunflow/core/InstanceList.java +83 -0
- data/src/main/java/org/sunflow/core/IntersectionState.java +120 -0
- data/src/main/java/org/sunflow/core/LightSample.java +104 -0
- data/src/main/java/org/sunflow/core/LightServer.java +382 -0
- data/src/main/java/org/sunflow/core/LightSource.java +67 -0
- data/src/main/java/org/sunflow/core/Modifier.java +16 -0
- data/src/main/java/org/sunflow/core/Options.java +20 -0
- data/src/main/java/org/sunflow/core/ParameterList.java +758 -0
- data/src/main/java/org/sunflow/core/PhotonStore.java +62 -0
- data/src/main/java/org/sunflow/core/PrimitiveList.java +70 -0
- data/src/main/java/org/sunflow/core/Ray.java +219 -0
- data/src/main/java/org/sunflow/core/RenderObject.java +25 -0
- data/src/main/java/org/sunflow/core/Scene.java +377 -0
- data/src/main/java/org/sunflow/core/SceneParser.java +58 -0
- data/src/main/java/org/sunflow/core/Shader.java +30 -0
- data/src/main/java/org/sunflow/core/ShadingCache.java +84 -0
- data/src/main/java/org/sunflow/core/ShadingState.java +939 -0
- data/src/main/java/org/sunflow/core/Statistics.java +85 -0
- data/src/main/java/org/sunflow/core/Tesselatable.java +36 -0
- data/src/main/java/org/sunflow/core/Texture.java +128 -0
- data/src/main/java/org/sunflow/core/TextureCache.java +48 -0
- data/src/main/java/org/sunflow/core/accel/BoundingIntervalHierarchy.java +652 -0
- data/src/main/java/org/sunflow/core/accel/KDTree.java +833 -0
- data/src/main/java/org/sunflow/core/accel/NullAccelerator.java +30 -0
- data/src/main/java/org/sunflow/core/accel/UniformGrid.java +329 -0
- data/src/main/java/org/sunflow/core/bucket/BucketOrderFactory.java +26 -0
- data/src/main/java/org/sunflow/core/bucket/ColumnBucketOrder.java +21 -0
- data/src/main/java/org/sunflow/core/bucket/DiagonalBucketOrder.java +28 -0
- data/src/main/java/org/sunflow/core/bucket/HilbertBucketOrder.java +65 -0
- data/src/main/java/org/sunflow/core/bucket/InvertedBucketOrder.java +28 -0
- data/src/main/java/org/sunflow/core/bucket/RandomBucketOrder.java +49 -0
- data/src/main/java/org/sunflow/core/bucket/RowBucketOrder.java +21 -0
- data/src/main/java/org/sunflow/core/bucket/SpiralBucketOrder.java +43 -0
- data/src/main/java/org/sunflow/core/camera/FisheyeLens.java +25 -0
- data/src/main/java/org/sunflow/core/camera/PinholeLens.java +43 -0
- data/src/main/java/org/sunflow/core/camera/SphericalLens.java +22 -0
- data/src/main/java/org/sunflow/core/camera/ThinLens.java +107 -0
- data/src/main/java/org/sunflow/core/display/FastDisplay.java +119 -0
- data/src/main/java/org/sunflow/core/display/FileDisplay.java +83 -0
- data/src/main/java/org/sunflow/core/display/FrameDisplay.java +97 -0
- data/src/main/java/org/sunflow/core/display/ImgPipeDisplay.java +109 -0
- data/src/main/java/org/sunflow/core/filter/BlackmanHarrisFilter.java +28 -0
- data/src/main/java/org/sunflow/core/filter/BoxFilter.java +16 -0
- data/src/main/java/org/sunflow/core/filter/CatmullRomFilter.java +29 -0
- data/src/main/java/org/sunflow/core/filter/CubicBSpline.java +32 -0
- data/src/main/java/org/sunflow/core/filter/GaussianFilter.java +24 -0
- data/src/main/java/org/sunflow/core/filter/LanczosFilter.java +30 -0
- data/src/main/java/org/sunflow/core/filter/MitchellFilter.java +28 -0
- data/src/main/java/org/sunflow/core/filter/SincFilter.java +25 -0
- data/src/main/java/org/sunflow/core/filter/TriangleFilter.java +16 -0
- data/src/main/java/org/sunflow/core/gi/AmbientOcclusionGIEngine.java +57 -0
- data/src/main/java/org/sunflow/core/gi/FakeGIEngine.java +48 -0
- data/src/main/java/org/sunflow/core/gi/InstantGI.java +194 -0
- data/src/main/java/org/sunflow/core/gi/IrradianceCacheGIEngine.java +268 -0
- data/src/main/java/org/sunflow/core/gi/PathTracingGIEngine.java +65 -0
- data/src/main/java/org/sunflow/core/light/DirectionalSpotlight.java +103 -0
- data/src/main/java/org/sunflow/core/light/ImageBasedLight.java +303 -0
- data/src/main/java/org/sunflow/core/light/PointLight.java +72 -0
- data/src/main/java/org/sunflow/core/light/SphereLight.java +166 -0
- data/src/main/java/org/sunflow/core/light/SunSkyLight.java +362 -0
- data/src/main/java/org/sunflow/core/light/TriangleMeshLight.java +296 -0
- data/src/main/java/org/sunflow/core/modifiers/BumpMappingModifier.java +37 -0
- data/src/main/java/org/sunflow/core/modifiers/NormalMapModifier.java +34 -0
- data/src/main/java/org/sunflow/core/modifiers/PerlinModifier.java +80 -0
- data/src/main/java/org/sunflow/core/parser/Keyword.java +39 -0
- data/src/main/java/org/sunflow/core/parser/RA2Parser.java +107 -0
- data/src/main/java/org/sunflow/core/parser/RA3Parser.java +68 -0
- data/src/main/java/org/sunflow/core/parser/SCAbstractParser.java +299 -0
- data/src/main/java/org/sunflow/core/parser/SCAsciiParser.java +251 -0
- data/src/main/java/org/sunflow/core/parser/SCBinaryParser.java +156 -0
- data/src/main/java/org/sunflow/core/parser/SCParser.java +1403 -0
- data/src/main/java/org/sunflow/core/parser/ShaveRibParser.java +174 -0
- data/src/main/java/org/sunflow/core/parser/TriParser.java +79 -0
- data/src/main/java/org/sunflow/core/photonmap/CausticPhotonMap.java +429 -0
- data/src/main/java/org/sunflow/core/photonmap/GlobalPhotonMap.java +530 -0
- data/src/main/java/org/sunflow/core/photonmap/GridPhotonMap.java +308 -0
- data/src/main/java/org/sunflow/core/primitive/Background.java +55 -0
- data/src/main/java/org/sunflow/core/primitive/BanchoffSurface.java +100 -0
- data/src/main/java/org/sunflow/core/primitive/Box.java +210 -0
- data/src/main/java/org/sunflow/core/primitive/CornellBox.java +476 -0
- data/src/main/java/org/sunflow/core/primitive/CubeGrid.java +318 -0
- data/src/main/java/org/sunflow/core/primitive/Cylinder.java +104 -0
- data/src/main/java/org/sunflow/core/primitive/Hair.java +275 -0
- data/src/main/java/org/sunflow/core/primitive/JuliaFractal.java +266 -0
- data/src/main/java/org/sunflow/core/primitive/ParticleSurface.java +114 -0
- data/src/main/java/org/sunflow/core/primitive/Plane.java +163 -0
- data/src/main/java/org/sunflow/core/primitive/QuadMesh.java +413 -0
- data/src/main/java/org/sunflow/core/primitive/Sphere.java +101 -0
- data/src/main/java/org/sunflow/core/primitive/SphereFlake.java +234 -0
- data/src/main/java/org/sunflow/core/primitive/Torus.java +145 -0
- data/src/main/java/org/sunflow/core/primitive/TriangleMesh.java +849 -0
- data/src/main/java/org/sunflow/core/renderer/BucketRenderer.java +491 -0
- data/src/main/java/org/sunflow/core/renderer/MultipassRenderer.java +237 -0
- data/src/main/java/org/sunflow/core/renderer/ProgressiveRenderer.java +171 -0
- data/src/main/java/org/sunflow/core/renderer/SimpleRenderer.java +106 -0
- data/src/main/java/org/sunflow/core/shader/AmbientOcclusionShader.java +53 -0
- data/src/main/java/org/sunflow/core/shader/AnisotropicWardShader.java +216 -0
- data/src/main/java/org/sunflow/core/shader/ConstantShader.java +31 -0
- data/src/main/java/org/sunflow/core/shader/DiffuseShader.java +65 -0
- data/src/main/java/org/sunflow/core/shader/GlassShader.java +147 -0
- data/src/main/java/org/sunflow/core/shader/IDShader.java +27 -0
- data/src/main/java/org/sunflow/core/shader/MirrorShader.java +68 -0
- data/src/main/java/org/sunflow/core/shader/NormalShader.java +32 -0
- data/src/main/java/org/sunflow/core/shader/PhongShader.java +89 -0
- data/src/main/java/org/sunflow/core/shader/PrimIDShader.java +30 -0
- data/src/main/java/org/sunflow/core/shader/QuickGrayShader.java +63 -0
- data/src/main/java/org/sunflow/core/shader/ShinyDiffuseShader.java +98 -0
- data/src/main/java/org/sunflow/core/shader/SimpleShader.java +24 -0
- data/src/main/java/org/sunflow/core/shader/TexturedAmbientOcclusionShader.java +31 -0
- data/src/main/java/org/sunflow/core/shader/TexturedDiffuseShader.java +31 -0
- data/src/main/java/org/sunflow/core/shader/TexturedPhongShader.java +31 -0
- data/src/main/java/org/sunflow/core/shader/TexturedShinyDiffuseShader.java +31 -0
- data/src/main/java/org/sunflow/core/shader/TexturedWardShader.java +31 -0
- data/src/main/java/org/sunflow/core/shader/UVShader.java +27 -0
- data/src/main/java/org/sunflow/core/shader/UberShader.java +149 -0
- data/src/main/java/org/sunflow/core/shader/ViewCausticsShader.java +33 -0
- data/src/main/java/org/sunflow/core/shader/ViewGlobalPhotonsShader.java +25 -0
- data/src/main/java/org/sunflow/core/shader/ViewIrradianceShader.java +25 -0
- data/src/main/java/org/sunflow/core/shader/WireframeShader.java +83 -0
- data/src/main/java/org/sunflow/core/tesselatable/BezierMesh.java +254 -0
- data/src/main/java/org/sunflow/core/tesselatable/FileMesh.java +251 -0
- data/src/main/java/org/sunflow/core/tesselatable/Gumbo.java +1147 -0
- data/src/main/java/org/sunflow/core/tesselatable/Teapot.java +237 -0
- data/src/main/java/org/sunflow/image/Bitmap.java +15 -0
- data/src/main/java/org/sunflow/image/BitmapReader.java +39 -0
- data/src/main/java/org/sunflow/image/BitmapWriter.java +79 -0
- data/src/main/java/org/sunflow/image/BlackbodySpectrum.java +16 -0
- data/src/main/java/org/sunflow/image/ChromaticitySpectrum.java +55 -0
- data/src/main/java/org/sunflow/image/Color.java +374 -0
- data/src/main/java/org/sunflow/image/ColorEncoder.java +94 -0
- data/src/main/java/org/sunflow/image/ColorFactory.java +122 -0
- data/src/main/java/org/sunflow/image/ConstantSpectralCurve.java +21 -0
- data/src/main/java/org/sunflow/image/IrregularSpectralCurve.java +57 -0
- data/src/main/java/org/sunflow/image/RGBSpace.java +207 -0
- data/src/main/java/org/sunflow/image/RegularSpectralCurve.java +30 -0
- data/src/main/java/org/sunflow/image/SpectralCurve.java +118 -0
- data/src/main/java/org/sunflow/image/XYZColor.java +50 -0
- data/src/main/java/org/sunflow/image/formats/BitmapBlack.java +27 -0
- data/src/main/java/org/sunflow/image/formats/BitmapG8.java +36 -0
- data/src/main/java/org/sunflow/image/formats/BitmapGA8.java +30 -0
- data/src/main/java/org/sunflow/image/formats/BitmapRGB8.java +40 -0
- data/src/main/java/org/sunflow/image/formats/BitmapRGBA8.java +40 -0
- data/src/main/java/org/sunflow/image/formats/BitmapRGBE.java +60 -0
- data/src/main/java/org/sunflow/image/formats/BitmapXYZ.java +38 -0
- data/src/main/java/org/sunflow/image/formats/GenericBitmap.java +73 -0
- data/src/main/java/org/sunflow/image/readers/BMPBitmapReader.java +39 -0
- data/src/main/java/org/sunflow/image/readers/HDRBitmapReader.java +155 -0
- data/src/main/java/org/sunflow/image/readers/IGIBitmapReader.java +104 -0
- data/src/main/java/org/sunflow/image/readers/JPGBitmapReader.java +39 -0
- data/src/main/java/org/sunflow/image/readers/PNGBitmapReader.java +40 -0
- data/src/main/java/org/sunflow/image/readers/TGABitmapReader.java +141 -0
- data/src/main/java/org/sunflow/image/writers/EXRBitmapWriter.java +395 -0
- data/src/main/java/org/sunflow/image/writers/HDRBitmapWriter.java +54 -0
- data/src/main/java/org/sunflow/image/writers/IGIBitmapWriter.java +75 -0
- data/src/main/java/org/sunflow/image/writers/PNGBitmapWriter.java +39 -0
- data/src/main/java/org/sunflow/image/writers/TGABitmapWriter.java +63 -0
- data/src/main/java/org/sunflow/math/BoundingBox.java +340 -0
- data/src/main/java/org/sunflow/math/MathUtils.java +159 -0
- data/src/main/java/org/sunflow/math/Matrix4.java +573 -0
- data/src/main/java/org/sunflow/math/MovingMatrix4.java +119 -0
- data/src/main/java/org/sunflow/math/OrthoNormalBasis.java +110 -0
- data/src/main/java/org/sunflow/math/PerlinScalar.java +331 -0
- data/src/main/java/org/sunflow/math/PerlinVector.java +132 -0
- data/src/main/java/org/sunflow/math/Point2.java +36 -0
- data/src/main/java/org/sunflow/math/Point3.java +133 -0
- data/src/main/java/org/sunflow/math/QMC.java +209 -0
- data/src/main/java/org/sunflow/math/Solvers.java +142 -0
- data/src/main/java/org/sunflow/math/Vector3.java +197 -0
- data/src/main/java/org/sunflow/system/BenchmarkFramework.java +73 -0
- data/src/main/java/org/sunflow/system/BenchmarkTest.java +17 -0
- data/src/main/java/org/sunflow/system/ByteUtil.java +119 -0
- data/src/main/java/org/sunflow/system/FileUtils.java +27 -0
- data/src/main/java/org/sunflow/system/ImagePanel.java +282 -0
- data/src/main/java/org/sunflow/system/Memory.java +18 -0
- data/src/main/java/org/sunflow/system/Parser.java +162 -0
- data/src/main/java/org/sunflow/system/Plugins.java +142 -0
- data/src/main/java/org/sunflow/system/RenderGlobalsPanel.java +209 -0
- data/src/main/java/org/sunflow/system/SearchPath.java +67 -0
- data/src/main/java/org/sunflow/system/Timer.java +53 -0
- data/src/main/java/org/sunflow/system/UI.java +112 -0
- data/src/main/java/org/sunflow/system/UserInterface.java +46 -0
- data/src/main/java/org/sunflow/system/ui/ConsoleInterface.java +48 -0
- data/src/main/java/org/sunflow/system/ui/SilentInterface.java +28 -0
- data/src/main/java/org/sunflow/util/FastHashMap.java +220 -0
- data/src/main/java/org/sunflow/util/FloatArray.java +77 -0
- data/src/main/java/org/sunflow/util/IntArray.java +77 -0
- data/src/test/java/a_maintest.java +129 -0
- 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
|
+
}
|