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,234 @@
|
|
|
1
|
+
package org.sunflow.core.primitive;
|
|
2
|
+
|
|
3
|
+
import org.sunflow.SunflowAPI;
|
|
4
|
+
import org.sunflow.core.Instance;
|
|
5
|
+
import org.sunflow.core.IntersectionState;
|
|
6
|
+
import org.sunflow.core.ParameterList;
|
|
7
|
+
import org.sunflow.core.PrimitiveList;
|
|
8
|
+
import org.sunflow.core.Ray;
|
|
9
|
+
import org.sunflow.core.ShadingState;
|
|
10
|
+
import org.sunflow.math.BoundingBox;
|
|
11
|
+
import org.sunflow.math.MathUtils;
|
|
12
|
+
import org.sunflow.math.Matrix4;
|
|
13
|
+
import org.sunflow.math.OrthoNormalBasis;
|
|
14
|
+
import org.sunflow.math.Point3;
|
|
15
|
+
import org.sunflow.math.Vector3;
|
|
16
|
+
|
|
17
|
+
public class SphereFlake implements PrimitiveList {
|
|
18
|
+
|
|
19
|
+
private static final int MAX_LEVEL = 20;
|
|
20
|
+
private static final float[] BOUNDING_OFFSET_RADIUS = new float[MAX_LEVEL + 1];
|
|
21
|
+
private static final float[] RECURSIVE_PATTERN = new float[9 * 3];
|
|
22
|
+
private int level = 2;
|
|
23
|
+
private Vector3 axis = new Vector3(0, 0, 1);
|
|
24
|
+
private float baseRadius = 1;
|
|
25
|
+
|
|
26
|
+
static {
|
|
27
|
+
// geometric series table, to compute bounding radius quickly
|
|
28
|
+
for (int i = 0, r = 3; i < BOUNDING_OFFSET_RADIUS.length; i++, r *= 3) {
|
|
29
|
+
BOUNDING_OFFSET_RADIUS[i] = (r - 3.0f) / r;
|
|
30
|
+
}
|
|
31
|
+
// lower ring
|
|
32
|
+
double a = 0, daL = 2 * Math.PI / 6, daU = 2 * Math.PI / 3;
|
|
33
|
+
for (int i = 0; i < 6; i++) {
|
|
34
|
+
RECURSIVE_PATTERN[3 * i + 0] = -0.3f;
|
|
35
|
+
RECURSIVE_PATTERN[3 * i + 1] = (float) Math.sin(a);
|
|
36
|
+
RECURSIVE_PATTERN[3 * i + 2] = (float) Math.cos(a);
|
|
37
|
+
a += daL;
|
|
38
|
+
}
|
|
39
|
+
a -= daL / 2; // tweak
|
|
40
|
+
for (int i = 6; i < 9; i++) {
|
|
41
|
+
RECURSIVE_PATTERN[3 * i + 0] = +0.7f;
|
|
42
|
+
RECURSIVE_PATTERN[3 * i + 1] = (float) Math.sin(a);
|
|
43
|
+
RECURSIVE_PATTERN[3 * i + 2] = (float) Math.cos(a);
|
|
44
|
+
a += daU;
|
|
45
|
+
}
|
|
46
|
+
for (int i = 0; i < RECURSIVE_PATTERN.length; i += 3) {
|
|
47
|
+
float x = RECURSIVE_PATTERN[i + 0];
|
|
48
|
+
float y = RECURSIVE_PATTERN[i + 1];
|
|
49
|
+
float z = RECURSIVE_PATTERN[i + 2];
|
|
50
|
+
float n = 1 / (float) Math.sqrt(x * x + y * y + z * z);
|
|
51
|
+
RECURSIVE_PATTERN[i + 0] = x * n;
|
|
52
|
+
RECURSIVE_PATTERN[i + 1] = y * n;
|
|
53
|
+
RECURSIVE_PATTERN[i + 2] = z * n;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@Override
|
|
58
|
+
public boolean update(ParameterList pl, SunflowAPI api) {
|
|
59
|
+
level = MathUtils.clamp(pl.getInt("level", level), 0, 20);
|
|
60
|
+
axis = pl.getVector("axis", axis);
|
|
61
|
+
axis.normalize();
|
|
62
|
+
baseRadius = Math.abs(pl.getFloat("radius", baseRadius));
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@Override
|
|
67
|
+
public BoundingBox getWorldBounds(Matrix4 o2w) {
|
|
68
|
+
BoundingBox bounds = new BoundingBox(getPrimitiveBound(0, 1));
|
|
69
|
+
if (o2w != null) {
|
|
70
|
+
bounds = o2w.transform(bounds);
|
|
71
|
+
}
|
|
72
|
+
return bounds;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@Override
|
|
76
|
+
public float getPrimitiveBound(int primID, int i) {
|
|
77
|
+
float br = 1 + BOUNDING_OFFSET_RADIUS[level];
|
|
78
|
+
return (i & 1) == 0 ? -br : br;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@Override
|
|
82
|
+
public int getNumPrimitives() {
|
|
83
|
+
return 1;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@Override
|
|
87
|
+
public void prepareShadingState(ShadingState state) {
|
|
88
|
+
state.init();
|
|
89
|
+
state.getRay().getPoint(state.getPoint());
|
|
90
|
+
Instance parent = state.getInstance();
|
|
91
|
+
Point3 localPoint = state.transformWorldToObject(state.getPoint());
|
|
92
|
+
|
|
93
|
+
float cx = state.getU();
|
|
94
|
+
float cy = state.getV();
|
|
95
|
+
float cz = state.getW();
|
|
96
|
+
|
|
97
|
+
state.getNormal().set(localPoint.x - cx, localPoint.y - cy, localPoint.z - cz);
|
|
98
|
+
state.getNormal().normalize();
|
|
99
|
+
|
|
100
|
+
float phi = (float) Math.atan2(state.getNormal().y, state.getNormal().x);
|
|
101
|
+
if (phi < 0) {
|
|
102
|
+
phi += 2 * Math.PI;
|
|
103
|
+
}
|
|
104
|
+
float theta = (float) Math.acos(state.getNormal().z);
|
|
105
|
+
state.getUV().y = theta / (float) Math.PI;
|
|
106
|
+
state.getUV().x = phi / (float) (2 * Math.PI);
|
|
107
|
+
Vector3 v = new Vector3();
|
|
108
|
+
v.x = -2 * (float) Math.PI * state.getNormal().y;
|
|
109
|
+
v.y = 2 * (float) Math.PI * state.getNormal().x;
|
|
110
|
+
v.z = 0;
|
|
111
|
+
state.setShader(parent.getShader(0));
|
|
112
|
+
state.setModifier(parent.getModifier(0));
|
|
113
|
+
// into world space
|
|
114
|
+
Vector3 worldNormal = state.transformNormalObjectToWorld(state.getNormal());
|
|
115
|
+
v = state.transformVectorObjectToWorld(v);
|
|
116
|
+
state.getNormal().set(worldNormal);
|
|
117
|
+
state.getNormal().normalize();
|
|
118
|
+
state.getGeoNormal().set(state.getNormal());
|
|
119
|
+
// compute basis in world space
|
|
120
|
+
state.setBasis(OrthoNormalBasis.makeFromWV(state.getNormal(), v));
|
|
121
|
+
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
@Override
|
|
125
|
+
public void intersectPrimitive(Ray r, int primID, IntersectionState state) {
|
|
126
|
+
// intersect in local space
|
|
127
|
+
float qa = r.dx * r.dx + r.dy * r.dy + r.dz * r.dz;
|
|
128
|
+
intersectFlake(r, state, level, qa, 1 / qa, 0, 0, 0, axis.x, axis.y, axis.z, baseRadius);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private void intersectFlake(Ray r, IntersectionState state, int level, float qa, float qaInv, float cx, float cy, float cz, float dx, float dy, float dz, float radius) {
|
|
132
|
+
if (level <= 0) {
|
|
133
|
+
// we reached the bottom - intersect sphere and bail out
|
|
134
|
+
float vcx = cx - r.ox;
|
|
135
|
+
float vcy = cy - r.oy;
|
|
136
|
+
float vcz = cz - r.oz;
|
|
137
|
+
float b = r.dx * vcx + r.dy * vcy + r.dz * vcz;
|
|
138
|
+
float disc = b * b - qa * ((vcx * vcx + vcy * vcy + vcz * vcz) - radius * radius);
|
|
139
|
+
if (disc > 0) {
|
|
140
|
+
// intersects - check t values
|
|
141
|
+
float d = (float) Math.sqrt(disc);
|
|
142
|
+
float t1 = (b - d) * qaInv;
|
|
143
|
+
float t2 = (b + d) * qaInv;
|
|
144
|
+
if (t1 >= r.getMax() || t2 <= r.getMin()) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (t1 > r.getMin()) {
|
|
148
|
+
r.setMax(t1);
|
|
149
|
+
} else {
|
|
150
|
+
r.setMax(t2);
|
|
151
|
+
}
|
|
152
|
+
state.setIntersection(0, cx, cy, cz);
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
float boundRadius = radius * (1 + BOUNDING_OFFSET_RADIUS[level]);
|
|
156
|
+
float vcx = cx - r.ox;
|
|
157
|
+
float vcy = cy - r.oy;
|
|
158
|
+
float vcz = cz - r.oz;
|
|
159
|
+
float b = r.dx * vcx + r.dy * vcy + r.dz * vcz;
|
|
160
|
+
float vcd = (vcx * vcx + vcy * vcy + vcz * vcz);
|
|
161
|
+
float disc = b * b - qa * (vcd - boundRadius * boundRadius);
|
|
162
|
+
if (disc > 0) {
|
|
163
|
+
// intersects - check t values
|
|
164
|
+
float d = (float) Math.sqrt(disc);
|
|
165
|
+
float t1 = (b - d) * qaInv;
|
|
166
|
+
float t2 = (b + d) * qaInv;
|
|
167
|
+
if (t1 >= r.getMax() || t2 <= r.getMin()) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// we hit the bounds, now compute intersection with the actual
|
|
172
|
+
// leaf sphere
|
|
173
|
+
disc = b * b - qa * (vcd - radius * radius);
|
|
174
|
+
if (disc > 0) {
|
|
175
|
+
d = (float) Math.sqrt(disc);
|
|
176
|
+
t1 = (b - d) * qaInv;
|
|
177
|
+
t2 = (b + d) * qaInv;
|
|
178
|
+
if (t1 >= r.getMax() || t2 <= r.getMin()) {
|
|
179
|
+
// no hit
|
|
180
|
+
} else {
|
|
181
|
+
if (t1 > r.getMin()) {
|
|
182
|
+
r.setMax(t1);
|
|
183
|
+
} else {
|
|
184
|
+
r.setMax(t2);
|
|
185
|
+
}
|
|
186
|
+
state.setIntersection(0, cx, cy, cz);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// recursively intersect 9 other spheres
|
|
191
|
+
// step1: compute basis around displacement vector
|
|
192
|
+
float b1x, b1y, b1z;
|
|
193
|
+
if (dx * dx < dy * dy && dx * dx < dz * dz) {
|
|
194
|
+
b1x = 0;
|
|
195
|
+
b1y = dz;
|
|
196
|
+
b1z = -dy;
|
|
197
|
+
} else if (dy * dy < dz * dz) {
|
|
198
|
+
b1x = dz;
|
|
199
|
+
b1y = 0;
|
|
200
|
+
b1z = -dx;
|
|
201
|
+
} else {
|
|
202
|
+
b1x = dy;
|
|
203
|
+
b1y = -dx;
|
|
204
|
+
b1z = 0;
|
|
205
|
+
}
|
|
206
|
+
float n = 1 / (float) Math.sqrt(b1x * b1x + b1y * b1y + b1z * b1z);
|
|
207
|
+
b1x *= n;
|
|
208
|
+
b1y *= n;
|
|
209
|
+
b1z *= n;
|
|
210
|
+
float b2x = dy * b1z - dz * b1y;
|
|
211
|
+
float b2y = dz * b1x - dx * b1z;
|
|
212
|
+
float b2z = dx * b1y - dy * b1x;
|
|
213
|
+
b1x = dy * b2z - dz * b2y;
|
|
214
|
+
b1y = dz * b2x - dx * b2z;
|
|
215
|
+
b1z = dx * b2y - dy * b2x;
|
|
216
|
+
// step2: generate 9 children recursively
|
|
217
|
+
float nr = radius * (1 / 3.0f), scale = radius + nr;
|
|
218
|
+
for (int i = 0; i < 9 * 3; i += 3) {
|
|
219
|
+
// transform by basis
|
|
220
|
+
float ndx = RECURSIVE_PATTERN[i] * dx + RECURSIVE_PATTERN[i + 1] * b1x + RECURSIVE_PATTERN[i + 2] * b2x;
|
|
221
|
+
float ndy = RECURSIVE_PATTERN[i] * dy + RECURSIVE_PATTERN[i + 1] * b1y + RECURSIVE_PATTERN[i + 2] * b2y;
|
|
222
|
+
float ndz = RECURSIVE_PATTERN[i] * dz + RECURSIVE_PATTERN[i + 1] * b1z + RECURSIVE_PATTERN[i + 2] * b2z;
|
|
223
|
+
// recurse!
|
|
224
|
+
intersectFlake(r, state, level - 1, qa, qaInv, cx + scale * ndx, cy + scale * ndy, cz + scale * ndz, ndx, ndy, ndz, nr);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
@Override
|
|
231
|
+
public PrimitiveList getBakingPrimitives() {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
package org.sunflow.core.primitive;
|
|
2
|
+
|
|
3
|
+
import org.sunflow.SunflowAPI;
|
|
4
|
+
import org.sunflow.core.Instance;
|
|
5
|
+
import org.sunflow.core.IntersectionState;
|
|
6
|
+
import org.sunflow.core.ParameterList;
|
|
7
|
+
import org.sunflow.core.PrimitiveList;
|
|
8
|
+
import org.sunflow.core.Ray;
|
|
9
|
+
import org.sunflow.core.ShadingState;
|
|
10
|
+
import org.sunflow.math.BoundingBox;
|
|
11
|
+
import org.sunflow.math.MathUtils;
|
|
12
|
+
import org.sunflow.math.Matrix4;
|
|
13
|
+
import org.sunflow.math.OrthoNormalBasis;
|
|
14
|
+
import org.sunflow.math.Point3;
|
|
15
|
+
import org.sunflow.math.Solvers;
|
|
16
|
+
import org.sunflow.math.Vector3;
|
|
17
|
+
|
|
18
|
+
public class Torus implements PrimitiveList {
|
|
19
|
+
|
|
20
|
+
private float ri2, ro2;
|
|
21
|
+
private float ri, ro;
|
|
22
|
+
|
|
23
|
+
public Torus() {
|
|
24
|
+
ri = 0.25f;
|
|
25
|
+
ro = 1;
|
|
26
|
+
ri2 = ri * ri;
|
|
27
|
+
ro2 = ro * ro;
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@Override
|
|
32
|
+
public boolean update(ParameterList pl, SunflowAPI api) {
|
|
33
|
+
ri = pl.getFloat("radiusInner", ri);
|
|
34
|
+
ro = pl.getFloat("radiusOuter", ro);
|
|
35
|
+
ri2 = ri * ri;
|
|
36
|
+
ro2 = ro * ro;
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@Override
|
|
41
|
+
public BoundingBox getWorldBounds(Matrix4 o2w) {
|
|
42
|
+
BoundingBox bounds = new BoundingBox(-ro - ri, -ro - ri, -ri);
|
|
43
|
+
bounds.include(ro + ri, ro + ri, ri);
|
|
44
|
+
if (o2w != null) {
|
|
45
|
+
bounds = o2w.transform(bounds);
|
|
46
|
+
}
|
|
47
|
+
return bounds;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@Override
|
|
51
|
+
public float getPrimitiveBound(int primID, int i) {
|
|
52
|
+
switch (i) {
|
|
53
|
+
case 0:
|
|
54
|
+
case 2:
|
|
55
|
+
return -ro - ri;
|
|
56
|
+
case 1:
|
|
57
|
+
case 3:
|
|
58
|
+
return ro + ri;
|
|
59
|
+
case 4:
|
|
60
|
+
return -ri;
|
|
61
|
+
case 5:
|
|
62
|
+
return ri;
|
|
63
|
+
default:
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@Override
|
|
69
|
+
public int getNumPrimitives() {
|
|
70
|
+
return 1;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@Override
|
|
74
|
+
public void prepareShadingState(ShadingState state) {
|
|
75
|
+
state.init();
|
|
76
|
+
state.getRay().getPoint(state.getPoint());
|
|
77
|
+
Instance parent = state.getInstance();
|
|
78
|
+
// get local point
|
|
79
|
+
Point3 p = state.transformWorldToObject(state.getPoint());
|
|
80
|
+
// compute local normal
|
|
81
|
+
float deriv = p.x * p.x + p.y * p.y + p.z * p.z - ri2 - ro2;
|
|
82
|
+
state.getNormal().set(p.x * deriv, p.y * deriv, p.z * deriv + 2 * ro2 * p.z);
|
|
83
|
+
state.getNormal().normalize();
|
|
84
|
+
|
|
85
|
+
double phi = Math.asin(MathUtils.clamp(p.z / ri, -1, 1));
|
|
86
|
+
double theta = Math.atan2(p.y, p.x);
|
|
87
|
+
if (theta < 0) {
|
|
88
|
+
theta += 2 * Math.PI;
|
|
89
|
+
}
|
|
90
|
+
state.getUV().x = (float) (theta / (2 * Math.PI));
|
|
91
|
+
state.getUV().y = (float) ((phi + Math.PI / 2) / Math.PI);
|
|
92
|
+
state.setShader(parent.getShader(0));
|
|
93
|
+
state.setModifier(parent.getModifier(0));
|
|
94
|
+
// into world space
|
|
95
|
+
Vector3 worldNormal = state.transformNormalObjectToWorld(state.getNormal());
|
|
96
|
+
state.getNormal().set(worldNormal);
|
|
97
|
+
state.getNormal().normalize();
|
|
98
|
+
state.getGeoNormal().set(state.getNormal());
|
|
99
|
+
// make basis in world space
|
|
100
|
+
state.setBasis(OrthoNormalBasis.makeFromW(state.getNormal()));
|
|
101
|
+
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@Override
|
|
105
|
+
public void intersectPrimitive(Ray r, int primID, IntersectionState state) {
|
|
106
|
+
// intersect in local space
|
|
107
|
+
float rd2x = r.dx * r.dx;
|
|
108
|
+
float rd2y = r.dy * r.dy;
|
|
109
|
+
float rd2z = r.dz * r.dz;
|
|
110
|
+
float ro2x = r.ox * r.ox;
|
|
111
|
+
float ro2y = r.oy * r.oy;
|
|
112
|
+
float ro2z = r.oz * r.oz;
|
|
113
|
+
// compute some common factors
|
|
114
|
+
double alpha = rd2x + rd2y + rd2z;
|
|
115
|
+
double beta = 2 * (r.ox * r.dx + r.oy * r.dy + r.oz * r.dz);
|
|
116
|
+
double gamma = (ro2x + ro2y + ro2z) - ri2 - ro2;
|
|
117
|
+
// setup quartic coefficients
|
|
118
|
+
double A = alpha * alpha;
|
|
119
|
+
double B = 2 * alpha * beta;
|
|
120
|
+
double C = beta * beta + 2 * alpha * gamma + 4 * ro2 * rd2z;
|
|
121
|
+
double D = 2 * beta * gamma + 8 * ro2 * r.oz * r.dz;
|
|
122
|
+
double E = gamma * gamma + 4 * ro2 * ro2z - 4 * ro2 * ri2;
|
|
123
|
+
// solve equation
|
|
124
|
+
double[] t = Solvers.solveQuartic(A, B, C, D, E);
|
|
125
|
+
if (t != null) {
|
|
126
|
+
// early rejection
|
|
127
|
+
if (t[0] >= r.getMax() || t[t.length - 1] <= r.getMin()) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
// find first intersection in front of the ray
|
|
131
|
+
for (int i = 0; i < t.length; i++) {
|
|
132
|
+
if (t[i] > r.getMin()) {
|
|
133
|
+
r.setMax((float) t[i]);
|
|
134
|
+
state.setIntersection(0);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
@Override
|
|
142
|
+
public PrimitiveList getBakingPrimitives() {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -0,0 +1,849 @@
|
|
|
1
|
+
package org.sunflow.core.primitive;
|
|
2
|
+
|
|
3
|
+
import java.io.FileWriter;
|
|
4
|
+
import java.io.IOException;
|
|
5
|
+
import java.util.Locale;
|
|
6
|
+
import java.util.logging.Level;
|
|
7
|
+
import java.util.logging.Logger;
|
|
8
|
+
|
|
9
|
+
import org.sunflow.SunflowAPI;
|
|
10
|
+
import org.sunflow.core.Instance;
|
|
11
|
+
import org.sunflow.core.IntersectionState;
|
|
12
|
+
import org.sunflow.core.ParameterList;
|
|
13
|
+
import org.sunflow.core.PrimitiveList;
|
|
14
|
+
import org.sunflow.core.Ray;
|
|
15
|
+
import org.sunflow.core.ShadingState;
|
|
16
|
+
import org.sunflow.core.ParameterList.FloatParameter;
|
|
17
|
+
import org.sunflow.core.ParameterList.InterpolationType;
|
|
18
|
+
import org.sunflow.math.BoundingBox;
|
|
19
|
+
import org.sunflow.math.MathUtils;
|
|
20
|
+
import org.sunflow.math.Matrix4;
|
|
21
|
+
import org.sunflow.math.OrthoNormalBasis;
|
|
22
|
+
import org.sunflow.math.Point3;
|
|
23
|
+
import org.sunflow.math.Vector3;
|
|
24
|
+
import org.sunflow.system.UI;
|
|
25
|
+
import org.sunflow.system.UI.Module;
|
|
26
|
+
|
|
27
|
+
public class TriangleMesh implements PrimitiveList {
|
|
28
|
+
|
|
29
|
+
private static boolean smallTriangles = false;
|
|
30
|
+
protected float[] points;
|
|
31
|
+
protected int[] triangles;
|
|
32
|
+
private WaldTriangle[] triaccel;
|
|
33
|
+
private FloatParameter normals;
|
|
34
|
+
private FloatParameter uvs;
|
|
35
|
+
private byte[] faceShaders;
|
|
36
|
+
|
|
37
|
+
public static void setSmallTriangles(boolean smallTriangles) {
|
|
38
|
+
if (smallTriangles) {
|
|
39
|
+
UI.printInfo(Module.GEOM, "Small trimesh mode: enabled");
|
|
40
|
+
} else {
|
|
41
|
+
UI.printInfo(Module.GEOM, "Small trimesh mode: disabled");
|
|
42
|
+
}
|
|
43
|
+
TriangleMesh.smallTriangles = smallTriangles;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public TriangleMesh() {
|
|
47
|
+
triangles = null;
|
|
48
|
+
points = null;
|
|
49
|
+
normals = uvs = new FloatParameter();
|
|
50
|
+
faceShaders = null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public void writeObj(String filename) {
|
|
54
|
+
try {
|
|
55
|
+
try (FileWriter file = new FileWriter(filename)) {
|
|
56
|
+
file.write(String.format("o object\n"));
|
|
57
|
+
for (int i = 0; i < points.length; i += 3) {
|
|
58
|
+
file.write(String.format("v %g %g %g\n", points[i], points[i + 1], points[i + 2]));
|
|
59
|
+
}
|
|
60
|
+
file.write("s off\n");
|
|
61
|
+
for (int i = 0; i < triangles.length; i += 3) {
|
|
62
|
+
file.write(String.format("f %d %d %d\n", triangles[i] + 1, triangles[i + 1] + 1, triangles[i + 2] + 1));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
} catch (IOException e) {
|
|
66
|
+
Logger.getLogger(TriangleMesh.class.getName()).log(Level.SEVERE, null, e);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@Override
|
|
71
|
+
public boolean update(ParameterList pl, SunflowAPI api) {
|
|
72
|
+
boolean updatedTopology = false;
|
|
73
|
+
{
|
|
74
|
+
int[] trianglesu = pl.getIntArray("triangles");
|
|
75
|
+
if (trianglesu != null) {
|
|
76
|
+
this.triangles = trianglesu;
|
|
77
|
+
updatedTopology = true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (triangles == null) {
|
|
81
|
+
UI.printError(Module.GEOM, "Unable to update mesh - triangle indices are missing");
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
if (triangles.length % 3 != 0) {
|
|
85
|
+
UI.printWarning(Module.GEOM, "Triangle index data is not a multiple of 3 - triangles may be missing");
|
|
86
|
+
}
|
|
87
|
+
pl.setFaceCount(triangles.length / 3);
|
|
88
|
+
{
|
|
89
|
+
FloatParameter pointsP = pl.getPointArray("points");
|
|
90
|
+
if (pointsP != null) {
|
|
91
|
+
if (pointsP.interp != InterpolationType.VERTEX) {
|
|
92
|
+
UI.printError(Module.GEOM, "Point interpolation type must be set to \"vertex\" - was \"%s\"", pointsP.interp.name().toLowerCase(Locale.ENGLISH));
|
|
93
|
+
} else {
|
|
94
|
+
points = pointsP.data;
|
|
95
|
+
updatedTopology = true;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (points == null) {
|
|
100
|
+
UI.printError(Module.GEOM, "Unable to update mesh - vertices are missing");
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
pl.setVertexCount(points.length / 3);
|
|
104
|
+
pl.setFaceVertexCount(3 * (triangles.length / 3));
|
|
105
|
+
FloatParameter normalsu = pl.getVectorArray("normals");
|
|
106
|
+
if (normalsu != null) {
|
|
107
|
+
this.normals = normalsu;
|
|
108
|
+
}
|
|
109
|
+
FloatParameter uvsu = pl.getTexCoordArray("uvs");
|
|
110
|
+
if (uvsu != null) {
|
|
111
|
+
this.uvs = uvsu;
|
|
112
|
+
}
|
|
113
|
+
int[] faceShadersu = pl.getIntArray("faceshaders");
|
|
114
|
+
if (faceShadersu != null && faceShadersu.length == triangles.length / 3) {
|
|
115
|
+
this.faceShaders = new byte[faceShadersu.length];
|
|
116
|
+
for (int i = 0; i < faceShadersu.length; i++) {
|
|
117
|
+
int v = faceShadersu[i];
|
|
118
|
+
if (v > 255) {
|
|
119
|
+
UI.printWarning(Module.GEOM, "Shader index too large on triangle %d", i);
|
|
120
|
+
}
|
|
121
|
+
this.faceShaders[i] = (byte) (v & 0xFF);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (updatedTopology) {
|
|
125
|
+
// create triangle acceleration structure
|
|
126
|
+
init();
|
|
127
|
+
}
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
@Override
|
|
132
|
+
public float getPrimitiveBound(int primID, int i) {
|
|
133
|
+
int tri = 3 * primID;
|
|
134
|
+
int a = 3 * triangles[tri + 0];
|
|
135
|
+
int b = 3 * triangles[tri + 1];
|
|
136
|
+
int c = 3 * triangles[tri + 2];
|
|
137
|
+
int axis = i >>> 1;
|
|
138
|
+
if ((i & 1) == 0) {
|
|
139
|
+
return MathUtils.min(points[a + axis], points[b + axis], points[c + axis]);
|
|
140
|
+
} else {
|
|
141
|
+
return MathUtils.max(points[a + axis], points[b + axis], points[c + axis]);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
@Override
|
|
146
|
+
public BoundingBox getWorldBounds(Matrix4 o2w) {
|
|
147
|
+
BoundingBox bounds = new BoundingBox();
|
|
148
|
+
if (o2w == null) {
|
|
149
|
+
for (int i = 0; i < points.length; i += 3) {
|
|
150
|
+
bounds.include(points[i], points[i + 1], points[i + 2]);
|
|
151
|
+
}
|
|
152
|
+
} else {
|
|
153
|
+
// transform vertices first
|
|
154
|
+
for (int i = 0; i < points.length; i += 3) {
|
|
155
|
+
float x = points[i];
|
|
156
|
+
float y = points[i + 1];
|
|
157
|
+
float z = points[i + 2];
|
|
158
|
+
float wx = o2w.transformPX(x, y, z);
|
|
159
|
+
float wy = o2w.transformPY(x, y, z);
|
|
160
|
+
float wz = o2w.transformPZ(x, y, z);
|
|
161
|
+
bounds.include(wx, wy, wz);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return bounds;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private void intersectTriangleKensler(Ray r, int primID, IntersectionState state) {
|
|
168
|
+
int tri = 3 * primID;
|
|
169
|
+
int a = 3 * triangles[tri + 0];
|
|
170
|
+
int b = 3 * triangles[tri + 1];
|
|
171
|
+
int c = 3 * triangles[tri + 2];
|
|
172
|
+
float edge0x = points[b + 0] - points[a + 0];
|
|
173
|
+
float edge0y = points[b + 1] - points[a + 1];
|
|
174
|
+
float edge0z = points[b + 2] - points[a + 2];
|
|
175
|
+
float edge1x = points[a + 0] - points[c + 0];
|
|
176
|
+
float edge1y = points[a + 1] - points[c + 1];
|
|
177
|
+
float edge1z = points[a + 2] - points[c + 2];
|
|
178
|
+
float nx = edge0y * edge1z - edge0z * edge1y;
|
|
179
|
+
float ny = edge0z * edge1x - edge0x * edge1z;
|
|
180
|
+
float nz = edge0x * edge1y - edge0y * edge1x;
|
|
181
|
+
float v = r.dot(nx, ny, nz);
|
|
182
|
+
float iv = 1 / v;
|
|
183
|
+
float edge2x = points[a + 0] - r.ox;
|
|
184
|
+
float edge2y = points[a + 1] - r.oy;
|
|
185
|
+
float edge2z = points[a + 2] - r.oz;
|
|
186
|
+
float va = nx * edge2x + ny * edge2y + nz * edge2z;
|
|
187
|
+
float t = iv * va;
|
|
188
|
+
if (!r.isInside(t)) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
float ix = edge2y * r.dz - edge2z * r.dy;
|
|
192
|
+
float iy = edge2z * r.dx - edge2x * r.dz;
|
|
193
|
+
float iz = edge2x * r.dy - edge2y * r.dx;
|
|
194
|
+
float v1 = ix * edge1x + iy * edge1y + iz * edge1z;
|
|
195
|
+
float beta = iv * v1;
|
|
196
|
+
if (beta < 0) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
float v2 = ix * edge0x + iy * edge0y + iz * edge0z;
|
|
200
|
+
if ((v1 + v2) * v > v * v) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
float gamma = iv * v2;
|
|
204
|
+
if (gamma < 0) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
r.setMax(t);
|
|
208
|
+
state.setIntersection(primID, beta, gamma);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
@Override
|
|
212
|
+
public void intersectPrimitive(Ray r, int primID, IntersectionState state) {
|
|
213
|
+
// alternative test -- disabled for now
|
|
214
|
+
// intersectPrimitiveRobust(r, primID, state);
|
|
215
|
+
|
|
216
|
+
if (triaccel != null) {
|
|
217
|
+
// optional fast intersection method
|
|
218
|
+
triaccel[primID].intersect(r, primID, state);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
intersectTriangleKensler(r, primID, state);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
@Override
|
|
225
|
+
public int getNumPrimitives() {
|
|
226
|
+
return triangles.length / 3;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
@Override
|
|
230
|
+
public void prepareShadingState(ShadingState state) {
|
|
231
|
+
state.init();
|
|
232
|
+
Instance parent = state.getInstance();
|
|
233
|
+
int primID = state.getPrimitiveID();
|
|
234
|
+
float u = state.getU();
|
|
235
|
+
float v = state.getV();
|
|
236
|
+
float w = 1 - u - v;
|
|
237
|
+
state.getRay().getPoint(state.getPoint());
|
|
238
|
+
int tri = 3 * primID;
|
|
239
|
+
int index0 = triangles[tri + 0];
|
|
240
|
+
int index1 = triangles[tri + 1];
|
|
241
|
+
int index2 = triangles[tri + 2];
|
|
242
|
+
Point3 v0p = getPoint(index0);
|
|
243
|
+
Point3 v1p = getPoint(index1);
|
|
244
|
+
Point3 v2p = getPoint(index2);
|
|
245
|
+
Vector3 ng = Point3.normal(v0p, v1p, v2p);
|
|
246
|
+
ng = state.transformNormalObjectToWorld(ng);
|
|
247
|
+
ng.normalize();
|
|
248
|
+
state.getGeoNormal().set(ng);
|
|
249
|
+
switch (normals.interp) {
|
|
250
|
+
case NONE:
|
|
251
|
+
case FACE: {
|
|
252
|
+
state.getNormal().set(ng);
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
case VERTEX: {
|
|
256
|
+
int i30 = 3 * index0;
|
|
257
|
+
int i31 = 3 * index1;
|
|
258
|
+
int i32 = 3 * index2;
|
|
259
|
+
float[] normalsu = this.normals.data;
|
|
260
|
+
state.getNormal().x = w * normalsu[i30 + 0] + u * normalsu[i31 + 0] + v * normalsu[i32 + 0];
|
|
261
|
+
state.getNormal().y = w * normalsu[i30 + 1] + u * normalsu[i31 + 1] + v * normalsu[i32 + 1];
|
|
262
|
+
state.getNormal().z = w * normalsu[i30 + 2] + u * normalsu[i31 + 2] + v * normalsu[i32 + 2];
|
|
263
|
+
state.getNormal().set(state.transformNormalObjectToWorld(state.getNormal()));
|
|
264
|
+
state.getNormal().normalize();
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
case FACEVARYING: {
|
|
268
|
+
int idx = 3 * tri;
|
|
269
|
+
float[] normalsu = this.normals.data;
|
|
270
|
+
state.getNormal().x = w * normalsu[idx + 0] + u * normalsu[idx + 3] + v * normalsu[idx + 6];
|
|
271
|
+
state.getNormal().y = w * normalsu[idx + 1] + u * normalsu[idx + 4] + v * normalsu[idx + 7];
|
|
272
|
+
state.getNormal().z = w * normalsu[idx + 2] + u * normalsu[idx + 5] + v * normalsu[idx + 8];
|
|
273
|
+
state.getNormal().set(state.transformNormalObjectToWorld(state.getNormal()));
|
|
274
|
+
state.getNormal().normalize();
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
float uv00 = 0, uv01 = 0, uv10 = 0, uv11 = 0, uv20 = 0, uv21 = 0;
|
|
279
|
+
switch (uvs.interp) {
|
|
280
|
+
case NONE:
|
|
281
|
+
case FACE: {
|
|
282
|
+
state.getUV().x = 0;
|
|
283
|
+
state.getUV().y = 0;
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
case VERTEX: {
|
|
287
|
+
int i20 = 2 * index0;
|
|
288
|
+
int i21 = 2 * index1;
|
|
289
|
+
int i22 = 2 * index2;
|
|
290
|
+
float[] uvsu = this.uvs.data;
|
|
291
|
+
uv00 = uvsu[i20 + 0];
|
|
292
|
+
uv01 = uvsu[i20 + 1];
|
|
293
|
+
uv10 = uvsu[i21 + 0];
|
|
294
|
+
uv11 = uvsu[i21 + 1];
|
|
295
|
+
uv20 = uvsu[i22 + 0];
|
|
296
|
+
uv21 = uvsu[i22 + 1];
|
|
297
|
+
break;
|
|
298
|
+
}
|
|
299
|
+
case FACEVARYING: {
|
|
300
|
+
int idx = tri << 1;
|
|
301
|
+
float[] uvsu = this.uvs.data;
|
|
302
|
+
uv00 = uvsu[idx + 0];
|
|
303
|
+
uv01 = uvsu[idx + 1];
|
|
304
|
+
uv10 = uvsu[idx + 2];
|
|
305
|
+
uv11 = uvsu[idx + 3];
|
|
306
|
+
uv20 = uvsu[idx + 4];
|
|
307
|
+
uv21 = uvsu[idx + 5];
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
if (uvs.interp != InterpolationType.NONE) {
|
|
312
|
+
// get exact uv coords and compute tangent vectors
|
|
313
|
+
state.getUV().x = w * uv00 + u * uv10 + v * uv20;
|
|
314
|
+
state.getUV().y = w * uv01 + u * uv11 + v * uv21;
|
|
315
|
+
float du1 = uv00 - uv20;
|
|
316
|
+
float du2 = uv10 - uv20;
|
|
317
|
+
float dv1 = uv01 - uv21;
|
|
318
|
+
float dv2 = uv11 - uv21;
|
|
319
|
+
Vector3 dp1 = Point3.sub(v0p, v2p, new Vector3()), dp2 = Point3.sub(v1p, v2p, new Vector3());
|
|
320
|
+
float determinant = du1 * dv2 - dv1 * du2;
|
|
321
|
+
if (determinant == 0.0f) {
|
|
322
|
+
// create basis in world space
|
|
323
|
+
state.setBasis(OrthoNormalBasis.makeFromW(state.getNormal()));
|
|
324
|
+
} else {
|
|
325
|
+
float invdet = 1.f / determinant;
|
|
326
|
+
// Vector3 dpdu = new Vector3();
|
|
327
|
+
// dpdu.x = (dv2 * dp1.x - dv1 * dp2.x) * invdet;
|
|
328
|
+
// dpdu.y = (dv2 * dp1.y - dv1 * dp2.y) * invdet;
|
|
329
|
+
// dpdu.z = (dv2 * dp1.z - dv1 * dp2.z) * invdet;
|
|
330
|
+
Vector3 dpdv = new Vector3();
|
|
331
|
+
dpdv.x = (-du2 * dp1.x + du1 * dp2.x) * invdet;
|
|
332
|
+
dpdv.y = (-du2 * dp1.y + du1 * dp2.y) * invdet;
|
|
333
|
+
dpdv.z = (-du2 * dp1.z + du1 * dp2.z) * invdet;
|
|
334
|
+
dpdv = state.transformVectorObjectToWorld(dpdv);
|
|
335
|
+
// create basis in world space
|
|
336
|
+
state.setBasis(OrthoNormalBasis.makeFromWV(state.getNormal(), dpdv));
|
|
337
|
+
}
|
|
338
|
+
} else {
|
|
339
|
+
state.setBasis(OrthoNormalBasis.makeFromW(state.getNormal()));
|
|
340
|
+
}
|
|
341
|
+
int shaderIndex = faceShaders == null ? 0 : (faceShaders[primID] & 0xFF);
|
|
342
|
+
state.setShader(parent.getShader(shaderIndex));
|
|
343
|
+
state.setModifier(parent.getModifier(shaderIndex));
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
public void init() {
|
|
347
|
+
triaccel = null;
|
|
348
|
+
int nt = getNumPrimitives();
|
|
349
|
+
if (!smallTriangles) {
|
|
350
|
+
// too many triangles? -- don't generate triaccel to save memory
|
|
351
|
+
if (nt > 2000000) {
|
|
352
|
+
UI.printWarning(Module.GEOM, "TRI - Too many triangles -- triaccel generation skipped");
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
triaccel = new WaldTriangle[nt];
|
|
356
|
+
for (int i = 0; i < nt; i++) {
|
|
357
|
+
triaccel[i] = new WaldTriangle(this, i);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
protected Point3 getPoint(int i) {
|
|
363
|
+
i *= 3;
|
|
364
|
+
return new Point3(points[i], points[i + 1], points[i + 2]);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
public void getPoint(int tri, int i, Point3 p) {
|
|
368
|
+
int index = 3 * triangles[3 * tri + i];
|
|
369
|
+
p.set(points[index], points[index + 1], points[index + 2]);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
private static final class WaldTriangle {
|
|
373
|
+
// private data for fast triangle intersection testing
|
|
374
|
+
|
|
375
|
+
private int k;
|
|
376
|
+
private final float nu;
|
|
377
|
+
private final float nv;
|
|
378
|
+
private float nd;
|
|
379
|
+
private final float bnu;
|
|
380
|
+
private final float bnv;
|
|
381
|
+
private float bnd;
|
|
382
|
+
private final float cnu;
|
|
383
|
+
private final float cnv;
|
|
384
|
+
private final float cnd;
|
|
385
|
+
|
|
386
|
+
private WaldTriangle(TriangleMesh mesh, int tri) {
|
|
387
|
+
k = 0;
|
|
388
|
+
tri *= 3;
|
|
389
|
+
int index0 = mesh.triangles[tri + 0];
|
|
390
|
+
int index1 = mesh.triangles[tri + 1];
|
|
391
|
+
int index2 = mesh.triangles[tri + 2];
|
|
392
|
+
Point3 v0p = mesh.getPoint(index0);
|
|
393
|
+
Point3 v1p = mesh.getPoint(index1);
|
|
394
|
+
Point3 v2p = mesh.getPoint(index2);
|
|
395
|
+
Vector3 ng = Point3.normal(v0p, v1p, v2p);
|
|
396
|
+
if (Math.abs(ng.x) > Math.abs(ng.y) && Math.abs(ng.x) > Math.abs(ng.z)) {
|
|
397
|
+
k = 0;
|
|
398
|
+
} else if (Math.abs(ng.y) > Math.abs(ng.z)) {
|
|
399
|
+
k = 1;
|
|
400
|
+
} else {
|
|
401
|
+
k = 2;
|
|
402
|
+
}
|
|
403
|
+
float ax, ay, bx, by, cx, cy;
|
|
404
|
+
switch (k) {
|
|
405
|
+
case 0: {
|
|
406
|
+
nu = ng.y / ng.x;
|
|
407
|
+
nv = ng.z / ng.x;
|
|
408
|
+
nd = v0p.x + (nu * v0p.y) + (nv * v0p.z);
|
|
409
|
+
ax = v0p.y;
|
|
410
|
+
ay = v0p.z;
|
|
411
|
+
bx = v2p.y - ax;
|
|
412
|
+
by = v2p.z - ay;
|
|
413
|
+
cx = v1p.y - ax;
|
|
414
|
+
cy = v1p.z - ay;
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
case 1: {
|
|
418
|
+
nu = ng.z / ng.y;
|
|
419
|
+
nv = ng.x / ng.y;
|
|
420
|
+
nd = (nv * v0p.x) + v0p.y + (nu * v0p.z);
|
|
421
|
+
ax = v0p.z;
|
|
422
|
+
ay = v0p.x;
|
|
423
|
+
bx = v2p.z - ax;
|
|
424
|
+
by = v2p.x - ay;
|
|
425
|
+
cx = v1p.z - ax;
|
|
426
|
+
cy = v1p.x - ay;
|
|
427
|
+
break;
|
|
428
|
+
}
|
|
429
|
+
case 2:
|
|
430
|
+
default: {
|
|
431
|
+
nu = ng.x / ng.z;
|
|
432
|
+
nv = ng.y / ng.z;
|
|
433
|
+
nd = (nu * v0p.x) + (nv * v0p.y) + v0p.z;
|
|
434
|
+
ax = v0p.x;
|
|
435
|
+
ay = v0p.y;
|
|
436
|
+
bx = v2p.x - ax;
|
|
437
|
+
by = v2p.y - ay;
|
|
438
|
+
cx = v1p.x - ax;
|
|
439
|
+
cy = v1p.y - ay;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
float det = bx * cy - by * cx;
|
|
443
|
+
bnu = -by / det;
|
|
444
|
+
bnv = bx / det;
|
|
445
|
+
bnd = (by * ax - bx * ay) / det;
|
|
446
|
+
cnu = cy / det;
|
|
447
|
+
cnv = -cx / det;
|
|
448
|
+
cnd = (cx * ay - cy * ax) / det;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
void intersect(Ray r, int primID, IntersectionState state) {
|
|
452
|
+
switch (k) {
|
|
453
|
+
case 0: {
|
|
454
|
+
float det = 1.0f / (r.dx + nu * r.dy + nv * r.dz);
|
|
455
|
+
float t = (nd - r.ox - nu * r.oy - nv * r.oz) * det;
|
|
456
|
+
if (!r.isInside(t)) {
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
float hu = r.oy + t * r.dy;
|
|
460
|
+
float hv = r.oz + t * r.dz;
|
|
461
|
+
float u = hu * bnu + hv * bnv + bnd;
|
|
462
|
+
if (u < 0.0f) {
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
float v = hu * cnu + hv * cnv + cnd;
|
|
466
|
+
if (v < 0.0f) {
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
if (u + v > 1.0f) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
r.setMax(t);
|
|
473
|
+
state.setIntersection(primID, u, v);
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
case 1: {
|
|
477
|
+
float det = 1.0f / (r.dy + nu * r.dz + nv * r.dx);
|
|
478
|
+
float t = (nd - r.oy - nu * r.oz - nv * r.ox) * det;
|
|
479
|
+
if (!r.isInside(t)) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
float hu = r.oz + t * r.dz;
|
|
483
|
+
float hv = r.ox + t * r.dx;
|
|
484
|
+
float u = hu * bnu + hv * bnv + bnd;
|
|
485
|
+
if (u < 0.0f) {
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
float v = hu * cnu + hv * cnv + cnd;
|
|
489
|
+
if (v < 0.0f) {
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
if (u + v > 1.0f) {
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
r.setMax(t);
|
|
496
|
+
state.setIntersection(primID, u, v);
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
case 2: {
|
|
500
|
+
float det = 1.0f / (r.dz + nu * r.dx + nv * r.dy);
|
|
501
|
+
float t = (nd - r.oz - nu * r.ox - nv * r.oy) * det;
|
|
502
|
+
if (!r.isInside(t)) {
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
float hu = r.ox + t * r.dx;
|
|
506
|
+
float hv = r.oy + t * r.dy;
|
|
507
|
+
float u = hu * bnu + hv * bnv + bnd;
|
|
508
|
+
if (u < 0.0f) {
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
float v = hu * cnu + hv * cnv + cnd;
|
|
512
|
+
if (v < 0.0f) {
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
if (u + v > 1.0f) {
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
r.setMax(t);
|
|
519
|
+
state.setIntersection(primID, u, v);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
@Override
|
|
526
|
+
public PrimitiveList getBakingPrimitives() {
|
|
527
|
+
switch (uvs.interp) {
|
|
528
|
+
case NONE:
|
|
529
|
+
case FACE:
|
|
530
|
+
UI.printError(Module.GEOM, "Cannot generate baking surface without texture coordinate data");
|
|
531
|
+
return null;
|
|
532
|
+
default:
|
|
533
|
+
return new BakingSurface();
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
private class BakingSurface implements PrimitiveList {
|
|
538
|
+
|
|
539
|
+
@Override
|
|
540
|
+
public PrimitiveList getBakingPrimitives() {
|
|
541
|
+
return null;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
@Override
|
|
545
|
+
public int getNumPrimitives() {
|
|
546
|
+
return TriangleMesh.this.getNumPrimitives();
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
@Override
|
|
550
|
+
public float getPrimitiveBound(int primID, int i) {
|
|
551
|
+
if (i > 3) {
|
|
552
|
+
return 0;
|
|
553
|
+
}
|
|
554
|
+
switch (uvs.interp) {
|
|
555
|
+
case NONE:
|
|
556
|
+
case FACE:
|
|
557
|
+
default: {
|
|
558
|
+
return 0;
|
|
559
|
+
}
|
|
560
|
+
case VERTEX: {
|
|
561
|
+
int tri = 3 * primID;
|
|
562
|
+
int index0 = triangles[tri + 0];
|
|
563
|
+
int index1 = triangles[tri + 1];
|
|
564
|
+
int index2 = triangles[tri + 2];
|
|
565
|
+
int i20 = 2 * index0;
|
|
566
|
+
int i21 = 2 * index1;
|
|
567
|
+
int i22 = 2 * index2;
|
|
568
|
+
float[] uvs = TriangleMesh.this.uvs.data;
|
|
569
|
+
switch (i) {
|
|
570
|
+
case 0:
|
|
571
|
+
return MathUtils.min(uvs[i20 + 0], uvs[i21 + 0], uvs[i22 + 0]);
|
|
572
|
+
case 1:
|
|
573
|
+
return MathUtils.max(uvs[i20 + 0], uvs[i21 + 0], uvs[i22 + 0]);
|
|
574
|
+
case 2:
|
|
575
|
+
return MathUtils.min(uvs[i20 + 1], uvs[i21 + 1], uvs[i22 + 1]);
|
|
576
|
+
case 3:
|
|
577
|
+
return MathUtils.max(uvs[i20 + 1], uvs[i21 + 1], uvs[i22 + 1]);
|
|
578
|
+
default:
|
|
579
|
+
return 0;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
case FACEVARYING: {
|
|
583
|
+
int idx = 6 * primID;
|
|
584
|
+
float[] uvs = TriangleMesh.this.uvs.data;
|
|
585
|
+
switch (i) {
|
|
586
|
+
case 0:
|
|
587
|
+
return MathUtils.min(uvs[idx + 0], uvs[idx + 2], uvs[idx + 4]);
|
|
588
|
+
case 1:
|
|
589
|
+
return MathUtils.max(uvs[idx + 0], uvs[idx + 2], uvs[idx + 4]);
|
|
590
|
+
case 2:
|
|
591
|
+
return MathUtils.min(uvs[idx + 1], uvs[idx + 3], uvs[idx + 5]);
|
|
592
|
+
case 3:
|
|
593
|
+
return MathUtils.max(uvs[idx + 1], uvs[idx + 3], uvs[idx + 5]);
|
|
594
|
+
default:
|
|
595
|
+
return 0;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
@Override
|
|
602
|
+
public BoundingBox getWorldBounds(Matrix4 o2w) {
|
|
603
|
+
BoundingBox bounds = new BoundingBox();
|
|
604
|
+
if (o2w == null) {
|
|
605
|
+
for (int i = 0; i < uvs.data.length; i += 2) {
|
|
606
|
+
bounds.include(uvs.data[i], uvs.data[i + 1], 0);
|
|
607
|
+
}
|
|
608
|
+
} else {
|
|
609
|
+
// transform vertices first
|
|
610
|
+
for (int i = 0; i < uvs.data.length; i += 2) {
|
|
611
|
+
float x = uvs.data[i];
|
|
612
|
+
float y = uvs.data[i + 1];
|
|
613
|
+
float wx = o2w.transformPX(x, y, 0);
|
|
614
|
+
float wy = o2w.transformPY(x, y, 0);
|
|
615
|
+
float wz = o2w.transformPZ(x, y, 0);
|
|
616
|
+
bounds.include(wx, wy, wz);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
return bounds;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
@Override
|
|
623
|
+
public void intersectPrimitive(Ray r, int primID, IntersectionState state) {
|
|
624
|
+
float uv00 = 0, uv01 = 0, uv10 = 0, uv11 = 0, uv20 = 0, uv21 = 0;
|
|
625
|
+
switch (uvs.interp) {
|
|
626
|
+
case NONE:
|
|
627
|
+
case FACE:
|
|
628
|
+
default:
|
|
629
|
+
return;
|
|
630
|
+
case VERTEX: {
|
|
631
|
+
int tri = 3 * primID;
|
|
632
|
+
int index0 = triangles[tri + 0];
|
|
633
|
+
int index1 = triangles[tri + 1];
|
|
634
|
+
int index2 = triangles[tri + 2];
|
|
635
|
+
int i20 = 2 * index0;
|
|
636
|
+
int i21 = 2 * index1;
|
|
637
|
+
int i22 = 2 * index2;
|
|
638
|
+
float[] uvs = TriangleMesh.this.uvs.data;
|
|
639
|
+
uv00 = uvs[i20 + 0];
|
|
640
|
+
uv01 = uvs[i20 + 1];
|
|
641
|
+
uv10 = uvs[i21 + 0];
|
|
642
|
+
uv11 = uvs[i21 + 1];
|
|
643
|
+
uv20 = uvs[i22 + 0];
|
|
644
|
+
uv21 = uvs[i22 + 1];
|
|
645
|
+
break;
|
|
646
|
+
|
|
647
|
+
}
|
|
648
|
+
case FACEVARYING: {
|
|
649
|
+
int idx = (3 * primID) << 1;
|
|
650
|
+
float[] uvs = TriangleMesh.this.uvs.data;
|
|
651
|
+
uv00 = uvs[idx + 0];
|
|
652
|
+
uv01 = uvs[idx + 1];
|
|
653
|
+
uv10 = uvs[idx + 2];
|
|
654
|
+
uv11 = uvs[idx + 3];
|
|
655
|
+
uv20 = uvs[idx + 4];
|
|
656
|
+
uv21 = uvs[idx + 5];
|
|
657
|
+
break;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
double edge1x = uv10 - uv00;
|
|
662
|
+
double edge1y = uv11 - uv01;
|
|
663
|
+
double edge2x = uv20 - uv00;
|
|
664
|
+
double edge2y = uv21 - uv01;
|
|
665
|
+
double pvecx = r.dy * 0 - r.dz * edge2y;
|
|
666
|
+
double pvecy = r.dz * edge2x - r.dx * 0;
|
|
667
|
+
double pvecz = r.dx * edge2y - r.dy * edge2x;
|
|
668
|
+
double qvecx, qvecy, qvecz;
|
|
669
|
+
double u, v;
|
|
670
|
+
double det = edge1x * pvecx + edge1y * pvecy + 0 * pvecz;
|
|
671
|
+
if (det > 0) {
|
|
672
|
+
double tvecx = r.ox - uv00;
|
|
673
|
+
double tvecy = r.oy - uv01;
|
|
674
|
+
double tvecz = r.oz;
|
|
675
|
+
u = (tvecx * pvecx + tvecy * pvecy + tvecz * pvecz);
|
|
676
|
+
if (u < 0.0 || u > det) {
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
qvecx = tvecy * 0 - tvecz * edge1y;
|
|
680
|
+
qvecy = tvecz * edge1x - tvecx * 0;
|
|
681
|
+
qvecz = tvecx * edge1y - tvecy * edge1x;
|
|
682
|
+
v = (r.dx * qvecx + r.dy * qvecy + r.dz * qvecz);
|
|
683
|
+
if (v < 0.0 || u + v > det) {
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
} else if (det < 0) {
|
|
687
|
+
double tvecx = r.ox - uv00;
|
|
688
|
+
double tvecy = r.oy - uv01;
|
|
689
|
+
double tvecz = r.oz;
|
|
690
|
+
u = (tvecx * pvecx + tvecy * pvecy + tvecz * pvecz);
|
|
691
|
+
if (u > 0.0 || u < det) {
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
qvecx = tvecy * 0 - tvecz * edge1y;
|
|
695
|
+
qvecy = tvecz * edge1x - tvecx * 0;
|
|
696
|
+
qvecz = tvecx * edge1y - tvecy * edge1x;
|
|
697
|
+
v = (r.dx * qvecx + r.dy * qvecy + r.dz * qvecz);
|
|
698
|
+
if (v > 0.0 || u + v < det) {
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
} else {
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
double inv_det = 1.0 / det;
|
|
705
|
+
float t = (float) ((edge2x * qvecx + edge2y * qvecy + 0 * qvecz) * inv_det);
|
|
706
|
+
if (r.isInside(t)) {
|
|
707
|
+
r.setMax(t);
|
|
708
|
+
state.setIntersection(primID, (float) (u * inv_det), (float) (v * inv_det));
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
@Override
|
|
713
|
+
public void prepareShadingState(ShadingState state) {
|
|
714
|
+
state.init();
|
|
715
|
+
Instance parent = state.getInstance();
|
|
716
|
+
int primID = state.getPrimitiveID();
|
|
717
|
+
float u = state.getU();
|
|
718
|
+
float v = state.getV();
|
|
719
|
+
float w = 1 - u - v;
|
|
720
|
+
// state.getRay().getPoint(state.getPoint());
|
|
721
|
+
int tri = 3 * primID;
|
|
722
|
+
int index0 = triangles[tri + 0];
|
|
723
|
+
int index1 = triangles[tri + 1];
|
|
724
|
+
int index2 = triangles[tri + 2];
|
|
725
|
+
Point3 v0p = getPoint(index0);
|
|
726
|
+
Point3 v1p = getPoint(index1);
|
|
727
|
+
Point3 v2p = getPoint(index2);
|
|
728
|
+
|
|
729
|
+
// get object space point from barycentric coordinates
|
|
730
|
+
state.getPoint().x = w * v0p.x + u * v1p.x + v * v2p.x;
|
|
731
|
+
state.getPoint().y = w * v0p.y + u * v1p.y + v * v2p.y;
|
|
732
|
+
state.getPoint().z = w * v0p.z + u * v1p.z + v * v2p.z;
|
|
733
|
+
// move into world space
|
|
734
|
+
state.getPoint().set(state.transformObjectToWorld(state.getPoint()));
|
|
735
|
+
|
|
736
|
+
Vector3 ng = Point3.normal(v0p, v1p, v2p);
|
|
737
|
+
if (parent != null) {
|
|
738
|
+
ng = state.transformNormalObjectToWorld(ng);
|
|
739
|
+
}
|
|
740
|
+
ng.normalize();
|
|
741
|
+
state.getGeoNormal().set(ng);
|
|
742
|
+
switch (normals.interp) {
|
|
743
|
+
case NONE:
|
|
744
|
+
case FACE: {
|
|
745
|
+
state.getNormal().set(ng);
|
|
746
|
+
break;
|
|
747
|
+
}
|
|
748
|
+
case VERTEX: {
|
|
749
|
+
int i30 = 3 * index0;
|
|
750
|
+
int i31 = 3 * index1;
|
|
751
|
+
int i32 = 3 * index2;
|
|
752
|
+
float[] normals = TriangleMesh.this.normals.data;
|
|
753
|
+
state.getNormal().x = w * normals[i30 + 0] + u * normals[i31 + 0] + v * normals[i32 + 0];
|
|
754
|
+
state.getNormal().y = w * normals[i30 + 1] + u * normals[i31 + 1] + v * normals[i32 + 1];
|
|
755
|
+
state.getNormal().z = w * normals[i30 + 2] + u * normals[i31 + 2] + v * normals[i32 + 2];
|
|
756
|
+
if (parent != null) {
|
|
757
|
+
state.getNormal().set(state.transformNormalObjectToWorld(state.getNormal()));
|
|
758
|
+
}
|
|
759
|
+
state.getNormal().normalize();
|
|
760
|
+
break;
|
|
761
|
+
}
|
|
762
|
+
case FACEVARYING: {
|
|
763
|
+
int idx = 3 * tri;
|
|
764
|
+
float[] normals = TriangleMesh.this.normals.data;
|
|
765
|
+
state.getNormal().x = w * normals[idx + 0] + u * normals[idx + 3] + v * normals[idx + 6];
|
|
766
|
+
state.getNormal().y = w * normals[idx + 1] + u * normals[idx + 4] + v * normals[idx + 7];
|
|
767
|
+
state.getNormal().z = w * normals[idx + 2] + u * normals[idx + 5] + v * normals[idx + 8];
|
|
768
|
+
if (parent != null) {
|
|
769
|
+
state.getNormal().set(state.transformNormalObjectToWorld(state.getNormal()));
|
|
770
|
+
}
|
|
771
|
+
state.getNormal().normalize();
|
|
772
|
+
break;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
float uv00 = 0, uv01 = 0, uv10 = 0, uv11 = 0, uv20 = 0, uv21 = 0;
|
|
776
|
+
switch (uvs.interp) {
|
|
777
|
+
case NONE:
|
|
778
|
+
case FACE: {
|
|
779
|
+
state.getUV().x = 0;
|
|
780
|
+
state.getUV().y = 0;
|
|
781
|
+
break;
|
|
782
|
+
}
|
|
783
|
+
case VERTEX: {
|
|
784
|
+
int i20 = 2 * index0;
|
|
785
|
+
int i21 = 2 * index1;
|
|
786
|
+
int i22 = 2 * index2;
|
|
787
|
+
float[] uvs = TriangleMesh.this.uvs.data;
|
|
788
|
+
uv00 = uvs[i20 + 0];
|
|
789
|
+
uv01 = uvs[i20 + 1];
|
|
790
|
+
uv10 = uvs[i21 + 0];
|
|
791
|
+
uv11 = uvs[i21 + 1];
|
|
792
|
+
uv20 = uvs[i22 + 0];
|
|
793
|
+
uv21 = uvs[i22 + 1];
|
|
794
|
+
break;
|
|
795
|
+
}
|
|
796
|
+
case FACEVARYING: {
|
|
797
|
+
int idx = tri << 1;
|
|
798
|
+
float[] uvs = TriangleMesh.this.uvs.data;
|
|
799
|
+
uv00 = uvs[idx + 0];
|
|
800
|
+
uv01 = uvs[idx + 1];
|
|
801
|
+
uv10 = uvs[idx + 2];
|
|
802
|
+
uv11 = uvs[idx + 3];
|
|
803
|
+
uv20 = uvs[idx + 4];
|
|
804
|
+
uv21 = uvs[idx + 5];
|
|
805
|
+
break;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
if (uvs.interp != InterpolationType.NONE) {
|
|
809
|
+
// get exact uv coords and compute tangent vectors
|
|
810
|
+
state.getUV().x = w * uv00 + u * uv10 + v * uv20;
|
|
811
|
+
state.getUV().y = w * uv01 + u * uv11 + v * uv21;
|
|
812
|
+
float du1 = uv00 - uv20;
|
|
813
|
+
float du2 = uv10 - uv20;
|
|
814
|
+
float dv1 = uv01 - uv21;
|
|
815
|
+
float dv2 = uv11 - uv21;
|
|
816
|
+
Vector3 dp1 = Point3.sub(v0p, v2p, new Vector3()), dp2 = Point3.sub(v1p, v2p, new Vector3());
|
|
817
|
+
float determinant = du1 * dv2 - dv1 * du2;
|
|
818
|
+
if (determinant == 0.0f) {
|
|
819
|
+
// create basis in world space
|
|
820
|
+
state.setBasis(OrthoNormalBasis.makeFromW(state.getNormal()));
|
|
821
|
+
} else {
|
|
822
|
+
float invdet = 1.f / determinant;
|
|
823
|
+
// Vector3 dpdu = new Vector3();
|
|
824
|
+
// dpdu.x = (dv2 * dp1.x - dv1 * dp2.x) * invdet;
|
|
825
|
+
// dpdu.y = (dv2 * dp1.y - dv1 * dp2.y) * invdet;
|
|
826
|
+
// dpdu.z = (dv2 * dp1.z - dv1 * dp2.z) * invdet;
|
|
827
|
+
Vector3 dpdv = new Vector3();
|
|
828
|
+
dpdv.x = (-du2 * dp1.x + du1 * dp2.x) * invdet;
|
|
829
|
+
dpdv.y = (-du2 * dp1.y + du1 * dp2.y) * invdet;
|
|
830
|
+
dpdv.z = (-du2 * dp1.z + du1 * dp2.z) * invdet;
|
|
831
|
+
if (parent != null) {
|
|
832
|
+
dpdv = state.transformVectorObjectToWorld(dpdv);
|
|
833
|
+
}
|
|
834
|
+
// create basis in world space
|
|
835
|
+
state.setBasis(OrthoNormalBasis.makeFromWV(state.getNormal(), dpdv));
|
|
836
|
+
}
|
|
837
|
+
} else {
|
|
838
|
+
state.setBasis(OrthoNormalBasis.makeFromW(state.getNormal()));
|
|
839
|
+
}
|
|
840
|
+
int shaderIndex = faceShaders == null ? 0 : (faceShaders[primID] & 0xFF);
|
|
841
|
+
state.setShader(parent.getShader(shaderIndex));
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
@Override
|
|
845
|
+
public boolean update(ParameterList pl, SunflowAPI api) {
|
|
846
|
+
return true;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
}
|