joonsrenderer 1.1-java

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