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