joonsrenderer 1.1-java

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