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