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 }