1 /* 2 * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package test3d; 27 28 import com.sun.javafx.geom.Vec3f; 29 import java.util.ArrayList; 30 import java.util.Collection; 31 import java.util.concurrent.atomic.AtomicBoolean; 32 import javafx.application.ConditionalFeature; 33 import javafx.application.Platform; 34 import javafx.scene.AmbientLight; 35 import javafx.scene.Group; 36 import javafx.scene.PerspectiveCamera; 37 import javafx.scene.PointLight; 38 import javafx.scene.Scene; 39 import javafx.scene.image.WritableImage; 40 import javafx.scene.paint.Color; 41 import javafx.scene.paint.PhongMaterial; 42 import javafx.scene.shape.Box; 43 import javafx.scene.shape.MeshView; 44 import javafx.scene.shape.Shape3D; 45 import javafx.scene.shape.Sphere; 46 import javafx.scene.shape.TriangleMesh; 47 import javafx.scene.shape.VertexFormat; 48 import javafx.stage.Stage; 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 import org.junit.runners.Parameterized; 52 import testharness.VisualTestBase; 53 54 /** 55 * 3D Snapshot validation tests. 56 */ 57 @RunWith(Parameterized.class) 58 public class MeshCompareTest extends VisualTestBase { 59 60 private static Collection params = null; 61 62 private static final Object[] pNumLights = { 0, 1, 2, 3 }; 63 64 @Parameterized.Parameters 65 public static Collection getParams() { 66 if (params == null) { 67 params = new ArrayList(); 68 for (Object o1 : pNumLights) { 69 params.add(new Object[]{o1}); 70 } 71 } 72 return params; 73 } 74 75 private static final double TOLERANCE = 0.07; 76 private static final int WIDTH = 400; 77 private static final int HEIGHT = 400; 78 private static final int SAMPLE_X1 = 100; 79 private static final int SAMPLE_Y1 = 100; 80 private static final int SAMPLE_X2 = 200; 81 private static final int SAMPLE_Y2 = 200; 82 private static final int SAMPLE_X3 = 300; 83 private static final int SAMPLE_Y3 = 300; 84 private static final Color bgColor = Color.rgb(10, 10, 40); 85 86 private Stage testStage; 87 private Scene testScene; 88 private WritableImage wImage; 89 private MeshView shape; 90 91 private int numLights; 92 93 public MeshCompareTest(int numLights) { 94 this.numLights = numLights; 95 } 96 97 private TriangleMesh createICOSphere(float scale) { 98 final int pointSize = 3; // x, y, z 99 final int texCoordSize = 2; // u, v 100 final int faceSize = 9; // 3 point indices, 3 normal indices and 3 texCoord indices per triangle 101 102 // create 12 vertices of a icosahedron 103 int numVerts = 12; 104 float t = (float) ((1.0 + Math.sqrt(5.0)) / 2.0); 105 float points[] = { 106 -1, t, 0, 107 1, t, 0, 108 -1, -t, 0, 109 1, -t, 0, 110 0, -1, t, 111 0, 1, t, 112 0, -1, -t, 113 0, 1, -t, 114 t, 0, -1, 115 t, 0, 1, 116 -t, 0, -1, 117 -t, 0, 1 118 }; 119 120 float texCoords[] = new float[numVerts * texCoordSize]; 121 122 for(int i = 0; i < numVerts; i++) { 123 int pointIndex = i * pointSize; 124 points[pointIndex] *= scale; 125 points[pointIndex + 1] *= scale; 126 points[pointIndex + 2] *= scale; 127 int texCoordIndex = i * texCoordSize; 128 texCoords[texCoordIndex] = 0f; 129 texCoords[texCoordIndex + 1] = 0f; 130 131 } 132 133 // create 20 triangles of the icosahedron 134 int faces[] = { 135 0, 0, 11, 0, 5, 0, 136 0, 0, 5, 0, 1, 0, 137 0, 0, 1, 0, 7, 0, 138 0, 0, 7, 0, 10, 0, 139 0, 0, 10, 0, 11, 0, 140 1, 0, 5, 0, 9, 0, 141 5, 0, 11, 0, 4, 0, 142 11, 0, 10, 0, 2, 0, 143 10, 0, 7, 0, 6, 0, 144 7, 0, 1, 0, 8, 0, 145 3, 0, 9, 0, 4, 0, 146 3, 0, 4, 0, 2, 0, 147 3, 0, 2, 0, 6, 0, 148 3, 0, 6, 0, 8, 0, 149 3, 0, 8, 0, 9, 0, 150 4, 0, 9, 0, 5, 0, 151 2, 0, 4, 0, 11, 0, 152 6, 0, 2, 0, 10, 0, 153 8, 0, 6, 0, 7, 0, 154 9, 0, 8, 0, 1, 0 155 }; 156 157 TriangleMesh triangleMesh = new TriangleMesh(VertexFormat.POINT_TEXCOORD); 158 triangleMesh.getPoints().setAll(points); 159 triangleMesh.getTexCoords().setAll(texCoords); 160 triangleMesh.getFaces().setAll(faces); 161 162 return triangleMesh; 163 } 164 165 private TriangleMesh createPNTICOSphere(float scale) { 166 final int pointSize = 3; // x, y, z 167 final int normalSize = 3; // nx, ny, nz 168 final int texCoordSize = 2; // u, v 169 final int faceSize = 9; // 3 point indices, 3 normal indices and 3 texCoord indices per triangle 170 171 // create 12 vertices of a icosahedron 172 int numVerts = 12; 173 float points[] = new float[numVerts * pointSize]; 174 float normals[] = new float[numVerts * normalSize]; 175 float texCoords[] = new float[numVerts * texCoordSize]; 176 177 Vec3f[] arrV = new Vec3f[numVerts]; 178 float t = (float) ((1.0 + Math.sqrt(5.0)) / 2.0); 179 arrV[0] = new Vec3f(-1, t, 0); 180 arrV[1] = new Vec3f(1, t, 0); 181 arrV[2] = new Vec3f(-1, -t, 0); 182 arrV[3] = new Vec3f(1, -t, 0); 183 184 arrV[4] = new Vec3f(0, -1, t); 185 arrV[5] = new Vec3f(0, 1, t); 186 arrV[6] = new Vec3f(0, -1, -t); 187 arrV[7] = new Vec3f(0, 1, -t); 188 189 arrV[8] = new Vec3f(t, 0, -1); 190 arrV[9] = new Vec3f(t, 0, 1); 191 arrV[10] = new Vec3f(-t, 0, -1); 192 arrV[11] = new Vec3f(-t, 0, 1); 193 194 for(int i = 0; i < numVerts; i++) { 195 int pointIndex = i * pointSize; 196 points[pointIndex] = scale * arrV[i].x; 197 points[pointIndex + 1] = scale * arrV[i].y; 198 points[pointIndex + 2] = scale * arrV[i].z; 199 200 int normalIndex = i * normalSize; 201 arrV[i].normalize(); 202 normals[normalIndex] = arrV[i].x; 203 normals[normalIndex + 1] = arrV[i].y; 204 normals[normalIndex + 2] = arrV[i].z; 205 206 int texCoordIndex = i * texCoordSize; 207 texCoords[texCoordIndex] = 0f; 208 texCoords[texCoordIndex + 1] = 0f; 209 } 210 211 // create 20 triangles of the icosahedron 212 int faces[] = { 213 0, 0, 0, 11, 11, 0, 5, 5, 0, 214 0, 0, 0, 5, 5, 0, 1, 1, 0, 215 0, 0, 0, 1, 1, 0, 7, 7, 0, 216 0, 0, 0, 7, 7, 0, 10, 10, 0, 217 0, 0, 0, 10, 10, 0, 11, 11, 0, 218 1, 1, 0, 5, 5, 0, 9, 9, 0, 219 5, 5, 0, 11, 11, 0, 4, 4, 0, 220 11, 11, 0, 10, 10, 0, 2, 2, 0, 221 10, 10, 0, 7, 7, 0, 6, 6, 0, 222 7, 7, 0, 1, 1, 0, 8, 8, 0, 223 3, 3, 0, 9, 9, 0, 4, 4, 0, 224 3, 3, 0, 4, 4, 0, 2, 2, 0, 225 3, 3, 0, 2, 2, 0, 6, 6, 0, 226 3, 3, 0, 6, 6, 0, 8, 8, 0, 227 3, 3, 0, 8, 8, 0, 9, 9, 0, 228 4, 4, 0, 9, 9, 0, 5, 5, 0, 229 2, 2, 0, 4, 4, 0, 11, 11, 0, 230 6, 6, 0, 2, 2, 0, 10, 10, 0, 231 8, 8, 0, 6, 6, 0, 7, 7, 0, 232 9, 9, 0, 8, 8, 0, 1, 1, 0 233 }; 234 235 TriangleMesh triangleMesh = new TriangleMesh(VertexFormat.POINT_NORMAL_TEXCOORD); 236 triangleMesh.getPoints().setAll(points); 237 triangleMesh.getNormals().setAll(normals); 238 triangleMesh.getTexCoords().setAll(texCoords); 239 triangleMesh.getFaces().setAll(faces); 240 241 return triangleMesh; 242 } 243 244 private Scene buildScene() { 245 Group root = new Group(); 246 247 PhongMaterial material = new PhongMaterial(); 248 material.setDiffuseColor(Color.WHITE); 249 material.setSpecularColor(null); 250 shape = new MeshView(); 251 shape.setMesh(createPNTICOSphere(100)); 252 shape.setTranslateX(200); 253 shape.setTranslateY(200); 254 shape.setTranslateZ(10); 255 shape.setMaterial(material); 256 root.getChildren().add(shape); 257 258 if (numLights >= 1) { 259 AmbientLight ambLight = new AmbientLight(Color.LIMEGREEN); 260 root.getChildren().add(ambLight); 261 } 262 263 if (numLights >= 2) { 264 PointLight pointLight = new PointLight(Color.RED); 265 pointLight.setTranslateX(75); 266 pointLight.setTranslateY(-50); 267 pointLight.setTranslateZ(-200); 268 root.getChildren().add(pointLight); 269 } 270 271 if (numLights >= 3) { 272 PointLight pointLight = new PointLight(Color.BLUE); 273 pointLight.setTranslateX(225); 274 pointLight.setTranslateY(50); 275 pointLight.setTranslateZ(-300); 276 root.getChildren().add(pointLight); 277 } 278 279 PerspectiveCamera camera = new PerspectiveCamera(); 280 281 Scene scene = new Scene(root, WIDTH, HEIGHT, false); 282 scene.setFill(bgColor); 283 scene.setCamera(camera); 284 285 return scene; 286 } 287 288 private void compareColors(Scene scene, WritableImage wImage, int x, int y) { 289 Color exColor = getColor(scene, x, y); 290 Color sColor = wImage.getPixelReader().getColor(x, y); 291 assertColorEquals(exColor, sColor, TOLERANCE); 292 } 293 294 // ------------------------------------------------------------- 295 // Tests 296 // ------------------------------------------------------------- 297 298 @Test(timeout=5000) 299 public void testSnapshot3D() { 300 final AtomicBoolean scene3dSupported = new AtomicBoolean(); 301 runAndWait(() -> scene3dSupported.set(Platform.isSupported(ConditionalFeature.SCENE3D))); 302 if (!scene3dSupported.get()) { 303 System.out.println("*************************************************************"); 304 System.out.println("* Platform isn't SCENE3D capable, skipping 3D test. *"); 305 System.out.println("*************************************************************"); 306 return; 307 } 308 309 runAndWait(() -> { 310 testStage = getStage(); 311 testStage.setTitle("Mesh VertexFormat Compare Test"); 312 313 testScene = buildScene(); 314 315 // Take snapshot 316 wImage = testScene.snapshot(null); 317 318 shape.setMesh(createICOSphere(100)); 319 testStage.setScene(testScene); 320 testStage.show(); 321 }); 322 waitFirstFrame(); 323 runAndWait(() -> { 324 // Compare the colors in the snapshot image with those rendered to the scene 325 compareColors(testScene, wImage, SAMPLE_X1, SAMPLE_Y1); 326 compareColors(testScene, wImage, SAMPLE_X2, SAMPLE_Y2); 327 compareColors(testScene, wImage, SAMPLE_X3, SAMPLE_Y3); 328 }); 329 } 330 331 }