From d11c9c6709737451fc5a4bce85a52ce018667801 Mon Sep 17 00:00:00 2001 From: Kneesnap Date: Fri, 30 Aug 2024 16:42:47 -0700 Subject: [PATCH] Initial Commit (Add TriangleMesh VertexColor support) --- .../src/fx83dfeatures/DynamicMeshViewer.java | 531 ++++++++++++++++++ .../fxml/builder/TriangleMeshBuilder.java | 14 + .../main/java/com/sun/javafx/geom/Vec4f.java | 7 + .../sun/javafx/sg/prism/NGTriangleMesh.java | 47 +- .../src/main/java/com/sun/prism/Mesh.java | 7 +- .../com/sun/prism/es2/ES2PhongShader.java | 1 + .../java/com/sun/prism/impl/BaseMesh.java | 332 ++++++++--- .../java/com/sun/prism/impl/MeshVertex.java | 3 +- .../java/javafx/scene/shape/TriangleMesh.java | 200 +++++-- .../java/javafx/scene/shape/VertexFormat.java | 36 +- .../src/main/native-prism-d3d/D3DContext.h | 1 + .../src/main/native-prism-d3d/D3DMesh.cc | 2 +- .../main/native-prism-d3d/hlsl/Mtl1PS.hlsl | 4 +- .../main/native-prism-d3d/hlsl/Mtl1VS.hlsl | 1 + .../src/main/native-prism-d3d/hlsl/vs2ps.h | 2 + .../src/main/native-prism-d3d/hlsl/vsDecl.h | 3 +- .../src/main/native-prism-es2/GLContext.c | 6 + .../src/main/native-prism-es2/PrismES2Defs.h | 4 +- .../com/sun/prism/es2/glsl/diffuse_color.frag | 2 +- .../com/sun/prism/es2/glsl/diffuse_none.frag | 2 +- .../sun/prism/es2/glsl/diffuse_texture.frag | 2 +- .../com/sun/prism/es2/glsl/main.vert | 5 +- .../com/sun/prism/es2/glsl/main0Lights.frag | 2 + .../com/sun/prism/es2/glsl/main1Light.frag | 2 + .../com/sun/prism/es2/glsl/main2Lights.frag | 2 + .../com/sun/prism/es2/glsl/main3Lights.frag | 2 + .../sun/prism/es2/glsl/selfIllum_texture.frag | 2 +- .../javafx/sg/prism/NGTriangleMeshShim.java | 5 + .../javafx/sg/prism/NGTriangleMeshTest.java | 30 + .../javafx/scene/shape/TriangleMeshTest.java | 142 +++++ .../impl/PNTMeshVertexBufferLengthTest.java | 59 +- .../test3d/TriangleMeshPTCValidationTest.java | 254 +++++++++ 32 files changed, 1542 insertions(+), 170 deletions(-) create mode 100644 apps/toys/FX8-3DFeatures/src/fx83dfeatures/DynamicMeshViewer.java create mode 100644 tests/system/src/test/java/test/robot/test3d/TriangleMeshPTCValidationTest.java diff --git a/apps/toys/FX8-3DFeatures/src/fx83dfeatures/DynamicMeshViewer.java b/apps/toys/FX8-3DFeatures/src/fx83dfeatures/DynamicMeshViewer.java new file mode 100644 index 00000000000..c54a86bdb25 --- /dev/null +++ b/apps/toys/FX8-3DFeatures/src/fx83dfeatures/DynamicMeshViewer.java @@ -0,0 +1,531 @@ +/* + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package fx83dfeatures; + +import java.util.Arrays; +import javafx.application.Application; +import javafx.event.EventHandler; +import javafx.scene.*; +import javafx.scene.control.Button; +import javafx.scene.image.Image; +import javafx.scene.input.KeyEvent; +import javafx.scene.paint.Color; +import javafx.scene.paint.PhongMaterial; +import javafx.scene.shape.*; +import javafx.scene.transform.Rotate; +import javafx.stage.Stage; + +public class DynamicMeshViewer extends Application { + + Group root; + PointLight pointLight; + MeshView meshView; + TriangleMesh triMesh; + PhongMaterial material; + VertexFormat vertexFormat = VertexFormat.POINT_TEXCOORD_COLOR; + + int divX = 100; + int divY = 100; + boolean smooth = true; + int smoothGroups[]; + float rotateAngle = 0.0f; + float funcValue = -10.0f; + float resolution = 0.1f; + boolean texture = false; + boolean textureSwitch = false; + final Image diffuseMap = new Image("resources/cup_diffuseMap_1024.png"); + final Image bumpMap = new Image("resources/cup_normalMap_1024.png"); + + final static ToysVec3f v1 = new ToysVec3f(); + final static ToysVec3f v2 = new ToysVec3f(); + static void computeNormal(ToysVec3f pa, ToysVec3f pb, ToysVec3f pc, ToysVec3f normal) { + // compute Normal |(v1-v0)X(v2-v0)| + v1.sub(pb, pa); + v2.sub(pc, pa); + normal.cross(v1, v2); + normal.normalize(); + } + + final static float meshScale = 20; + final static float minX = -10; + final static float minY = -10; + final static float maxX = 10; + final static float maxY = 10; + + double getSinDivX(double x, double y) { + double r = Math.sqrt(x*x + y*y); + return funcValue * (r == 0 ? 1 : Math.sin(r) / r); + } + + void computeMesh(int subDivX, int subDivY, float scale) { + final int pointSize = 3; + int numDivX = subDivX + 1; + float[] points = triMesh.getPoints().toArray(null); + float[] normals = triMesh.getNormals().toArray(null); + int[] faces = triMesh.getFaces().toArray(null); + VertexFormat format = triMesh.getVertexFormat(); + int faceElementCount = format.getVertexIndexSize(); + + // Initial points and texCoords + for (int y = 0; y <= subDivY; y++) { + float dy = (float) y / subDivY; + double fy = (1 - dy) * minY + dy * maxY; +// System.err.println("dy = " + dy + ", fy = " + fy); + for (int x = 0; x <= subDivX; x++) { + float dx = (float) x / subDivX; + double fx = (1 - dx) * minX + dx * maxX; +// System.err.println("dx = " + dx + ", fx = " + fx); + int index = y * numDivX * pointSize + (x * pointSize); +// System.err.println("point index = " + index); + points[index] = (float) fx * scale; + points[index + 1] = (float) fy * scale; + points[index + 2] = (float) getSinDivX(fx, fy) * scale; +// System.err.println("points[" + (index + 2) + " = " + points[index + 2]); + } + } + triMesh.getPoints().set(0, points, 0, points.length); + + ToysVec3f[] triPoints = new ToysVec3f[3]; + triPoints[0] = new ToysVec3f(); + triPoints[1] = new ToysVec3f(); + triPoints[2] = new ToysVec3f(); + ToysVec3f normal = new ToysVec3f(); + for (int index = 0; index < faces.length; index += 3 * faceElementCount) { + int pIndex = faces[index] * 3; + triPoints[0].x = points[pIndex]; + triPoints[0].y = points[pIndex + 1]; + triPoints[0].z = points[pIndex + 2]; + pIndex = faces[index + faceElementCount] * 3; + triPoints[1].x = points[pIndex]; + triPoints[1].y = points[pIndex + 1]; + triPoints[1].z = points[pIndex + 2]; + pIndex = faces[index + (2 * faceElementCount)] * 3; + triPoints[2].x = points[pIndex]; + triPoints[2].y = points[pIndex + 1]; + triPoints[2].z = points[pIndex + 2]; +// System.err.println("point 0 = " + triPoints[0]); +// System.err.println("point 1 = " + triPoints[1]); +// System.err.println("point 2 = " + triPoints[2]); + + computeNormal(triPoints[0], triPoints[1], triPoints[2], normal); +// System.err.println(faces[index + 1] + ": normal = " + normal); + assert (faces[index + 1] == faces[index + faceElementCount + 1]) + && (faces[index + 1] == faces[index + (2 * faceElementCount) + 1]); + int nIndex = faces[index + 1] * 3; + normals[nIndex] = normal.x; + normals[nIndex + 1] = normal.y; + normals[nIndex + 2] = normal.z; + } + + triMesh.getNormals().set(0, normals, 0, normals.length); + + } + + TriangleMesh buildTriangleMesh(int subDivX, int subDivY, + float scale) { + +// System.err.println("subDivX = " + subDivX + ", subDivY = " + subDivY); + + final int pointSize = 3; + final int normalSize = 3; + final int texCoordSize = 2; + final int colorSize = 4; + final int faceElementCount = vertexFormat.getVertexIndexSize(); + final int faceSize = 3 * faceElementCount; + int numDivX = subDivX + 1; + int numVerts = (subDivY + 1) * numDivX; + float points[] = new float[numVerts * pointSize]; + float texCoords[] = new float[numVerts * texCoordSize]; + float colors[] = new float[numVerts * colorSize]; + int faceCount = subDivX * subDivY * 2; + float normals[] = new float[faceCount * normalSize]; + int faces[] = new int[ faceCount * faceSize]; + + // Create points and texCoords + for (int y = 0; y <= subDivY; y++) { + float dy = (float) y / subDivY; + double fy = (1 - dy) * minY + dy * maxY; +// System.err.println("dy = " + dy + ", fy = " + fy); + for (int x = 0; x <= subDivX; x++) { + float dx = (float) x / subDivX; + double fx = (1 - dx) * minX + dx * maxX; +// System.err.println("dx = " + dx + ", fx = " + fx); + int index = y * numDivX * pointSize + (x * pointSize); +// System.err.println("point index = " + index); + points[index] = (float) fx * scale; + points[index + 1] = (float) fy * scale; + points[index + 2] = (float) getSinDivX(fx, fy) * scale; + index = y * numDivX * texCoordSize + (x * texCoordSize); +// System.err.println("texCoord index = " + index); + texCoords[index] = dx; + texCoords[index + 1] = dy; + index = y * numDivX * colorSize + (x * colorSize); + colors[index] = dx; + colors[index + 1] = 1F - dy; + colors[index + 2] = Math.max(0F, dy - dx); + colors[index + 3] = 1F; + } + } + + // Initial faces and normals + int normalCount = 0; + ToysVec3f[] triPoints = new ToysVec3f[3]; + triPoints[0] = new ToysVec3f(); + triPoints[1] = new ToysVec3f(); + triPoints[2] = new ToysVec3f(); + ToysVec3f normal = new ToysVec3f(); + for (int y = 0; y < subDivY; y++) { + for (int x = 0; x < subDivX; x++) { + int p00 = y * numDivX + x; + int p01 = p00 + 1; + int p10 = p00 + numDivX; + int p11 = p10 + 1; + int tc00 = y * numDivX + x; + int tc01 = tc00 + 1; + int tc10 = tc00 + numDivX; + int tc11 = tc10 + 1; + int vc00 = y * numDivX + x; + int vc01 = vc00 + 1; + int vc10 = vc00 + numDivX; + int vc11 = vc10 + 1; + + int ii = p00 * 3; + triPoints[0].x = points[ii]; + triPoints[0].y = points[ii + 1]; + triPoints[0].z = points[ii + 2]; + ii = p10 * 3; + triPoints[1].x = points[ii]; + triPoints[1].y = points[ii + 1]; + triPoints[1].z = points[ii + 2]; + ii = p11 * 3; + triPoints[2].x = points[ii]; + triPoints[2].y = points[ii + 1]; + triPoints[2].z = points[ii + 2]; + computeNormal(triPoints[0], triPoints[1], triPoints[2], normal); +// System.err.println("normal = " + normal); + int normalIndex = normalCount * normalSize; + normals[normalIndex] = normal.x; //nx + normals[normalIndex + 1] = normal.y; //ny + normals[normalIndex + 2] = normal.z; //nz + + int index = (y * subDivX * faceSize + (x * faceSize)) * 2; +// System.err.println("face 0 index = " + index); + + if (vertexFormat.getPointIndexOffset() >= 0) + faces[index++] = p00; + if (vertexFormat.getNormalIndexOffset() >= 0) + faces[index++] = normalCount; + if (vertexFormat.getTexCoordIndexOffset() >= 0) + faces[index++] = tc00; + if (vertexFormat.getColorIndexOffset() >= 0) + faces[index++] = vc00; + if (vertexFormat.getPointIndexOffset() >= 0) + faces[index++] = p10; + if (vertexFormat.getNormalIndexOffset() >= 0) + faces[index++] = normalCount; + if (vertexFormat.getTexCoordIndexOffset() >= 0) + faces[index++] = tc10; + if (vertexFormat.getColorIndexOffset() >= 0) + faces[index++] = vc10; + if (vertexFormat.getPointIndexOffset() >= 0) + faces[index++] = p11; + if (vertexFormat.getNormalIndexOffset() >= 0) + faces[index++] = normalCount++; + if (vertexFormat.getTexCoordIndexOffset() >= 0) + faces[index++] = tc11; + if (vertexFormat.getColorIndexOffset() >= 0) + faces[index++] = vc11; + + ii = p11 * 3; + triPoints[0].x = points[ii]; + triPoints[0].y = points[ii + 1]; + triPoints[0].z = points[ii + 2]; + ii = p01 * 3; + triPoints[1].x = points[ii]; + triPoints[1].y = points[ii + 1]; + triPoints[1].z = points[ii + 2]; + ii = p00 * 3; + triPoints[2].x = points[ii]; + triPoints[2].y = points[ii + 1]; + triPoints[2].z = points[ii + 2]; + computeNormal(triPoints[0], triPoints[1], triPoints[2], normal); +// System.err.println("normal = " + normal); + normalIndex = normalCount * normalSize; + normals[normalIndex] = normal.x; //nx + normals[normalIndex + 1] = normal.y; //ny + normals[normalIndex + 2] = normal.z; //nz + +// System.err.println("face 1 index = " + index); + if (vertexFormat.getPointIndexOffset() >= 0) + faces[index++] = p11; + if (vertexFormat.getNormalIndexOffset() >= 0) + faces[index++] = normalCount; + if (vertexFormat.getTexCoordIndexOffset() >= 0) + faces[index++] = tc11; + if (vertexFormat.getColorIndexOffset() >= 0) + faces[index++] = vc11; + if (vertexFormat.getPointIndexOffset() >= 0) + faces[index++] = p01; + if (vertexFormat.getNormalIndexOffset() >= 0) + faces[index++] = normalCount; + if (vertexFormat.getTexCoordIndexOffset() >= 0) + faces[index++] = tc01; + if (vertexFormat.getColorIndexOffset() >= 0) + faces[index++] = vc01; + if (vertexFormat.getPointIndexOffset() >= 0) + faces[index++] = p00; + if (vertexFormat.getNormalIndexOffset() >= 0) + faces[index++] = normalCount++; + if (vertexFormat.getTexCoordIndexOffset() >= 0) + faces[index++] = tc00; + if (vertexFormat.getColorIndexOffset() >= 0) + faces[index++] = vc00; + } + } +// for(int i = 0; i < points.length; i++) { +// System.err.println("points[" + i + "] = " + points[i]); +// } +// for(int i = 0; i < texCoords.length; i++) { +// System.err.println("texCoords[" + i + "] = " + texCoords[i]); +// } +// for(int i = 0; i < normals.length; i++) { +// System.err.println("normals[" + i + "] = " + normals[i]); +// } +// for(int i = 0; i < faces.length; i++) { +// System.err.println("faces[" + i + "] = " + faces[i]); +// } + + TriangleMesh triangleMesh = new TriangleMesh(vertexFormat); + triangleMesh.getPoints().setAll(points); + triangleMesh.getNormals().setAll(normals); + triangleMesh.getTexCoords().setAll(texCoords); + triangleMesh.getColors().setAll(colors); + triangleMesh.getFaces().setAll(faces); + + return triangleMesh; + } + + private Scene buildScene(int width, int height, boolean depthBuffer) { + + triMesh = buildTriangleMesh(divX, divY, meshScale); + smoothGroups = new int[divX * divY * 2]; + material = new PhongMaterial(); + material.setDiffuseColor(Color.LIGHTGRAY); + material.setSpecularColor(Color.WHITE); + material.setSpecularPower(64); + meshView = new MeshView(triMesh); + meshView.setMaterial(material); + + //Set Wireframe mode + meshView.setDrawMode(DrawMode.FILL); + meshView.setCullFace(CullFace.BACK); + + final Group grp1 = new Group(meshView); + grp1.setRotate(0); + grp1.setRotationAxis(Rotate.X_AXIS); + Group grp2 = new Group(grp1); + grp2.setRotate(-30); + grp2.setRotationAxis(Rotate.X_AXIS); + Group grp3 = new Group(grp2); + grp3.setTranslateX(400); + grp3.setTranslateY(400); + grp3.setTranslateZ(10); + + pointLight = new PointLight(Color.ANTIQUEWHITE); + pointLight.setTranslateX(300); + pointLight.setTranslateY(-50); + pointLight.setTranslateZ(-1000); + + root = new Group(grp3, pointLight); + Scene scene = new Scene(root, width, height, depthBuffer); + scene.setOnKeyTyped(new EventHandler() { + @Override + public void handle(KeyEvent e) { + switch (e.getCharacter()) { + case "[": + funcValue -= resolution; + if (funcValue < -20.0f) { + funcValue = -20.0f; + } + computeMesh(divX, divY, meshScale); + break; + case "]": + funcValue += resolution; + if (funcValue > 20.0f) { + funcValue = 20.0f; + } + computeMesh(divX, divY, meshScale); + break; + case "p": + funcValue = -10; + computeMesh(divX, divY, meshScale); + break; + case "i": + System.err.print("i "); + if (!textureSwitch) { + texture = texture ? false : true; + } else { + textureSwitch = false; + } + if (texture) { + material.setDiffuseMap(diffuseMap); + material.setBumpMap(bumpMap); + material.setDiffuseColor(Color.WHITE); + } else { + material.setDiffuseMap(null); + material.setBumpMap(null); + material.setDiffuseColor(Color.LIGHTGRAY); + } + break; + case "s": + smooth = !smooth; + if (smooth) { + Arrays.fill(smoothGroups, 0); + } else { + for (int i = 0; i < smoothGroups.length; i++) { + smoothGroups[i] = i % 32; +// System.err.println("XXX smoothGroups[" + i + "] = " + smoothGroups[i]); + } + } + triMesh.getFaceSmoothingGroups().setAll(smoothGroups); + break; + case "k": + System.err.print("k "); + if ((texture) || (!textureSwitch)) { + material.setDiffuseMap(diffuseMap); + material.setBumpMap(null); + material.setDiffuseColor(Color.WHITE); + texture = true; + textureSwitch = true; + } else { + material.setDiffuseMap(null); + material.setBumpMap(null); + material.setDiffuseColor(Color.LIGHTGRAY); + } + break; + case "u": + System.err.print("u "); + if (texture) { + material.setDiffuseMap(null); + material.setBumpMap(bumpMap); + material.setDiffuseColor(Color.LIGHTGRAY); + textureSwitch = true; + } else { + material.setDiffuseMap(null); + material.setBumpMap(null); + material.setDiffuseColor(Color.LIGHTGRAY); + } + break; + case "l": + System.err.print("l "); + boolean wireframe = meshView.getDrawMode() == DrawMode.LINE; + meshView.setDrawMode(wireframe ? DrawMode.FILL : DrawMode.LINE); + break; + case "<": + grp1.setRotate(rotateAngle -= (resolution * 5)); + break; + case ">": + grp1.setRotate(rotateAngle += (resolution * 5)); + break; + case "X": + grp1.setRotationAxis(Rotate.X_AXIS); + break; + case "Y": + grp1.setRotationAxis(Rotate.Y_AXIS); + break; + case "Z": + grp1.setRotationAxis(Rotate.Z_AXIS); + break; + case "P": + rotateAngle = 0; + grp1.setRotate(rotateAngle); + case "1": + System.err.print("1 "); + divX = 5; + divY = 5; + smooth = true; + smoothGroups = new int[divX * divY * 2]; + rotateAngle = 0.0f; + funcValue = 0.0f; + triMesh = buildTriangleMesh(divX, divY, meshScale); + meshView.setMesh(triMesh); + break; + case "2": + System.err.print("2 "); + divX = 70; + divY = 70; + smooth = true; + smoothGroups = new int[divX * divY * 2]; + rotateAngle = 0.0f; + funcValue = 0.0f; + triMesh = buildTriangleMesh(divX, divY, meshScale); + meshView.setMesh(triMesh); + break; + case "f": + System.err.print("f "); + if (vertexFormat == VertexFormat.POINT_NORMAL_TEXCOORD_COLOR) { + vertexFormat = VertexFormat.POINT_TEXCOORD; + } else if (vertexFormat == VertexFormat.POINT_TEXCOORD) { + vertexFormat = VertexFormat.POINT_NORMAL_TEXCOORD; + } else if (vertexFormat == VertexFormat.POINT_NORMAL_TEXCOORD) { + vertexFormat = VertexFormat.POINT_TEXCOORD_COLOR; + } else { + vertexFormat = VertexFormat.POINT_NORMAL_TEXCOORD_COLOR; + } + + triMesh = buildTriangleMesh(divX, divY, meshScale); + meshView.setMesh(triMesh); + break; + case " ": + root.getChildren().add(new Button("Button")); + break; + + } + } + }); + return scene; + } + + private PerspectiveCamera addCamera(Scene scene) { + PerspectiveCamera perspectiveCamera = new PerspectiveCamera(); + scene.setCamera(perspectiveCamera); + return perspectiveCamera; + } + + @Override + public void start(Stage primaryStage) { + Scene scene = buildScene(800, 800, true); + scene.setFill(Color.rgb(10, 10, 40)); + addCamera(scene); + primaryStage.setTitle("Dynamic MeshViewer"); + primaryStage.setScene(scene); + primaryStage.show(); + } + + public static void main(String[] args) { + launch(args); + } +} diff --git a/modules/javafx.fxml/src/main/java/com/sun/javafx/fxml/builder/TriangleMeshBuilder.java b/modules/javafx.fxml/src/main/java/com/sun/javafx/fxml/builder/TriangleMeshBuilder.java index 423c9df4c62..527c0373516 100644 --- a/modules/javafx.fxml/src/main/java/com/sun/javafx/fxml/builder/TriangleMeshBuilder.java +++ b/modules/javafx.fxml/src/main/java/com/sun/javafx/fxml/builder/TriangleMeshBuilder.java @@ -41,6 +41,7 @@ public class TriangleMeshBuilder extends TreeMap implements Buil private float[] points; private float[] texCoords; private float[] normals; + private float[] colors; private int[] faces; private int[] faceSmoothingGroups; private VertexFormat vertexFormat; @@ -63,6 +64,9 @@ public TriangleMesh build() { if (normals != null) { mesh.getNormals().setAll(normals); } + if (colors != null) { + mesh.getColors().setAll(colors); + } if (vertexFormat != null) { mesh.setVertexFormat(vertexFormat); } @@ -102,13 +106,23 @@ public Object put(String key, Object value) { for (int i = 0; i < split.length; ++i) { normals[i] = Float.parseFloat(split[i]); } + } else if ("colors".equalsIgnoreCase(key)) { + String[] split = ((String) value).split(VALUE_SEPARATOR_REGEX); + colors = new float[split.length]; + for (int i = 0; i < split.length; ++i) { + colors[i] = Float.parseFloat(split[i]); + } } else if ("vertexformat".equalsIgnoreCase(key)) { if (value instanceof VertexFormat) { vertexFormat = (VertexFormat) value; } else if ("point_texcoord".equalsIgnoreCase((String)value)) { vertexFormat = VertexFormat.POINT_TEXCOORD; + } else if ("point_texcoord_color".equalsIgnoreCase((String)value)) { + vertexFormat = VertexFormat.POINT_TEXCOORD_COLOR; } else if ("point_normal_texcoord".equalsIgnoreCase((String)value)) { vertexFormat = VertexFormat.POINT_NORMAL_TEXCOORD; + } else if ("point_normal_texcoord_color".equalsIgnoreCase((String)value)) { + vertexFormat = VertexFormat.POINT_NORMAL_TEXCOORD_COLOR; } } diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/geom/Vec4f.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/geom/Vec4f.java index 120c991d103..e7391e4949f 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/geom/Vec4f.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/geom/Vec4f.java @@ -73,6 +73,13 @@ public void set(Vec4f v) { this.w = v.w; } + public void set(float x, float y, float z, float w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + @Override public String toString() { return "(" + x + ", " + y + ", " + z + ", " + w + ")"; diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/sg/prism/NGTriangleMesh.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/sg/prism/NGTriangleMesh.java index 3039bb815c9..90c37c6b110 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/sg/prism/NGTriangleMesh.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/sg/prism/NGTriangleMesh.java @@ -29,6 +29,7 @@ import com.sun.javafx.collections.IntegerArraySyncer; import com.sun.prism.Mesh; import com.sun.prism.ResourceFactory; +import javafx.scene.shape.VertexFormat; /** * TODO: 3D - Need documentation @@ -36,7 +37,9 @@ public class NGTriangleMesh { private boolean meshDirty = true; private Mesh mesh; + private VertexFormat vertexFormat; private boolean userDefinedNormals = false; + private boolean userDefinedColors = false; // points is an array of x,y,z interleaved private float[] points; @@ -50,6 +53,10 @@ public class NGTriangleMesh { private float[] texCoords; private int[] texCoordsFromAndLengthIndices = new int[2]; + // colors is an array of r,g,b,a interleaved + private float[] colors; + private int[] colorsFromAndLengthIndices = new int[2]; + // faces is an array of v1,v2,v3 interleaved (where v = {point, texCoord}) private int[] faces; private int[] facesFromAndLengthIndices = new int[2]; @@ -75,14 +82,17 @@ Mesh createMesh(ResourceFactory rf) { boolean validate() { if (points == null || texCoords == null || faces == null || faceSmoothingGroups == null - || (userDefinedNormals && (normals == null))) { + || (userDefinedNormals && (normals == null)) || (userDefinedColors && (colors == null)) + || vertexFormat == null) { return false; } if (meshDirty) { - if (!mesh.buildGeometry(userDefinedNormals, + if (!mesh.buildGeometry( + vertexFormat, userDefinedNormals, userDefinedColors, points, pointsFromAndLengthIndices, normals, normalsFromAndLengthIndices, texCoords, texCoordsFromAndLengthIndices, + colors, colorsFromAndLengthIndices, faces, facesFromAndLengthIndices, faceSmoothingGroups, faceSmoothingGroupsFromAndLengthIndices)) { throw new RuntimeException("NGTriangleMesh: buildGeometry failed"); @@ -116,6 +126,14 @@ void setTexCoordsByRef(float[] texCoords) { this.texCoords = texCoords; } + // Note: This method is intentionally made package scope for security + // reason. It is created for internal use only. + // Do not make it a public method without careful consideration. + void setColorsByRef(float[] colors) { + meshDirty = true; + this.colors = colors; + } + // Note: This method is intentionally made package scope for security // reason. It is created for internal use only. // Do not make it a public method without careful consideration. @@ -132,6 +150,10 @@ void setFaceSmoothingGroupsByRef(int[] faceSmoothingGroups) { this.faceSmoothingGroups = faceSmoothingGroups; } + public void setVertexFormat(VertexFormat vertexFormat) { + this.vertexFormat = vertexFormat; + } + public void setUserDefinedNormals(boolean userDefinedNormals) { this.userDefinedNormals = userDefinedNormals; } @@ -140,6 +162,14 @@ public boolean isUserDefinedNormals() { return userDefinedNormals; } + public void setUserDefinedColors(boolean userDefinedColors) { + this.userDefinedColors = userDefinedColors; + } + + public boolean isUserDefinedColors() { + return userDefinedColors; + } + public void syncPoints(FloatArraySyncer array) { meshDirty = true; points = array != null ? array.syncTo(points, pointsFromAndLengthIndices) : null; @@ -155,6 +185,11 @@ public void syncTexCoords(FloatArraySyncer array) { texCoords = array != null ? array.syncTo(texCoords, texCoordsFromAndLengthIndices) : null; } + public void syncColors(FloatArraySyncer array) { + meshDirty = true; + colors = array != null ? array.syncTo(colors, colorsFromAndLengthIndices) : null; + } + public void syncFaces(IntegerArraySyncer array) { meshDirty = true; faces = array != null ? array.syncTo(faces, facesFromAndLengthIndices) : null; @@ -165,6 +200,10 @@ public void syncFaceSmoothingGroups(IntegerArraySyncer array) { faceSmoothingGroups = array != null ? array.syncTo(faceSmoothingGroups, faceSmoothingGroupsFromAndLengthIndices) : null; } + // NOTE: This method is used for unit test purpose only. + VertexFormat test_getVertexFormat() { + return this.vertexFormat; + } // NOTE: This method is used for unit test purpose only. int[] test_getFaceSmoothingGroups() { return this.faceSmoothingGroups; @@ -186,6 +225,10 @@ float[] test_getTexCoords() { return this.texCoords; } // NOTE: This method is used for unit test purpose only. + float[] test_getColors() { + return this.colors; + } + // NOTE: This method is used for unit test purpose only. Mesh test_getMesh() { return this.mesh; } diff --git a/modules/javafx.graphics/src/main/java/com/sun/prism/Mesh.java b/modules/javafx.graphics/src/main/java/com/sun/prism/Mesh.java index 58ca156bd07..677f1e0fb22 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/prism/Mesh.java +++ b/modules/javafx.graphics/src/main/java/com/sun/prism/Mesh.java @@ -25,6 +25,8 @@ package com.sun.prism; +import javafx.scene.shape.VertexFormat; + /** * TODO: 3D - Need documentation * This class represents mesh geometry data object @@ -32,10 +34,13 @@ public interface Mesh extends GraphicsResource { // This method will fail if and only if ALL faces are wrong. // A wrong face is one with zero area or with any index out of range - public boolean buildGeometry(boolean userDefinedNormals, + public boolean buildGeometry( + VertexFormat vertexFormat, + boolean userDefinedNormals, boolean userDefinedColors, float points[], int[] pointsFromAndLengthIndices, float normals[], int[] normalsFromAndLengthIndices, float texCoords[], int[] texCoordsFromAndLengthIndices, + float colors[], int[] colorsFromAndLengthIndices, int faces[], int[] facesFromAndLengthIndices, int faceSmoothingGroups[], int[] faceSmoothingGroupsFromAndLengthIndices); diff --git a/modules/javafx.graphics/src/main/java/com/sun/prism/es2/ES2PhongShader.java b/modules/javafx.graphics/src/main/java/com/sun/prism/es2/ES2PhongShader.java index 43d86534b2a..63c90446a08 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/prism/es2/ES2PhongShader.java +++ b/modules/javafx.graphics/src/main/java/com/sun/prism/es2/ES2PhongShader.java @@ -167,6 +167,7 @@ static ES2Shader getShader(ES2MeshView meshView, ES2Context context) { attributes.put("pos", 0); attributes.put("texCoords", 1); attributes.put("tangent", 2); + attributes.put("color", 3); Map samplers = new HashMap<>(); samplers.put("diffuseTexture", 0); diff --git a/modules/javafx.graphics/src/main/java/com/sun/prism/impl/BaseMesh.java b/modules/javafx.graphics/src/main/java/com/sun/prism/impl/BaseMesh.java index 9513f17f4e4..91e21818fc2 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/prism/impl/BaseMesh.java +++ b/modules/javafx.graphics/src/main/java/com/sun/prism/impl/BaseMesh.java @@ -28,6 +28,7 @@ import com.sun.javafx.geom.Quat4f; import com.sun.javafx.geom.Vec2f; import com.sun.javafx.geom.Vec3f; +import com.sun.javafx.geom.Vec4f; import com.sun.prism.Mesh; import java.util.Arrays; import java.util.HashMap; @@ -44,6 +45,7 @@ public abstract class BaseMesh extends BaseGraphicsResource implements Mesh { private int nFaces; private float[] pos; private float[] uv; + private float[] colors; private int[] faces; private int[] smoothing; private boolean allSameSmoothing; @@ -52,21 +54,28 @@ public abstract class BaseMesh extends BaseGraphicsResource implements Mesh { protected static final int POINT_SIZE = 3; protected static final int NORMAL_SIZE = 3; protected static final int TEXCOORD_SIZE = 2; + protected static final int COLOR_SIZE = 4; protected static final int POINT_SIZE_VB = 3; protected static final int TEXCOORD_SIZE_VB = 2; protected static final int NORMAL_SIZE_VB = 4; - //point (3 floats), texcoord (2 floats) and normal (as in 4 floats) - protected static final int VERTEX_SIZE_VB = 9; + protected static final int COLOR_SIZE_VB = 4; + //point (3 floats), texcoord (2 floats), normal (as in 4 floats), and color (4 floats) + protected static final int VERTEX_SIZE_VB = POINT_SIZE_VB + TEXCOORD_SIZE_VB + + NORMAL_SIZE_VB + COLOR_SIZE_VB; + private static final int VERTICES_PER_FACE = 3; + + // The following are used for obtaining face data from faces which do not contain normal data. // Data members container for a single face // Vec3i pVerts; // Vec3i tVerts; // int smGroup; public static enum FaceMembers { - POINT0, TEXCOORD0, POINT1, TEXCOORD1, POINT2, TEXCOORD2, SMOOTHING_GROUP + POINT0, TEXCOORD0, COLOR0, POINT1, TEXCOORD1, COLOR1, POINT2, TEXCOORD2, COLOR2, SMOOTHING_GROUP } - public static final int FACE_MEMBERS_SIZE = 7; + public static final int FACE_MEMBERS_SIZE = FaceMembers.values().length; + public static final int FACE_MEMBERS_COPIED_SIZE = FACE_MEMBERS_SIZE - 1; protected BaseMesh(Disposer.Record disposerRecord) { super(disposerRecord); @@ -91,6 +100,7 @@ public abstract boolean buildNativeGeometry(float[] vertexBuffer, private HashMap point2vbMap; private HashMap normal2vbMap; private HashMap texCoord2vbMap; + private HashMap color2vbMap; private void convertNormalsToQuats(MeshTempState instance, int numberOfVertices, float[] normals, float[] tangents, float[] bitangents, @@ -132,8 +142,8 @@ private void convertNormalsToQuats(MeshTempState instance, int numberOfVertices, } // Build PointNormalTexCoordGeometry - private boolean doBuildPNTGeometry(float[] points, float[] normals, - float[] texCoords, int[] faces) { + private boolean doBuildPNTCGeometry(VertexFormat vertexFormat, float[] points, float[] normals, + float[] texCoords, float[] colors, int[] faces) { if (point2vbMap == null) { point2vbMap = new HashMap(); @@ -150,12 +160,19 @@ private boolean doBuildPNTGeometry(float[] points, float[] normals, } else { texCoord2vbMap.clear(); } + if (color2vbMap == null) { + color2vbMap = new HashMap(); + } else { + color2vbMap.clear(); + } - int vertexIndexSize = VertexFormat.POINT_NORMAL_TEXCOORD.getVertexIndexSize(); - int faceIndexSize = vertexIndexSize * 3; - int pointIndexOffset = VertexFormat.POINT_NORMAL_TEXCOORD.getPointIndexOffset(); - int normalIndexOffset = VertexFormat.POINT_NORMAL_TEXCOORD.getNormalIndexOffset(); - int texCoordIndexOffset = VertexFormat.POINT_NORMAL_TEXCOORD.getTexCoordIndexOffset(); + int vertexIndexSize = vertexFormat.getVertexIndexSize(); + int faceIndexSize = vertexIndexSize * VERTICES_PER_FACE; + int pointIndexOffset = vertexFormat.getPointIndexOffset(); + int normalIndexOffset = vertexFormat.getNormalIndexOffset(); + int texCoordIndexOffset = vertexFormat.getTexCoordIndexOffset(); + int colorIndexOffset = vertexFormat.getColorIndexOffset(); + boolean hasColors = vertexFormat.getColorIndexOffset() >= 0; int numPoints = points.length / POINT_SIZE; int numNormals = normals.length / NORMAL_SIZE; @@ -167,17 +184,18 @@ private boolean doBuildPNTGeometry(float[] points, float[] normals, BaseMesh.MeshGeomComp2VB mp2vb; BaseMesh.MeshGeomComp2VB mn2vb; BaseMesh.MeshGeomComp2VB mt2vb; + BaseMesh.MeshGeomComp2VB mc2vb; // Allocate an initial size, may grow as we process the faces array. cachedNormals = new float[numPoints * NORMAL_SIZE]; cachedTangents = new float[numPoints * NORMAL_SIZE]; cachedBitangents = new float[numPoints * NORMAL_SIZE]; vertexBuffer = new float[numPoints * VERTEX_SIZE_VB]; - indexBuffer = new int[numFaces * 3]; + indexBuffer = new int[numFaces * VERTICES_PER_FACE]; int ibCount = 0; int vbCount = 0; MeshTempState instance = MeshTempState.getInstance(); - for (int i = 0; i < 3; i++) { + for (int i = 0; i < VERTICES_PER_FACE; i++) { if (instance.triPoints[i] == null) { instance.triPoints[i] = new Vec3f(); } @@ -188,18 +206,19 @@ private boolean doBuildPNTGeometry(float[] points, float[] normals, for (int faceCount = 0; faceCount < numFaces; faceCount++) { int faceIndex = faceCount * faceIndexSize; - for (int i = 0; i < 3; i++) { + for (int i = 0; i < VERTICES_PER_FACE; i++) { int vertexIndex = faceIndex + (i * vertexIndexSize); int pointIndex = vertexIndex + pointIndexOffset; int normalIndex = vertexIndex + normalIndexOffset; int texCoordIndex = vertexIndex + texCoordIndexOffset; + int colorIndex = vertexIndex + colorIndexOffset; mf2vb = vbCount / VERTEX_SIZE_VB; if (vertexBuffer.length <= vbCount) { int numVertices = vbCount / VERTEX_SIZE_VB; // Increment by 1/8th of numVertices or 6 (by 2 triangles) which ever is greater - final int newNumVertices = numVertices + Math.max((numVertices >> 3), 6); + final int newNumVertices = numVertices + Math.max((numVertices >> 3), 2 * VERTICES_PER_FACE); float[] temp = new float[newNumVertices * VERTEX_SIZE_VB]; System.arraycopy(vertexBuffer, 0, temp, 0, vertexBuffer.length); vertexBuffer = temp; @@ -217,6 +236,7 @@ private boolean doBuildPNTGeometry(float[] points, float[] normals, int pointOffset = faces[pointIndex] * POINT_SIZE; int normalOffset = faces[normalIndex] * NORMAL_SIZE; int texCoordOffset = faces[texCoordIndex] * TEXCOORD_SIZE; + int colorOffset = hasColors ? faces[colorIndex] * COLOR_SIZE : -1; // Save the vertex of triangle instance.triPointIndex[i] = pointOffset; @@ -226,8 +246,11 @@ private boolean doBuildPNTGeometry(float[] points, float[] normals, vertexBuffer[vbCount] = points[pointOffset]; vertexBuffer[vbCount + 1] = points[pointOffset + 1]; vertexBuffer[vbCount + 2] = points[pointOffset + 2]; - vertexBuffer[vbCount + 3] = texCoords[texCoordOffset]; - vertexBuffer[vbCount + 4] = texCoords[texCoordOffset + 1]; + vertexBuffer[vbCount + POINT_SIZE_VB] = texCoords[texCoordOffset]; + vertexBuffer[vbCount + POINT_SIZE_VB + 1] = texCoords[texCoordOffset + 1]; + writeColorsToBuffer(colors, colorOffset, vertexBuffer, + vbCount + POINT_SIZE_VB + TEXCOORD_SIZE_VB + NORMAL_SIZE_VB); + // Store the normal in the cachedNormals array int index = instance.triVerts[i] * NORMAL_SIZE; cachedNormals[index] = normals[normalOffset]; @@ -266,13 +289,25 @@ private boolean doBuildPNTGeometry(float[] points, float[] normals, mt2vb.addLoc(mf2vb); } + if (colorOffset >= 0) { + mc2vb = color2vbMap.get(colorOffset); + if (mc2vb == null) { + // create + mc2vb = new MeshGeomComp2VB(colorOffset, mf2vb); + color2vbMap.put(colorOffset, mc2vb); + } else { + // addLoc + mc2vb.addLoc(mf2vb); + } + } + // Construct IndexBuffer indexBuffer[ibCount++] = mf2vb; } // This is the best time to compute the tangent and bitangent for each // of the vertex. Go thro. the 3 vertices of a triangle - for (int i = 0; i < 3; i++) { + for (int i = 0; i < VERTICES_PER_FACE; i++) { instance.triPoints[i].x = points[instance.triPointIndex[i]]; instance.triPoints[i].y = points[instance.triPointIndex[i] + 1]; instance.triPoints[i].z = points[instance.triPointIndex[i] + 2]; @@ -285,7 +320,7 @@ private boolean doBuildPNTGeometry(float[] points, float[] normals, instance.triTexCoords[1], instance.triTexCoords[2], instance.triNormals); - for (int i = 0; i < 3; i++) { + for (int i = 0; i < VERTICES_PER_FACE; i++) { int index = instance.triVerts[i] * NORMAL_SIZE; cachedTangents[index] = instance.triNormals[1].x; cachedTangents[index + 1] = instance.triNormals[1].y; @@ -302,7 +337,7 @@ private boolean doBuildPNTGeometry(float[] points, float[] normals, convertNormalsToQuats(instance, numberOfVertices, cachedNormals, cachedTangents, cachedBitangents, vertexBuffer, null); - indexBufferSize = numFaces * 3; + indexBufferSize = numFaces * VERTICES_PER_FACE; if (numberOfVertices > 0x10000) { // > 64K return buildNativeGeometry(vertexBuffer, @@ -325,9 +360,11 @@ private boolean doBuildPNTGeometry(float[] points, float[] normals, } // Update PointNormalTexCoordGeometry - private boolean updatePNTGeometry(float[] points, int[] pointsFromAndLengthIndices, + private boolean updatePNTCGeometry(VertexFormat vertexFormat, + float[] points, int[] pointsFromAndLengthIndices, float[] normals, int[] normalsFromAndLengthIndices, - float[] texCoords, int[] texCoordsFromAndLengthIndices) { + float[] texCoords, int[] texCoordsFromAndLengthIndices, + float[] colors, int[] colorsFromAndLengthIndices) { if (dirtyVertices == null) { // Create a dirty array of size equal to number of vertices in vertexBuffer. @@ -413,7 +450,6 @@ private boolean updatePNTGeometry(float[] points, int[] pointsFromAndLengthIndic numNormals++; } if (numNormals > 0) { - MeshTempState instance = MeshTempState.getInstance(); for (int i = 0; i < numNormals; i++) { int normalOffset = (startNormal + i) * NORMAL_SIZE; MeshGeomComp2VB mn2vb = normal2vbMap.get(normalOffset); @@ -443,9 +479,44 @@ private boolean updatePNTGeometry(float[] points, int[] pointsFromAndLengthIndic } } + // Find out the list of modified colors. + if (colorsFromAndLengthIndices != null) { + int startVtxColor = colorsFromAndLengthIndices[0] / COLOR_SIZE; + int numVtxColors = (colorsFromAndLengthIndices[1] / COLOR_SIZE); + if ((colorsFromAndLengthIndices[1] % COLOR_SIZE) > 0) { + numVtxColors++; + } + if (numVtxColors > 0) { + for (int i = 0; i < numVtxColors; i++) { + int vtxColorOffset = (startVtxColor + i) * COLOR_SIZE; + MeshGeomComp2VB mc2vb = color2vbMap.get(vtxColorOffset); + assert mc2vb != null; + // mc2vb shouldn't be null. We can't have a color referred by + // the faces array that isn't in the vertexBuffer. + if (mc2vb != null) { + int[] locs = mc2vb.getLocs(); + int validLocs = mc2vb.getValidLocs(); + if (locs != null) { + for (int j = 0; j < validLocs; j++) { + int vbIndex = + (locs[j] * VERTEX_SIZE_VB) + POINT_SIZE_VB + TEXCOORD_SIZE_VB + NORMAL_SIZE_VB; + writeColorsToBuffer(colors, vtxColorOffset, vertexBuffer, vbIndex); + dirtyVertices[locs[j]] = true; + } + } else { + int loc = mc2vb.getLoc(); + int vbIndex = (loc * VERTEX_SIZE_VB) + POINT_SIZE_VB + TEXCOORD_SIZE_VB + NORMAL_SIZE_VB; + writeColorsToBuffer(colors, vtxColorOffset, vertexBuffer, vbIndex); + dirtyVertices[loc] = true; + } + } + } + } + } + // Prepare process all dirty vertices MeshTempState instance = MeshTempState.getInstance(); - for (int i = 0; i < 3; i++) { + for (int i = 0; i < VERTICES_PER_FACE; i++) { if (instance.triPoints[i] == null) { instance.triPoints[i] = new Vec3f(); } @@ -454,12 +525,12 @@ private boolean updatePNTGeometry(float[] points, int[] pointsFromAndLengthIndic } } // Every 3 vertices form a triangle - for (int j = 0; j < numberOfVertices; j += 3) { + for (int j = 0; j < numberOfVertices; j += VERTICES_PER_FACE) { // Only process the triangle that has one of more dirty vertices if (dirtyVertices[j] || dirtyVertices[j+1] || dirtyVertices[j+2]) { int vbIndex = j * VERTEX_SIZE_VB; // Go thro. the 3 vertices of a triangle - for (int i = 0; i < 3; i++) { + for (int i = 0; i < VERTICES_PER_FACE; i++) { instance.triPoints[i].x = vertexBuffer[vbIndex]; instance.triPoints[i].y = vertexBuffer[vbIndex + 1]; instance.triPoints[i].z = vertexBuffer[vbIndex + 2]; @@ -474,7 +545,7 @@ private boolean updatePNTGeometry(float[] points, int[] pointsFromAndLengthIndic instance.triNormals); int index = j * NORMAL_SIZE; - for (int i = 0; i < 3; i++) { + for (int i = 0; i < VERTICES_PER_FACE; i++) { cachedTangents[index] = instance.triNormals[1].x; cachedTangents[index + 1] = instance.triNormals[1].y; cachedTangents[index + 2] = instance.triNormals[1].z; @@ -501,24 +572,72 @@ private boolean updatePNTGeometry(float[] points, int[] pointsFromAndLengthIndic } @Override - public boolean buildGeometry(boolean userDefinedNormals, + public boolean buildGeometry(VertexFormat vertexFormat, + boolean userDefinedNormals, boolean userDefinedColors, float[] points, int[] pointsFromAndLengthIndices, float[] normals, int[] normalsFromAndLengthIndices, float[] texCoords, int[] texCoordsFromAndLengthIndices, + float[] colors, int[] colorsFromAndLengthIndices, int[] faces, int[] facesFromAndLengthIndices, int[] faceSmoothingGroups, int[] faceSmoothingGroupsFromAndLengthIndices) { if (userDefinedNormals) { - return buildPNTGeometry(points, pointsFromAndLengthIndices, + if (userDefinedColors) { + return buildPNTCGeometry(vertexFormat, + points, pointsFromAndLengthIndices, + normals, normalsFromAndLengthIndices, + texCoords, texCoordsFromAndLengthIndices, + colors, colorsFromAndLengthIndices, + faces, facesFromAndLengthIndices); + } else { + return buildPNTGeometry(vertexFormat, + points, pointsFromAndLengthIndices, + normals, normalsFromAndLengthIndices, + texCoords, texCoordsFromAndLengthIndices, + faces, facesFromAndLengthIndices); + } + } else if (userDefinedColors) { + return buildPTCGeometry(vertexFormat, points, texCoords, colors, faces, faceSmoothingGroups); + } else { + return buildPTCGeometry(vertexFormat, points, texCoords, null, faces, faceSmoothingGroups); + } + } + + // Build PointNormalTexCoordGeometry + private boolean buildPNTCGeometry(VertexFormat vertexFormat, + float[] points, int[] pointsFromAndLengthIndices, + float[] normals, int[] normalsFromAndLengthIndices, + float[] texCoords, int[] texCoordsFromAndLengthIndices, + float[] colors, int[] colorsFromAndLengthIndices, + int[] faces, int[] facesFromAndLengthIndices) { + + boolean updatePoints = pointsFromAndLengthIndices[1] > 0; + boolean updateNormals = normalsFromAndLengthIndices[1] > 0; + boolean updateTexCoords = texCoordsFromAndLengthIndices[1] > 0; + boolean updateColors = colorsFromAndLengthIndices[1] > 0; + boolean updateFaces = facesFromAndLengthIndices[1] > 0; + + // First time creation + boolean buildGeom = !(updatePoints || updateNormals || updateTexCoords || updateColors || updateFaces); + + // We will need to rebuild geom buffers if there is a change to faces + if (updateFaces) { + buildGeom = true; + } + + if ((!buildGeom) && (vertexBuffer != null) + && ((indexBuffer != null) || (indexBufferShort != null))) { + return updatePNTCGeometry(vertexFormat, + points, pointsFromAndLengthIndices, normals, normalsFromAndLengthIndices, texCoords, texCoordsFromAndLengthIndices, - faces, facesFromAndLengthIndices); - } else { - return buildPTGeometry(points, texCoords, faces, faceSmoothingGroups); + colors, colorsFromAndLengthIndices); } + return doBuildPNTCGeometry(vertexFormat, points, normals, texCoords, colors, faces); + } // Build PointNormalTexCoordGeometry - private boolean buildPNTGeometry( + private boolean buildPNTGeometry(VertexFormat vertexFormat, float[] points, int[] pointsFromAndLengthIndices, float[] normals, int[] normalsFromAndLengthIndices, float[] texCoords, int[] texCoordsFromAndLengthIndices, @@ -539,33 +658,37 @@ private boolean buildPNTGeometry( if ((!buildGeom) && (vertexBuffer != null) && ((indexBuffer != null) || (indexBufferShort != null))) { - return updatePNTGeometry(points, pointsFromAndLengthIndices, + return updatePNTCGeometry(vertexFormat, + points, pointsFromAndLengthIndices, normals, normalsFromAndLengthIndices, - texCoords, texCoordsFromAndLengthIndices); + texCoords, texCoordsFromAndLengthIndices, + null, null); } - return doBuildPNTGeometry(points, normals, texCoords, faces); + return doBuildPNTCGeometry(vertexFormat, points, normals, texCoords, null, faces); } // Build PointTexCoordGeometry - private boolean buildPTGeometry(float[] pos, float[] uv, int[] faces, int[] smoothing) { - nVerts = pos.length / 3; - nTVerts = uv.length / 2; - nFaces = faces.length / (VertexFormat.POINT_TEXCOORD.getVertexIndexSize() * 3); + private boolean buildPTCGeometry(VertexFormat vertexFormat, + float[] pos, float[] uv, float[] colors, int[] faces, int[] smoothing) { + nVerts = pos.length / POINT_SIZE; + nTVerts = uv.length / TEXCOORD_SIZE; + nFaces = faces.length / (vertexFormat.getVertexIndexSize() * VERTICES_PER_FACE); assert nVerts > 0 && nFaces > 0 && nTVerts > 0; this.pos = pos; + this.colors = colors; this.uv = uv; this.faces = faces; this.smoothing = smoothing.length == nFaces ? smoothing : null; MeshTempState instance = MeshTempState.getInstance(); // big pool for all possible vertices - if (instance.pool == null || instance.pool.length < nFaces * 3) { - instance.pool = new MeshVertex[nFaces * 3]; + if (instance.pool == null || instance.pool.length < nFaces * VERTICES_PER_FACE) { + instance.pool = new MeshVertex[nFaces * VERTICES_PER_FACE]; } - if (instance.indexBuffer == null || instance.indexBuffer.length < nFaces * 3) { - instance.indexBuffer = new int[nFaces * 3]; + if (instance.indexBuffer == null || instance.indexBuffer.length < nFaces * VERTICES_PER_FACE) { + instance.indexBuffer = new int[nFaces * VERTICES_PER_FACE]; } if (instance.pVertex == null || instance.pVertex.length < nVerts) { @@ -578,7 +701,7 @@ private boolean buildPTGeometry(float[] pos, float[] uv, int[] faces, int[] smoo checkSmoothingGroup(); // compute [N, T, B] for each face - computeTBNormal(instance.pool, instance.pVertex, instance.indexBuffer); + computeTBNormal(vertexFormat, instance.pool, instance.pVertex, instance.indexBuffer); // process sm and weld points int nNewVerts = MeshVertex.processVertices(instance.pVertex, nVerts, @@ -592,19 +715,19 @@ private boolean buildPTGeometry(float[] pos, float[] uv, int[] faces, int[] smoo if (nNewVerts > 0x10000) { buildIndexBuffer(instance.pool, instance.indexBuffer, null); - return buildNativeGeometry(instance.vertexBuffer, - nNewVerts * VERTEX_SIZE_VB, instance.indexBuffer, nFaces * 3); + return buildNativeGeometry(instance.vertexBuffer, nNewVerts * VERTEX_SIZE_VB, + instance.indexBuffer, nFaces * VERTICES_PER_FACE); } else { - if (instance.indexBufferShort == null || instance.indexBufferShort.length < nFaces * 3) { - instance.indexBufferShort = new short[nFaces * 3]; + if (instance.indexBufferShort == null || instance.indexBufferShort.length < nFaces * VERTICES_PER_FACE) { + instance.indexBufferShort = new short[nFaces * VERTICES_PER_FACE]; } buildIndexBuffer(instance.pool, instance.indexBuffer, instance.indexBufferShort); return buildNativeGeometry(instance.vertexBuffer, - nNewVerts * VERTEX_SIZE_VB, instance.indexBufferShort, nFaces * 3); + nNewVerts * VERTEX_SIZE_VB, instance.indexBufferShort, nFaces * VERTICES_PER_FACE); } } - private void computeTBNormal(MeshVertex[] pool, MeshVertex[] pVertex, int[] indexBuffer) { + private void computeTBNormal(VertexFormat vtxFormat, MeshVertex[] pool, MeshVertex[] pVertex, int[] indexBuffer) { MeshTempState instance = MeshTempState.getInstance(); // tmp variables @@ -616,9 +739,9 @@ private void computeTBNormal(MeshVertex[] pool, MeshVertex[] pVertex, int[] inde final String logname = BaseMesh.class.getName(); for (int f = 0, nDeadFaces = 0, poolIndex = 0; f < nFaces; f++) { - int index = f * 3; + int index = f * VERTICES_PER_FACE; - smFace = getFace(f, smFace); // copy from mesh to tmp smFace + smFace = getFace(vtxFormat, f, smFace); // copy from mesh to tmp smFace // Get tex. point. index triVerts[0] = smFace[BaseMesh.FaceMembers.POINT0.ordinal()]; @@ -634,7 +757,7 @@ private void computeTBNormal(MeshVertex[] pool, MeshVertex[] pVertex, int[] inde + "] @ face group " + f + "; nEmptyFaces = " + nDeadFaces); } - for (int i = 0; i < 3; i++) { + for (int i = 0; i < VERTICES_PER_FACE; i++) { triPoints[i] = getVertex(triVerts[i], triPoints[i]); } @@ -643,7 +766,7 @@ private void computeTBNormal(MeshVertex[] pool, MeshVertex[] pVertex, int[] inde triVerts[1] = smFace[BaseMesh.FaceMembers.TEXCOORD1.ordinal()]; triVerts[2] = smFace[BaseMesh.FaceMembers.TEXCOORD2.ordinal()]; - for (int i = 0; i < 3; i++) { + for (int i = 0; i < VERTICES_PER_FACE; i++) { triTexCoords[i] = getTVertex(triVerts[i], triTexCoords[i]); } @@ -651,21 +774,39 @@ private void computeTBNormal(MeshVertex[] pool, MeshVertex[] pVertex, int[] inde triTexCoords[0], triTexCoords[1], triTexCoords[2], triNormals); - for (int j = 0; j < 3; ++j) { + for (int j = 0; j < VERTICES_PER_FACE; ++j) { pool[poolIndex] = (pool[poolIndex] == null) ? new MeshVertex() : pool[poolIndex]; - for (int i = 0; i < 3; ++i) { + for (int i = 0; i < VERTICES_PER_FACE; ++i) { pool[poolIndex].norm[i].set(triNormals[i]); } pool[poolIndex].smGroup = smFace[BaseMesh.FaceMembers.SMOOTHING_GROUP.ordinal()]; pool[poolIndex].fIdx = f; pool[poolIndex].tVert = triVerts[j]; pool[poolIndex].index = MeshVertex.IDX_UNDEFINED; - int ii = j == 0 ? BaseMesh.FaceMembers.POINT0.ordinal() - : j == 1 ? BaseMesh.FaceMembers.POINT1.ordinal() - : BaseMesh.FaceMembers.POINT2.ordinal(); - int pIdx = smFace[ii]; + + BaseMesh.FaceMembers pointMember; + BaseMesh.FaceMembers colorMember; + switch (j) { + case 0: + pointMember = BaseMesh.FaceMembers.POINT0; + colorMember = BaseMesh.FaceMembers.COLOR0; + break; + case 1: + pointMember = BaseMesh.FaceMembers.POINT1; + colorMember = BaseMesh.FaceMembers.COLOR1; + break; + case 2: + default: + pointMember = BaseMesh.FaceMembers.POINT2; + colorMember = BaseMesh.FaceMembers.COLOR2; + break; + } + + int pIdx = smFace[pointMember.ordinal()]; + int cIdx = smFace[colorMember.ordinal()]; pool[poolIndex].pVert = pIdx; + pool[poolIndex].cVert = cIdx; indexBuffer[index + j] = pIdx; pool[poolIndex].next = pVertex[pIdx]; pVertex[pIdx] = pool[poolIndex]; @@ -703,11 +844,11 @@ private void buildVertexBuffer(MeshVertex[] pVerts, float[] vertexBuffer) { MeshVertex v = pVerts[i]; for (; v != null; v = v.next) { if (v.index == idLast) { - int ind = v.pVert * 3; + int ind = v.pVert * POINT_SIZE; vertexBuffer[index++] = pos[ind]; vertexBuffer[index++] = pos[ind + 1]; vertexBuffer[index++] = pos[ind + 2]; - ind = v.tVert * 2; + ind = v.tVert * TEXCOORD_SIZE; vertexBuffer[index++] = uv[ind]; vertexBuffer[index++] = uv[ind + 1]; buildVSQuat(v.norm, quat); @@ -715,17 +856,35 @@ private void buildVertexBuffer(MeshVertex[] pVerts, float[] vertexBuffer) { vertexBuffer[index++] = quat.y; vertexBuffer[index++] = quat.z; vertexBuffer[index++] = quat.w; + ind = v.cVert * COLOR_SIZE; + index = writeColorsToBuffer(colors, ind, vertexBuffer, index); idLast++; } } } } + private static int writeColorsToBuffer(float[] colors, int colorIndex, float[] outputVertexBuffer, int outputIndex) { + if (colors != null && colors.length >= colorIndex + COLOR_SIZE && colorIndex >= 0) { + outputVertexBuffer[outputIndex++] = colors[colorIndex]; // Red + outputVertexBuffer[outputIndex++] = colors[colorIndex + 1]; // Green + outputVertexBuffer[outputIndex++] = colors[colorIndex + 2]; // Blue + outputVertexBuffer[outputIndex++] = colors[colorIndex + 3]; // Alpha + } else { + outputVertexBuffer[outputIndex++] = 1F; + outputVertexBuffer[outputIndex++] = 1F; + outputVertexBuffer[outputIndex++] = 1F; + outputVertexBuffer[outputIndex++] = 1F; + } + + return outputIndex; + } + private void buildIndexBuffer(MeshVertex[] pool, int[] indexBuffer, short[] indexBufferShort) { for (int i = 0; i < nFaces; ++i) { - int index = i * 3; + int index = i * VERTICES_PER_FACE; if (indexBuffer[index] != MeshVertex.IDX_UNDEFINED) { - for (int j = 0; j < 3; ++j) { + for (int j = 0; j < VERTICES_PER_FACE; ++j) { assert (pool[index].fIdx == i); if (indexBufferShort != null) { indexBufferShort[index + j] = (short) pool[index + j].index; @@ -735,7 +894,7 @@ private void buildIndexBuffer(MeshVertex[] pool, int[] indexBuffer, short[] inde pool[index + j].next = null; // release reference } } else { - for (int j = 0; j < 3; ++j) { + for (int j = 0; j < VERTICES_PER_FACE; ++j) { if (indexBufferShort != null) { indexBufferShort[index + j] = 0; } else { @@ -762,7 +921,7 @@ public Vec3f getVertex(int pIdx, Vec3f vertex) { if (vertex == null) { vertex = new Vec3f(); } - int index = pIdx * 3; + int index = pIdx * POINT_SIZE; vertex.set(pos[index], pos[index + 1], pos[index + 2]); return vertex; } @@ -771,11 +930,20 @@ public Vec2f getTVertex(int tIdx, Vec2f texCoord) { if (texCoord == null) { texCoord = new Vec2f(); } - int index = tIdx * 2; + int index = tIdx * TEXCOORD_SIZE; texCoord.set(uv[index], uv[index + 1]); return texCoord; } + public Vec4f getColor(int tIdx, Vec4f color) { + if (color == null) { + color = new Vec4f(); + } + int index = tIdx * COLOR_SIZE; + color.set(colors[index], colors[index + 1], colors[index + 2], colors[index + 3]); + return color; + } + private void checkSmoothingGroup() { if (smoothing == null || smoothing.length == 0) { // all smooth allSameSmoothing = true; @@ -801,19 +969,31 @@ private void checkSmoothingGroup() { } } - public int[] getFace(int fIdx, int[] face) { - int index = fIdx * 6; + // Only valid when the VertexFormat does not contain normal data. + public int[] getFace(VertexFormat vertexFormat, int fIdx, int[] face) { + int vertexIndexSize = vertexFormat.getVertexIndexSize(); + int faceMemberCopyCount = (vertexIndexSize * VERTICES_PER_FACE); + + int index = fIdx * faceMemberCopyCount; if ((face == null) || (face.length < FACE_MEMBERS_SIZE)) { face = new int[FACE_MEMBERS_SIZE]; } - // Note: Order matter, [0, 5] == FaceMembers' points and texcoords - for (int i = 0; i < 6; i++) { - face[i] = faces[index + i]; + // Note: Order matters, + for (int i = 0; i < VERTICES_PER_FACE; i++) { + int baseIndex = (3 * i); + int srcIndex = index + (i * vertexIndexSize); + face[baseIndex] = faces[srcIndex + vertexFormat.getPointIndexOffset()]; + face[baseIndex + 1] = faces[srcIndex + vertexFormat.getTexCoordIndexOffset()]; + if (vertexFormat.getColorIndexOffset() >= 0) { + face[baseIndex + 2] = faces[srcIndex + vertexFormat.getColorIndexOffset()]; + } else { + face[baseIndex + 2] = -1; + } } - // Note: Order matter, 6 == FaceMembers.SMOOTHING_GROUP.ordinal() + // Note: Order matter, FACE_MEMBERS_COPIED_SIZE == FaceMembers.SMOOTHING_GROUP.ordinal() // There is a total of 32 smoothing groups. // Assign to 1st smoothing group if smoothing is null. - face[6] = smoothing != null ? smoothing[fIdx] : 1; + face[FACE_MEMBERS_COPIED_SIZE] = smoothing != null ? smoothing[fIdx] : 1; return face; } @@ -854,7 +1034,7 @@ class MeshGeomComp2VB { void addLoc(int loc) { if (locs == null) { - locs = new int[3]; // edge of mesh case + locs = new int[VERTICES_PER_FACE]; // edge of mesh case locs[0] = this.loc; locs[1] = loc; this.validLocs = 2; diff --git a/modules/javafx.graphics/src/main/java/com/sun/prism/impl/MeshVertex.java b/modules/javafx.graphics/src/main/java/com/sun/prism/impl/MeshVertex.java index e2eb87c0e85..f626e118f22 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/prism/impl/MeshVertex.java +++ b/modules/javafx.graphics/src/main/java/com/sun/prism/impl/MeshVertex.java @@ -36,6 +36,7 @@ class MeshVertex { int smGroup; int pVert; // vertexBuffer int tVert; // vertexBuffer + int cVert; // vertexBuffer int fIdx; int index; // indexBuffer Vec3f[] norm; // {N, T, B} @@ -216,7 +217,7 @@ public String toString() { + "\tnorm[0] = " + norm[0] + "\n" + "\tnorm[1] = " + norm[1] + "\n" + "\tnorm[2] = " + norm[2] + "\n" - + "\ttIndex = " + tVert + ", fIndex = " + fIdx + "\n" + + "\ttIndex = " + tVert + ", cIndex = " + cVert + ", fIndex = " + fIdx + "\n" + "\tpIdx = " + index + "\n" + "\tnext = " + ((next == null) ? next : next.getClass().getName() + "@0x" + Integer.toHexString(next.hashCode())) + "\n"; diff --git a/modules/javafx.graphics/src/main/java/javafx/scene/shape/TriangleMesh.java b/modules/javafx.graphics/src/main/java/javafx/scene/shape/TriangleMesh.java index 824060d0713..4ae5dcbbdd3 100644 --- a/modules/javafx.graphics/src/main/java/javafx/scene/shape/TriangleMesh.java +++ b/modules/javafx.graphics/src/main/java/javafx/scene/shape/TriangleMesh.java @@ -54,8 +54,8 @@ /** * Defines a 3D triangle mesh that consists of its associated {@code VertexFormat} * and a set of separate arrays of vertex components such as points, normals, - * texture coordinates, and an array of faces that define the individual triangles - * of the mesh. + * texture coordinates, colors, and an array of faces that define the individual + * triangles of the mesh. *

* Note that the term point, as used in the method names and method * descriptions, actually refers to a 3D point (x, y, z) in space @@ -67,11 +67,16 @@ * The term texCoord is used to indicate a single pair of 2D texture * coordinates (u, v) for a single vertex, while the term texCoords (plural) is used * to indicate sets of texture coordinates for multiple vertices. + * Next, the term color is used to indicate a group of four floats (r, g, b, a) + * forming a multiplicative color value for a single vertex. It is used for + * texture-modulated gouraud shading on the diffuse/selfIllumination maps. + * The Values represent the intensity of a particular color component, so a value of + * 1.5 for the blue component multiplies the texture's blue color intensity by 1.5. * Lastly, the term face is used to indicate 3 sets of interleaving points, - * normals (optional, depending on the associated VertexFormat) - * and texture coordinates that together represent the geometric topology of a - * single triangle, while the term faces (plural) is used to indicate sets of - * triangles (each represent by a face). + * normals (optional, depending on the associated VertexFormat), texCoords, and + * colors (optional, depending on the associated VertexFormat), that + * together represent the geometric topology of a single triangle, while the term + * faces (plural) is used to indicate sets of triangles (each represent by a face). *

* For example, the faces with {@code VertexFormat.POINT_TEXCOORD} that represent * a single textured rectangle, using 2 triangles, have the following data order: [ @@ -103,16 +108,19 @@ * information. * *

- * The length of {@code points}, {@code normals}, and {@code texCoords} must be - * divisible by 3, 3, and 2 respectively. The length of {@code faces} must be - * divisible by 6 if it is of {@code VertexFormat.POINT_TEXCOORD} else it must - * be divisible by 9 if it is of {@code VertexFormat.POINT_NORMAL_TEXCOORD}. + * The length of {@code points}, {@code normals}, {@code texCoords}, and + * {@code colors} must be divisible by 3, 3, 2, and 4 respectively. + * The length of {@code faces} must be divisible by the number of array elements + * tracked per-face. For example, {@code faces} must be divisible by 6 if it is of + * {@code VertexFormat.POINT_TEXCOORD}, or it must be divisible by 9 if it is + * of {@code VertexFormat.POINT_NORMAL_TEXCOORD}. * The values in the faces array must be within the range of the number of vertices * in the points array (0 to points.length / 3 - 1) for the point indices, within * the range of the number of vertices in the normals array - * (0 to normals.length / 3 - 1) for the normal indices, and within the range of + * (0 to normals.length / 3 - 1) for the normal indices, within the range of * the number of the vertices in the texCoords array (0 to texCoords.length / 2 - 1) - * for the texture coordinate indices. + * for the texture coordinate indices, and within the colors array + * (0 to colors.length / 4 - 1) for the color indices. * *

A warning will be recorded to the logger and the mesh will not be rendered * (and will have an empty bounds) if any of the array lengths are invalid @@ -136,17 +144,20 @@ public boolean doComputeIntersects(Mesh mesh, PickRay pickRay, private final ObservableFloatArray points = FXCollections.observableFloatArray(); private final ObservableFloatArray normals = FXCollections.observableFloatArray(); private final ObservableFloatArray texCoords = FXCollections.observableFloatArray(); + private final ObservableFloatArray colors = FXCollections.observableFloatArray(); private final ObservableFaceArray faces = new ObservableFaceArrayImpl(); private final ObservableIntegerArray faceSmoothingGroups = FXCollections.observableIntegerArray(); private final Listener pointsSyncer = new Listener(points); private final Listener normalsSyncer = new Listener(normals); private final Listener texCoordsSyncer = new Listener(texCoords); + private final Listener colorsSyncer = new Listener(colors); private final Listener facesSyncer = new Listener(faces); private final Listener faceSmoothingGroupsSyncer = new Listener(faceSmoothingGroups); private final boolean isPredefinedShape; private boolean isValidDirty = true; - private boolean isPointsValid, isNormalsValid, isTexCoordsValid, isFacesValid, isFaceSmoothingGroupValid; + private boolean isPointsValid, isNormalsValid, isTexCoordsValid, + isColorsValid, isFacesValid, isFaceSmoothingGroupValid; private int refCount = 1; private BaseBounds cachedBounds; @@ -180,12 +191,14 @@ public TriangleMesh(VertexFormat vertexFormat) { isPointsValid = true; isNormalsValid = true; isTexCoordsValid = true; + isColorsValid = true; isFacesValid = true; isFaceSmoothingGroupValid = true; } else { isPointsValid = false; isNormalsValid = false; isTexCoordsValid = false; + isColorsValid = false; isFacesValid = false; isFaceSmoothingGroupValid = false; } @@ -194,7 +207,8 @@ public TriangleMesh(VertexFormat vertexFormat) { /** * Specifies the vertex format of this {@code TriangleMesh}, one of - * {@code VertexFormat.POINT_TEXCOORD} or {@code VertexFormat.POINT_NORMAL_TEXCOORD}. + * {@code VertexFormat.POINT_TEXCOORD}, {@code VertexFormat.POINT_TEXCOORD_COLOR}, + * {@code VertexFormat.POINT_NORMAL_TEXCOORD}, or {@code VertexFormat.POINT_NORMAL_TEXCOORD_COLOR}. * * @defaultValue VertexFormat.POINT_TEXCOORD * @@ -217,6 +231,7 @@ public final ObjectProperty vertexFormatProperty() { @Override protected void invalidated() { setDirty(true); + isValidDirty = true; // Need to mark faces and faceSmoothingGroups dirty too. facesSyncer.setDirty(true); faceSmoothingGroupsSyncer.setDirty(true); @@ -255,6 +270,15 @@ public final int getTexCoordElementSize() { return getVertexFormat().getTexCoordElementSize(); } + /** + * Returns the number of elements that represents a color. + * + * @return number of elements + */ + public final int getColorElementSize() { + return getVertexFormat().getColorElementSize(); + } + /** * Returns the number of elements that represents a face. * @@ -298,6 +322,23 @@ public final ObservableFloatArray getTexCoords() { return texCoords; } + /** + * Gets the {@code colors} array of this {@code TriangleMesh}. + * The multiplicative colors are normalized, so a value of (0.5, 2.5, 1.0, 1.0) + * will multiply the texture's red component by 0.5, the blue component by 2.5, + * and the rest by 1.0. + * Colors are interpolated between vertices before usage. + * They scale (multiply) the diffuse pixel color before lighting calculations + * as well as the self-illumination pixel color after lighting calculations. + * Specular lighting is not impacted by vertex coloring. + * + * @return {@code color} array where each color is represented + * by 4 float values: red, green, blue, and alpha, in that order. + */ + public final ObservableFloatArray getColors() { + return colors; + } + /** * Gets the {@code faces} array, indices into the {@code points}, * {@code normals} (optional, if it is a {@code VertexFormat.POINT_NORMAL_TEXCOORD} @@ -335,7 +376,8 @@ public final ObservableFaceArray getFaces() { * be equal to number of faces. * *

This faceSmoothingGroups has no effect on its {@code TriangleMesh} if - * it is of {@code VertexFormat.POINT_NORMAL_TEXCOORD} format. + * it is of either {@code VertexFormat.POINT_NORMAL_TEXCOORD} or + * {@code VertexFormat.POINT_NORMAL_TEXCOORD_COLOR} format. * @return the {@code faceSmoothingGroups} array of this {@code TriangleMesh} */ public final ObservableIntegerArray getFaceSmoothingGroups() { @@ -348,6 +390,7 @@ public final ObservableIntegerArray getFaceSmoothingGroups() { pointsSyncer.setDirty(false); normalsSyncer.setDirty(false); texCoordsSyncer.setDirty(false); + colorsSyncer.setDirty(false); facesSyncer.setDirty(false); faceSmoothingGroupsSyncer.setDirty(false); } @@ -395,9 +438,15 @@ private boolean validatePoints() { return true; } + private boolean vertexFormatHasNormals() { + return getVertexFormat().getNormalIndexOffset() >= 0; + } + private boolean validateNormals() { // Only validate normals if vertex format has normal component - if (getVertexFormat() != VertexFormat.POINT_NORMAL_TEXCOORD) return true; + if (!vertexFormatHasNormals()) { + return true; + } if (normals.size() == 0) { // Valid but meaningless for picking or rendering. return false; @@ -429,6 +478,30 @@ private boolean validateTexCoords() { return true; } + private boolean vertexFormatHasColors() { + return getVertexFormat().getColorIndexOffset() >= 0; + } + + private boolean validateColors() { + if (!vertexFormatHasColors()) { // Only validate normals if vertex format has color component + return true; + } + + if (colors.size() == 0) { // Valid but meaningless for picking or rendering. + return false; + } + + if ((colors.size() % getVertexFormat().getColorElementSize()) != 0) { + String logname = TriangleMesh.class.getName(); + PlatformLogger.getLogger(logname).warning("colors.size() " + + "has to be divisible by getColorElementSize()." + + " It is to store multiple r/g/b/a colors" + + " of this mesh"); + return false; + } + return true; + } + private boolean validateFaces() { if (faces.size() == 0) { // Valid but meaningless for picking or rendering. return false; @@ -441,44 +514,50 @@ private boolean validateFaces() { return false; } - if (getVertexFormat() == VertexFormat.POINT_TEXCOORD) { - int nVerts = points.size() / getVertexFormat().getPointElementSize(); - int nTVerts = texCoords.size() / getVertexFormat().getTexCoordElementSize(); - for (int i = 0; i < faces.size(); i++) { - if (i % 2 == 0 && (faces.get(i) >= nVerts || faces.get(i) < 0) - || (i % 2 != 0 && (faces.get(i) >= nTVerts || faces.get(i) < 0))) { - PlatformLogger.getLogger(logname).warning("The values in the " - + "faces array must be within the range of the number " - + "of vertices in the points array (0 to points.length / 3 - 1) " - + "for the point indices and within the range of the " - + "number of the vertices in the texCoords array (0 to " - + "texCoords.length / 2 - 1) for the texture coordinate indices."); - return false; - } + VertexFormat vertexFormat = getVertexFormat(); + int nVerts = points.size() / vertexFormat.getPointElementSize(); + int nNVerts = normals.size() / vertexFormat.getNormalElementSize(); + int nTVerts = texCoords.size() / vertexFormat.getTexCoordElementSize(); + int nCVerts = colors.size() / vertexFormat.getColorElementSize(); + int pointIndexOffset = vertexFormat.getPointIndexOffset(); + int normalIndexOffset = vertexFormat.getNormalIndexOffset(); + int texCoordIndexOffset = vertexFormat.getTexCoordIndexOffset(); + int colorIndexOffset = vertexFormat.getColorIndexOffset(); + + for (int i = 0; i < faces.size(); i += vertexFormat.getVertexIndexSize()) { + if (pointIndexOffset >= 0 + && (faces.get(i + pointIndexOffset) >= nVerts || faces.get(i + pointIndexOffset) < 0)) { + PlatformLogger.getLogger(logname).warning("The indices in the " + + "faces array must be within the range of the number " + + "of entries in the points array (0 to points.length / 3 - 1)."); + return false; } - } else if (getVertexFormat() == VertexFormat.POINT_NORMAL_TEXCOORD) { - int nVerts = points.size() / getVertexFormat().getPointElementSize(); - int nNVerts = normals.size() / getVertexFormat().getNormalElementSize(); - int nTVerts = texCoords.size() / getVertexFormat().getTexCoordElementSize(); - for (int i = 0; i < faces.size(); i+=3) { - if ((faces.get(i) >= nVerts || faces.get(i) < 0) - || (faces.get(i + 1) >= nNVerts || faces.get(i + 1) < 0) - || (faces.get(i + 2) >= nTVerts || faces.get(i + 2) < 0)) { - PlatformLogger.getLogger(logname).warning("The values in the " - + "faces array must be within the range of the number " - + "of vertices in the points array (0 to points.length / 3 - 1) " - + "for the point indices, and within the range of the " - + "number of the vertices in the normals array (0 to " - + "normals.length / 3 - 1) for the normals indices, and " - + "number of the vertices in the texCoords array (0 to " - + "texCoords.length / 2 - 1) for the texture coordinate indices."); - return false; - } + + if (normalIndexOffset >= 0 + && (faces.get(i + normalIndexOffset) >= nNVerts || faces.get(i + normalIndexOffset) < 0)) { + PlatformLogger.getLogger(logname).warning("The indices in the " + + "faces array must be within the range of the number " + + "of entries in the normals array (0 to normals.length / 3 - 1)."); + return false; + } + + if (texCoordIndexOffset >= 0 + && (faces.get(i + texCoordIndexOffset) >= nTVerts || faces.get(i + texCoordIndexOffset) < 0)) { + PlatformLogger.getLogger(logname).warning("The indices in the " + + "faces array must be within the range of the number " + + "of entries in the texCoords array (0 to texCoords.length / 2 - 1)."); + return false; + } + + if (colorIndexOffset >= 0 + && (faces.get(i + colorIndexOffset) >= nCVerts || faces.get(i + colorIndexOffset) < 0)) { + PlatformLogger.getLogger(logname).warning("The indices in the " + + "faces array must be within the range of the number " + + "of entries in the colors array (0 to colors.length / 4 - 1)."); + return false; } - } else { - PlatformLogger.getLogger(logname).warning("Unsupported VertexFormat: " + getVertexFormat().toString()); - return false; } + return true; } @@ -508,17 +587,20 @@ private boolean validate() { if (texCoordsSyncer.dirtyInFull) { isTexCoordsValid = validateTexCoords(); } - if (facesSyncer.dirty || pointsSyncer.dirtyInFull - || normalsSyncer.dirtyInFull || texCoordsSyncer.dirtyInFull) { + if (colorsSyncer.dirtyInFull) { + isColorsValid = validateColors(); + } + if (facesSyncer.dirty || pointsSyncer.dirtyInFull || normalsSyncer.dirtyInFull + || colorsSyncer.dirtyInFull || texCoordsSyncer.dirtyInFull) { isFacesValid = isPointsValid && isNormalsValid - && isTexCoordsValid && validateFaces(); + && isTexCoordsValid && isColorsValid && validateFaces(); } if (faceSmoothingGroupsSyncer.dirtyInFull || facesSyncer.dirtyInFull) { isFaceSmoothingGroupValid = isFacesValid && validateFaceSmoothingGroups(); } isValidDirty = false; } - return isPointsValid && isNormalsValid && isTexCoordsValid + return isPointsValid && isNormalsValid && isTexCoordsValid && isColorsValid && isFaceSmoothingGroupValid && isFacesValid; } @@ -530,17 +612,23 @@ void updatePG() { final NGTriangleMesh pgTriMesh = getPGTriangleMesh(); if (validate()) { - pgTriMesh.setUserDefinedNormals(getVertexFormat() == VertexFormat.POINT_NORMAL_TEXCOORD); + pgTriMesh.setVertexFormat(getVertexFormat()); + pgTriMesh.setUserDefinedNormals(vertexFormatHasNormals()); + pgTriMesh.setUserDefinedColors(vertexFormatHasColors()); pgTriMesh.syncPoints(pointsSyncer); pgTriMesh.syncNormals(normalsSyncer); pgTriMesh.syncTexCoords(texCoordsSyncer); + pgTriMesh.syncColors(colorsSyncer); pgTriMesh.syncFaces(facesSyncer); pgTriMesh.syncFaceSmoothingGroups(faceSmoothingGroupsSyncer); } else { + pgTriMesh.setVertexFormat(null); pgTriMesh.setUserDefinedNormals(false); + pgTriMesh.setUserDefinedColors(false); pgTriMesh.syncPoints(null); pgTriMesh.syncNormals(null); pgTriMesh.syncTexCoords(null); + pgTriMesh.syncColors(null); pgTriMesh.syncFaces(null); pgTriMesh.syncFaceSmoothingGroups(null); } diff --git a/modules/javafx.graphics/src/main/java/javafx/scene/shape/VertexFormat.java b/modules/javafx.graphics/src/main/java/javafx/scene/shape/VertexFormat.java index a442597b879..4c81424fce1 100644 --- a/modules/javafx.graphics/src/main/java/javafx/scene/shape/VertexFormat.java +++ b/modules/javafx.graphics/src/main/java/javafx/scene/shape/VertexFormat.java @@ -26,7 +26,7 @@ /** * Defines the format of the vertices in a mesh. A vertex consists of an array - * of points, normals (optional), and texture coordinates. + * of points, normals (optional), texture coordinates, and colors (optional). * * @since JavaFX 8u40 */ @@ -35,31 +35,45 @@ public final class VertexFormat { /** * Specifies the format of a vertex that consists of a point and texture coordinates. */ - public static final VertexFormat POINT_TEXCOORD = new VertexFormat("POINT_TEXCOORD", 2, 0, -1, 1); + public static final VertexFormat POINT_TEXCOORD = new VertexFormat("POINT_TEXCOORD", 2, 0, -1, 1, -1); + + /** + * Specifies the format of a vertex that consists of a point, texture coordinates, and a color. + */ + public static final VertexFormat POINT_TEXCOORD_COLOR = new VertexFormat("POINT_TEXCOORD_COLOR", 3, 0, -1, 1, 2); /** * Specifies the format of a vertex that consists of a point, normal and texture coordinates. */ - public static final VertexFormat POINT_NORMAL_TEXCOORD = new VertexFormat("POINT_NORMAL_TEXCOORD", 3, 0, 1, 2); + public static final VertexFormat POINT_NORMAL_TEXCOORD = new VertexFormat("POINT_NORMAL_TEXCOORD", 3, 0, 1, 2, -1); + + /** + * Specifies the format of a vertex that consists of a point, normal, texture coordinates, and a color. + */ + public static final VertexFormat POINT_NORMAL_TEXCOORD_COLOR + = new VertexFormat("POINT_NORMAL_TEXCOORD_COLOR", 4, 0, 1, 2, 3); // For internal use only private static final int POINT_ELEMENT_SIZE = 3; private static final int NORMAL_ELEMENT_SIZE = 3; private static final int TEXCOORD_ELEMENT_SIZE = 2; + private static final int COLOR_ELEMENT_SIZE = 4; private final String name; private final int vertexIndexSize; private final int pointIndexOffset; private final int normalIndexOffset; private final int texCoordIndexOffset; + private final int colorIndexOffset; private VertexFormat(String name, int vertexIndexSize, - int pointIndexOffset, int normalIndexOffset, int texCoordIndexOffset) { + int pointIndexOffset, int normalIndexOffset, int texCoordIndexOffset, int colorIndexOffset) { this.name = name; this.vertexIndexSize = vertexIndexSize; this.pointIndexOffset = pointIndexOffset; this.normalIndexOffset = normalIndexOffset; this.texCoordIndexOffset = texCoordIndexOffset; + this.colorIndexOffset = colorIndexOffset; } int getPointElementSize() { @@ -74,6 +88,10 @@ int getTexCoordElementSize() { return TEXCOORD_ELEMENT_SIZE; } + int getColorElementSize() { + return COLOR_ELEMENT_SIZE; + } + /** * Returns the number of component indices that represents a vertex. For example, * a POINT_TEXCOORD vertex consists of 2 indices, one for point component and @@ -115,6 +133,16 @@ public int getTexCoordIndexOffset() { return texCoordIndexOffset; } + /** + * Returns the index offset in the face array of the color component + * within a vertex. + * + * @return the offset to the color component. + */ + public int getColorIndexOffset() { + return colorIndexOffset; + } + @Override public String toString() { return name; diff --git a/modules/javafx.graphics/src/main/native-prism-d3d/D3DContext.h b/modules/javafx.graphics/src/main/native-prism-d3d/D3DContext.h index 88de3157917..eb8e4c0973d 100644 --- a/modules/javafx.graphics/src/main/native-prism-d3d/D3DContext.h +++ b/modules/javafx.graphics/src/main/native-prism-d3d/D3DContext.h @@ -52,6 +52,7 @@ struct PRISM_VERTEX_3D { float x, y, z; float tu, tv; float nx, ny, nz, nw; + float red, green, blue, alpha; }; const D3DVERTEXELEMENT9 PrismVDecl[5] = { diff --git a/modules/javafx.graphics/src/main/native-prism-d3d/D3DMesh.cc b/modules/javafx.graphics/src/main/native-prism-d3d/D3DMesh.cc index 5dc6cb5af93..8d7ab11227f 100644 --- a/modules/javafx.graphics/src/main/native-prism-d3d/D3DMesh.cc +++ b/modules/javafx.graphics/src/main/native-prism-d3d/D3DMesh.cc @@ -44,7 +44,7 @@ D3DMesh::D3DMesh(D3DContext *ctx) { indexBuffer = NULL; vertexBuffer = NULL; // See MeshData.cc where n = 1 - fvf = D3DFVF_XYZ | (2 << D3DFVF_TEXCOUNT_SHIFT) | D3DFVF_TEXCOORDSIZE4(1); + fvf = D3DFVF_XYZ | (3 << D3DFVF_TEXCOUNT_SHIFT) | D3DFVF_TEXCOORDSIZE4(2) | D3DFVF_TEXCOORDSIZE4(1); numVertices = 0; numIndices = 0; } diff --git a/modules/javafx.graphics/src/main/native-prism-d3d/hlsl/Mtl1PS.hlsl b/modules/javafx.graphics/src/main/native-prism-d3d/hlsl/Mtl1PS.hlsl index eb4e7af18b3..dcc4adb3624 100644 --- a/modules/javafx.graphics/src/main/native-prism-d3d/hlsl/Mtl1PS.hlsl +++ b/modules/javafx.graphics/src/main/native-prism-d3d/hlsl/Mtl1PS.hlsl @@ -72,8 +72,8 @@ float4 main(PsInput psInput) : color { // diffuse float4 tDiff = tex2D(mapDiffuse, texD); + tDiff = tDiff * gDiffuseColor * psInput.vertColor; if (tDiff.a == 0.0) discard; - tDiff = tDiff * gDiffuseColor; // return gDiffuseColor.aaaa; @@ -125,7 +125,7 @@ float4 main(PsInput psInput) : color { // self-illumination if (isIlluminated) { - rez += tex2D(mapSelfIllum, texD).rgb; + rez += tex2D(mapSelfIllum, texD).rgb * psInput.vertColor; } return float4(saturate(rez), tDiff.a); diff --git a/modules/javafx.graphics/src/main/native-prism-d3d/hlsl/Mtl1VS.hlsl b/modules/javafx.graphics/src/main/native-prism-d3d/hlsl/Mtl1VS.hlsl index 1a37e8929ef..f5796ac3dfd 100644 --- a/modules/javafx.graphics/src/main/native-prism-d3d/hlsl/Mtl1VS.hlsl +++ b/modules/javafx.graphics/src/main/native-prism-d3d/hlsl/Mtl1VS.hlsl @@ -31,6 +31,7 @@ VsOutput main(VertexType vsInput) { VsOutput vsOutput; vsOutput.texD = vsInput.texD; + vsOutput.vertColor = vsInput.modelVertexColor; transformVertexAttributes(vsInput.modelVertexPos, vsInput.modelVertexNormal, vsOutput); return vsOutput; diff --git a/modules/javafx.graphics/src/main/native-prism-d3d/hlsl/vs2ps.h b/modules/javafx.graphics/src/main/native-prism-d3d/hlsl/vs2ps.h index 7e2290e251e..fa8e045c82c 100644 --- a/modules/javafx.graphics/src/main/native-prism-d3d/hlsl/vs2ps.h +++ b/modules/javafx.graphics/src/main/native-prism-d3d/hlsl/vs2ps.h @@ -45,6 +45,8 @@ typedef struct PsInput { float2 texD : texcoord0; + float4 vertColor : color0; + // float oFog : fog; // float3 debug : texcoord11; } VsOutput; diff --git a/modules/javafx.graphics/src/main/native-prism-d3d/hlsl/vsDecl.h b/modules/javafx.graphics/src/main/native-prism-d3d/hlsl/vsDecl.h index 5bc7bda5797..c2985e083fa 100644 --- a/modules/javafx.graphics/src/main/native-prism-d3d/hlsl/vsDecl.h +++ b/modules/javafx.graphics/src/main/native-prism-d3d/hlsl/vsDecl.h @@ -31,5 +31,6 @@ struct VsInput { // model space = local space = object space float4 modelVertexPos : position; float2 texD : texcoord0; - float4 modelVertexNormal : texcoord1; + float4 modelVertexNormal : texcoord1; // Presumably these are declared as texcoords to make FVF vertex buffers work. + float4 modelVertexColor : texcoord2; }; diff --git a/modules/javafx.graphics/src/main/native-prism-es2/GLContext.c b/modules/javafx.graphics/src/main/native-prism-es2/GLContext.c index 2cb5db885e6..a6de3c221f5 100644 --- a/modules/javafx.graphics/src/main/native-prism-es2/GLContext.c +++ b/modules/javafx.graphics/src/main/native-prism-es2/GLContext.c @@ -1740,6 +1740,7 @@ JNIEXPORT void JNICALL Java_com_sun_prism_es2_GLContext_nSetDeviceParametersFor2 ctxInfo->glDisableVertexAttribArray(VC_3D_INDEX); ctxInfo->glDisableVertexAttribArray(NC_3D_INDEX); ctxInfo->glDisableVertexAttribArray(TC_3D_INDEX); + ctxInfo->glDisableVertexAttribArray(CC_3D_INDEX); ctxInfo->vbFloatData = NULL; ctxInfo->vbByteData = NULL; @@ -2348,6 +2349,7 @@ JNIEXPORT void JNICALL Java_com_sun_prism_es2_GLContext_nRenderMeshView ctxInfo->glEnableVertexAttribArray(VC_3D_INDEX); ctxInfo->glEnableVertexAttribArray(TC_3D_INDEX); ctxInfo->glEnableVertexAttribArray(NC_3D_INDEX); + ctxInfo->glEnableVertexAttribArray(CC_3D_INDEX); ctxInfo->glVertexAttribPointer(VC_3D_INDEX, VC_3D_SIZE, GL_FLOAT, GL_FALSE, VERT_3D_STRIDE, (const GLvoid *) jlong_to_ptr((jlong) offset)); @@ -2357,6 +2359,9 @@ JNIEXPORT void JNICALL Java_com_sun_prism_es2_GLContext_nRenderMeshView offset += TC_3D_SIZE * sizeof(GLfloat); ctxInfo->glVertexAttribPointer(NC_3D_INDEX, NC_3D_SIZE, GL_FLOAT, GL_FALSE, VERT_3D_STRIDE, (const GLvoid *) jlong_to_ptr((jlong) offset)); + offset += NC_3D_SIZE * sizeof(GLfloat); + ctxInfo->glVertexAttribPointer(CC_3D_INDEX, CC_3D_SIZE, GL_FLOAT, GL_FALSE, + VERT_3D_STRIDE, (const GLvoid *) jlong_to_ptr((jlong) offset)); glDrawElements(GL_TRIANGLES, mvInfo->meshInfo->indexBufferSize, mvInfo->meshInfo->indexBufferType, 0); @@ -2365,6 +2370,7 @@ JNIEXPORT void JNICALL Java_com_sun_prism_es2_GLContext_nRenderMeshView ctxInfo->glDisableVertexAttribArray(VC_3D_INDEX); ctxInfo->glDisableVertexAttribArray(NC_3D_INDEX); ctxInfo->glDisableVertexAttribArray(TC_3D_INDEX); + ctxInfo->glDisableVertexAttribArray(CC_3D_INDEX); ctxInfo->glBindBuffer(GL_ARRAY_BUFFER, 0); ctxInfo->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } diff --git a/modules/javafx.graphics/src/main/native-prism-es2/PrismES2Defs.h b/modules/javafx.graphics/src/main/native-prism-es2/PrismES2Defs.h index aa43478e350..dbcfae093cc 100644 --- a/modules/javafx.graphics/src/main/native-prism-es2/PrismES2Defs.h +++ b/modules/javafx.graphics/src/main/native-prism-es2/PrismES2Defs.h @@ -346,10 +346,12 @@ extern void deletePixelFormatInfo(PixelFormatInfo *pfInfo); #define VC_3D_INDEX 0 #define TC_3D_INDEX 1 #define NC_3D_INDEX 2 +#define CC_3D_INDEX 3 #define VC_3D_SIZE 3 /* x, y, z */ #define TC_3D_SIZE 2 /* tu, tv */ #define NC_3D_SIZE 4 /* nx, ny, nz, nw */ -#define VERT_3D_SIZE (VC_3D_SIZE + TC_3D_SIZE + NC_3D_SIZE) +#define CC_3D_SIZE 4 /* red, green, blue, alpha */ +#define VERT_3D_SIZE (VC_3D_SIZE + TC_3D_SIZE + NC_3D_SIZE + CC_3D_SIZE) #define VERT_3D_STRIDE (sizeof(GLfloat) * VERT_3D_SIZE) #define MESH_VERTEXBUFFER 0 diff --git a/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/diffuse_color.frag b/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/diffuse_color.frag index 9900d788572..57cead41665 100644 --- a/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/diffuse_color.frag +++ b/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/diffuse_color.frag @@ -57,6 +57,6 @@ uniform sampler2D diffuseTexture; varying vec2 oTexCoords; vec4 apply_diffuse() { - return diffuseColor; + return diffuseColor * vtxColor; } */ diff --git a/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/diffuse_none.frag b/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/diffuse_none.frag index 2f6557f21fd..1ff66d207f5 100644 --- a/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/diffuse_none.frag +++ b/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/diffuse_none.frag @@ -56,6 +56,6 @@ uniform sampler2D diffuseTexture; varying vec2 oTexCoords; vec4 apply_diffuse() { - return vec4(0); + return vtxColor; } diff --git a/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/diffuse_texture.frag b/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/diffuse_texture.frag index c6f7fb24ed9..4875e5b9757 100644 --- a/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/diffuse_texture.frag +++ b/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/diffuse_texture.frag @@ -57,5 +57,5 @@ varying vec2 oTexCoords; vec4 apply_diffuse() { vec4 dTexColor = texture2D(diffuseTexture, oTexCoords); - return dTexColor * diffuseColor; + return dTexColor * diffuseColor * vtxColor; } diff --git a/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/main.vert b/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/main.vert index cd696123b12..f631e1e083a 100644 --- a/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/main.vert +++ b/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/main.vert @@ -31,6 +31,7 @@ uniform vec3 ambientColor; attribute vec3 pos; attribute vec2 texCoords; attribute vec4 tangent; +attribute vec4 color; struct Light { vec4 pos; @@ -49,6 +50,7 @@ uniform Light lights[3]; varying vec4 lightTangentSpacePositions[3]; varying vec4 lightTangentSpaceDirections[3]; varying vec2 oTexCoords; +varying vec4 vtxColor; varying vec3 eyePos; vec3 getLocalVector(vec3 global, vec3 tangentFrame[3]) { @@ -113,7 +115,8 @@ void main() mat4 mvpMatrix = viewProjectionMatrix * worldMatrix; - //Send texcoords to Pixel Shader and calculate vertex position. + //Send color & texcoords to Pixel Shader and calculate vertex position. oTexCoords = texCoords; + vtxColor = color; gl_Position = mvpMatrix * vec4(pos,1.0); } diff --git a/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/main0Lights.frag b/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/main0Lights.frag index 4b50e0c36d0..dd3d8c4febd 100644 --- a/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/main0Lights.frag +++ b/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/main0Lights.frag @@ -50,6 +50,8 @@ precision mediump int; #endif +varying vec4 vtxColor; + vec4 apply_diffuse(); vec4 apply_selfIllum(); diff --git a/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/main1Light.frag b/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/main1Light.frag index 6889855ae76..4c7334705bb 100644 --- a/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/main1Light.frag +++ b/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/main1Light.frag @@ -50,6 +50,8 @@ precision mediump int; #endif +varying vec4 vtxColor; + vec4 apply_diffuse(); vec4 apply_specular(); vec3 apply_normal(); diff --git a/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/main2Lights.frag b/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/main2Lights.frag index 310f9b66e91..986ab10e210 100644 --- a/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/main2Lights.frag +++ b/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/main2Lights.frag @@ -50,6 +50,8 @@ precision mediump int; #endif +varying vec4 vtxColor; + vec4 apply_diffuse(); vec4 apply_specular(); vec3 apply_normal(); diff --git a/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/main3Lights.frag b/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/main3Lights.frag index 7bdf176007f..ebfdce1b4af 100644 --- a/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/main3Lights.frag +++ b/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/main3Lights.frag @@ -50,6 +50,8 @@ precision mediump int; #endif +varying vec4 vtxColor; + vec4 apply_diffuse(); vec4 apply_specular(); vec3 apply_normal(); diff --git a/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/selfIllum_texture.frag b/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/selfIllum_texture.frag index 6c0d1e7ea7e..0f28be36cac 100644 --- a/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/selfIllum_texture.frag +++ b/modules/javafx.graphics/src/main/resources/com/sun/prism/es2/glsl/selfIllum_texture.frag @@ -53,6 +53,6 @@ precision mediump int; uniform sampler2D selfIllumTexture; vec4 apply_selfIllum() { - return texture2D(selfIllumTexture, oTexCoords); + return texture2D(selfIllumTexture, oTexCoords) * vtxColor; } diff --git a/modules/javafx.graphics/src/shims/java/com/sun/javafx/sg/prism/NGTriangleMeshShim.java b/modules/javafx.graphics/src/shims/java/com/sun/javafx/sg/prism/NGTriangleMeshShim.java index ed551660813..12df23f5b71 100644 --- a/modules/javafx.graphics/src/shims/java/com/sun/javafx/sg/prism/NGTriangleMeshShim.java +++ b/modules/javafx.graphics/src/shims/java/com/sun/javafx/sg/prism/NGTriangleMeshShim.java @@ -54,6 +54,11 @@ public float[] test_getTexCoords() { return super.test_getTexCoords(); } + @Override + public float[] test_getColors() { + return super.test_getColors(); + } + public static BaseMesh test_getMesh(NGTriangleMesh triMesh) { return (BaseMesh) triMesh.test_getMesh(); } diff --git a/modules/javafx.graphics/src/test/java/test/com/sun/javafx/sg/prism/NGTriangleMeshTest.java b/modules/javafx.graphics/src/test/java/test/com/sun/javafx/sg/prism/NGTriangleMeshTest.java index 2a130349f11..ca0fe995c25 100644 --- a/modules/javafx.graphics/src/test/java/test/com/sun/javafx/sg/prism/NGTriangleMeshTest.java +++ b/modules/javafx.graphics/src/test/java/test/com/sun/javafx/sg/prism/NGTriangleMeshTest.java @@ -153,6 +153,36 @@ public void testSyncTexCoords2() { assertArrayEquals(expecteds, actuals, EPSILON_FLOAT); } + /** + * Test of syncColors method, of class NGTriangleMesh. + */ + @Test + public void testSyncColors() { + final float[] colors = new float[]{0, 1, 2, 3, 4, 5}; + NGTriangleMeshShim instance = new NGTriangleMeshShim(); + instance.syncColors((array, fromAndLengthIndices) -> colors); + float[] actuals = instance.test_getColors(); + float[] expecteds = new float[]{0, 1, 2, 3, 4, 5}; + assertArrayEquals(expecteds, actuals, EPSILON_FLOAT); + } + + /** + * Test of syncColors method, of class NGTriangleMesh. + */ + @Test + public void testSyncColors2() { + final float[] colors = new float[]{0, 1, 2, 3, 4, 5}; + NGTriangleMeshShim instance = new NGTriangleMeshShim(); + instance.syncColors((array, fromAndLengthIndices) -> colors); + instance.syncColors((array, fromAndLengthIndices) -> { + Arrays.fill(array, 1, 1 + 4, 1); + return array; + }); + float[] actuals = instance.test_getColors(); + float[] expecteds = new float[]{0, 1, 1, 1, 1, 5}; + assertArrayEquals(expecteds, actuals, EPSILON_FLOAT); + } + /** * Test of syncFaces method, of class NGTriangleMesh. */ diff --git a/modules/javafx.graphics/src/test/java/test/javafx/scene/shape/TriangleMeshTest.java b/modules/javafx.graphics/src/test/java/test/javafx/scene/shape/TriangleMeshTest.java index a11d6dbb8c1..9b624abf4ca 100644 --- a/modules/javafx.graphics/src/test/java/test/javafx/scene/shape/TriangleMeshTest.java +++ b/modules/javafx.graphics/src/test/java/test/javafx/scene/shape/TriangleMeshTest.java @@ -27,6 +27,8 @@ import java.util.Arrays; import javafx.scene.shape.TriangleMesh; import static org.junit.Assert.*; + +import javafx.scene.shape.VertexFormat; import org.junit.Test; public class TriangleMeshTest { @@ -446,4 +448,144 @@ TriangleMesh buildTriangleMesh(int subDivX, int subDivY) { return triangleMesh; } + + /** + * Test of setColors method, of class TriangleMesh. + */ + @Test + public void testSetColors_4args() { + int divX = 10; + int divY = 10; + TriangleMesh instance = buildColoredTriangleMesh(divX, divY); // 121 colors + float colors[] = { + 1, 1, 1, 1, + 1, 1, -1, 1, + 1, -1, 1, 1, + 1, -1, -1, 1, + -1, 1, 1, 1, + -1, 1, -1, 1, + -1, -1, 1, 1, + -1, -1, -1, 1,}; + float[] expecteds = new float[colors.length]; + int index = 4; + int start = 0; + int length = colors.length; + instance.getColors().set(index, colors, start, length); + assertArrayEquals(instance.getColors().toArray(index, expecteds, length), colors, 1e-3f); + } + + /** + * Test setColors with illegal value, of class TriangleMesh. + */ + @Test(expected = ArrayIndexOutOfBoundsException.class) + public void testSetColors_4argsIllegalArgument() { + int divX = 10; + int divY = 10; + TriangleMesh instance = buildColoredTriangleMesh(divX, divY); // 121 colors + float colors[] = { + 1, 1, 1, 1, + 1, 1, -1, 1, + 1, -1, 1, 1, + 1, -1, -1, 1, + -1, 1, 1, 1, + -1, 1, -1, 1, + -1, -1, 1, 1, + -1, -1, -1, 1,}; + float[] expecteds = instance.getColors().toArray(null); + int length = colors.length; + instance.getColors().set(-1, colors, -1, length); + // colors should not change + assertArrayEquals(instance.getColors().toArray(null), expecteds, 1e-3f); + } + + /** + * Test setColors with index argument out of range of the + * setColors method, of class TriangleMesh. + */ + @Test(expected = ArrayIndexOutOfBoundsException.class) + public void testSetColors_4argsIndexOutOfRange() { + int divX = 10; + int divY = 10; + TriangleMesh instance = buildColoredTriangleMesh(divX, divY); // 121 colors + float colors[] = { + 1, 1, 1, 1, + 1, 1, -1, 1, + 1, -1, 1, 1, + 1, -1, -1, 1, + -1, 1, 1, 1, + -1, 1, -1, 1, + -1, -1, 1, 1, + -1, -1, -1, 1,}; + float[] expecteds = instance.getColors().toArray(null); + int start = 0; + int length = colors.length; + instance.getColors().set(120 * 4, colors, start, length); + // colors should not change + assertArrayEquals(instance.getColors().toArray(null), expecteds, 1e-3f); + } + + /** + * Test setColors with start argument out of range of + * the setColors method, of class TriangleMesh. + */ + @Test(expected = ArrayIndexOutOfBoundsException.class) + public void testSetColors_4argsStartOutOfRange() { + int divX = 10; + int divY = 10; + TriangleMesh instance = buildColoredTriangleMesh(divX, divY); // 121 colors + float colors[] = { + 1, 1, 1, 1, + 1, 1, -1, 1, + 1, -1, 1, 1, + 1, -1, -1, 1, + -1, 1, 1, 1, + -1, 1, -1, 1, + -1, -1, 1, 1, + -1, -1, -1, 1,}; + float[] expecteds = instance.getColors().toArray(null); + int index = 4; + int length = colors.length; + instance.getColors().set(index, colors, 1, length); + // colors should not change + assertArrayEquals(instance.getColors().toArray(null), expecteds, 1e-3f); + } + + /** + * Test the vertex format length (point, texcoord, color, and face) of the colored test TriangleMesh. + */ + @Test + public void testVertexFormatOfColoredTriangleMesh() { + TriangleMesh triMesh = new TriangleMesh(VertexFormat.POINT_TEXCOORD_COLOR); + // x, y, z + assertEquals(3, triMesh.getPointElementSize()); + // u, v + assertEquals(2, triMesh.getTexCoordElementSize()); + // red, green, blue, alpha + assertEquals(4, triMesh.getColorElementSize()); + + // 3 point indices, 3 texCoord indices, and 3 color indices per triangle + assertEquals(9, triMesh.getFaceElementSize()); + } + + TriangleMesh buildColoredTriangleMesh(int subDivX, int subDivY) { + TriangleMesh triangleMesh = new TriangleMesh(VertexFormat.POINT_TEXCOORD_COLOR); + final int pointSize = triangleMesh.getPointElementSize(); + final int texCoordSize = triangleMesh.getTexCoordElementSize(); + final int colorSize = triangleMesh.getColorElementSize(); + final int faceSize = triangleMesh.getFaceElementSize(); + int numDivX = subDivX + 1; + int numVerts = (subDivY + 1) * numDivX; + float points[] = new float[numVerts * pointSize]; + float texCoords[] = new float[numVerts * texCoordSize]; + float colors[] = new float[numVerts * colorSize]; + int faceCount = subDivX * subDivY * 2; + int faces[] = new int[faceCount * faceSize]; + + triangleMesh.getPoints().setAll(points); + triangleMesh.getTexCoords().setAll(texCoords); + triangleMesh.getColors().setAll(colors); + triangleMesh.getFaces().setAll(faces); + + return triangleMesh; + } } diff --git a/tests/system/src/test/java/test/com/sun/prism/impl/PNTMeshVertexBufferLengthTest.java b/tests/system/src/test/java/test/com/sun/prism/impl/PNTMeshVertexBufferLengthTest.java index 0412f24c0b5..3eb39fb4990 100644 --- a/tests/system/src/test/java/test/com/sun/prism/impl/PNTMeshVertexBufferLengthTest.java +++ b/tests/system/src/test/java/test/com/sun/prism/impl/PNTMeshVertexBufferLengthTest.java @@ -67,7 +67,7 @@ public class PNTMeshVertexBufferLengthTest { private static final int SLEEP_TIME = 1000; // Size of a vertex - private static final int VERTEX_SIZE = 9; + private static final int VERTEX_SIZE = 13; private static final float meshScale = 15; private static final float minX = -10; @@ -98,11 +98,14 @@ private static void buildTriangleMesh(MeshView meshView, final int pointSize = 3; final int normalSize = 3; final int texCoordSize = 2; - final int faceSize = 9; // 3 point indices, 3 normal indices and 3 texCoord indices per triangle + final int colorSize = 4; + // 3 point indices, 3 normal indices, 3 texCoord indices, and 3 color indices per triangle + final int faceSize = 12; int numDivX = subDivX + 1; int numVerts = (subDivY + 1) * numDivX; float points[] = new float[numVerts * pointSize]; float texCoords[] = new float[numVerts * texCoordSize]; + float colors[] = new float[numVerts * colorSize]; int faceCount = subDivX * subDivY * 2; float normals[] = new float[faceCount * normalSize]; int faces[] = new int[faceCount * faceSize]; @@ -121,6 +124,11 @@ private static void buildTriangleMesh(MeshView meshView, index = y * numDivX * texCoordSize + (x * texCoordSize); texCoords[index] = dx; texCoords[index + 1] = dy; + index = y * numDivX * colorSize + (x * colorSize); + colors[index] = dx; + colors[index + 1] = 1F - dy; + colors[index + 2] = Math.max(0F, dy - dx); + colors[index + 3] = 1F; } } @@ -141,6 +149,10 @@ private static void buildTriangleMesh(MeshView meshView, int tc01 = tc00 + 1; int tc10 = tc00 + numDivX; int tc11 = tc10 + 1; + int vc00 = y * numDivX + x; + int vc01 = vc00 + 1; + int vc10 = vc00 + numDivX; + int vc11 = vc10 + 1; int ii = p00 * 3; triPoints[0].x = points[ii]; @@ -165,12 +177,15 @@ private static void buildTriangleMesh(MeshView meshView, faces[index + 0] = p00; faces[index + 1] = normalCount; faces[index + 2] = tc00; - faces[index + 3] = p10; - faces[index + 4] = normalCount; - faces[index + 5] = tc10; - faces[index + 6] = p11; - faces[index + 7] = normalCount++; - faces[index + 8] = tc11; + faces[index + 3] = vc00; + faces[index + 4] = p10; + faces[index + 5] = normalCount; + faces[index + 6] = tc10; + faces[index + 7] = vc10; + faces[index + 8] = p11; + faces[index + 9] = normalCount++; + faces[index + 10] = tc11; + faces[index + 11] = vc11; index += faceSize; ii = p11 * 3; @@ -194,20 +209,24 @@ private static void buildTriangleMesh(MeshView meshView, faces[index + 0] = p11; faces[index + 1] = normalCount; faces[index + 2] = tc11; - faces[index + 3] = p01; - faces[index + 4] = normalCount; - faces[index + 5] = tc01; - faces[index + 6] = p00; - faces[index + 7] = normalCount++; - faces[index + 8] = tc00; + faces[index + 3] = vc11; + faces[index + 4] = p01; + faces[index + 5] = normalCount; + faces[index + 6] = tc01; + faces[index + 7] = vc01; + faces[index + 8] = p00; + faces[index + 9] = normalCount++; + faces[index + 10] = tc00; + faces[index + 11] = vc00; } } - TriangleMesh triangleMesh = new TriangleMesh(VertexFormat.POINT_NORMAL_TEXCOORD); + TriangleMesh triangleMesh = new TriangleMesh(VertexFormat.POINT_NORMAL_TEXCOORD_COLOR); triangleMesh.getPoints().setAll(points); triangleMesh.getNormals().setAll(normals); triangleMesh.getTexCoords().setAll(texCoords); + triangleMesh.getColors().setAll(colors); triangleMesh.getFaces().setAll(faces); meshView.setMesh(triangleMesh); } @@ -235,7 +254,7 @@ public void init() { @Override public void start(Stage primaryStage) throws Exception { primaryStage.setTitle("PNTMeshVertexBufferLengthTest"); - TriangleMesh triangleMesh = new TriangleMesh(VertexFormat.POINT_NORMAL_TEXCOORD); + TriangleMesh triangleMesh = new TriangleMesh(VertexFormat.POINT_NORMAL_TEXCOORD_COLOR); meshView = new MeshView(triangleMesh); Group rotateGrp = new Group(meshView); rotateGrp.setRotate(-30); @@ -309,7 +328,7 @@ public void testMeshWithOneDiv() throws InterruptedException { // mesh with 6 vertices (2 triangles) assertEquals(6, BaseMeshShim.test_getNumberOfVertices(baseMesh)); // vertexBuffer started with 4 vertices and grew by 6 (since 12.5% or 1/8th - // of 4 is less than 6). Size of vertex is 9 floats (10 * VERTEX_SIZE = 90) + // of 4 is less than 6). Size of vertex is 12 floats (10 * VERTEX_SIZE = 120) assertEquals(10 * VERTEX_SIZE, BaseMeshShim.test_getVertexBufferLength(baseMesh)); } @@ -328,7 +347,7 @@ public void testMeshWithTwoDiv() throws InterruptedException { // mesh with 24 vertices (8 triangles) assertEquals(24, BaseMeshShim.test_getNumberOfVertices(baseMesh)); // vertexBuffer started with 9 vertices and grew by 6, 3 times, to a - // capacity of 27 vertices (27 * VERTEX_SIZE = 243) + // capacity of 27 vertices (27 * VERTEX_SIZE = 324) assertEquals(27 * VERTEX_SIZE, BaseMeshShim.test_getVertexBufferLength(baseMesh)); } @@ -348,7 +367,7 @@ public void testMeshWithThreeDiv() throws InterruptedException { assertEquals(294, BaseMeshShim.test_getNumberOfVertices(baseMesh)); // vertexBuffer started with 64 vertices and grew by 6 the first time // then crossed over to 12.5% growth rate at each subsequence increase - // to a capacity of 325 vertices (325 * VERTEX_SIZE = 2925) + // to a capacity of 325 vertices (325 * VERTEX_SIZE = 3900) assertEquals(325 * VERTEX_SIZE, BaseMeshShim.test_getVertexBufferLength(baseMesh)); } @@ -368,7 +387,7 @@ public void testMeshWithFiveDiv() throws InterruptedException { assertEquals(15000, BaseMeshShim.test_getNumberOfVertices(baseMesh)); // vertexBuffer started with 2601 vertices and grew at 12.5% growth rate // at each subsequence increase to a capacity of 15201 vertices - // (15201 * VERTEX_SIZE = 136809) + // (15201 * VERTEX_SIZE = 182412) assertEquals(15201 * VERTEX_SIZE, BaseMeshShim.test_getVertexBufferLength(baseMesh)); } diff --git a/tests/system/src/test/java/test/robot/test3d/TriangleMeshPTCValidationTest.java b/tests/system/src/test/java/test/robot/test3d/TriangleMeshPTCValidationTest.java new file mode 100644 index 00000000000..6b6abc6f453 --- /dev/null +++ b/tests/system/src/test/java/test/robot/test3d/TriangleMeshPTCValidationTest.java @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package test.robot.test3d; + +import javafx.application.ConditionalFeature; +import javafx.application.Platform; +import javafx.scene.AmbientLight; +import javafx.scene.Group; +import javafx.scene.PerspectiveCamera; +import javafx.scene.Scene; +import javafx.scene.paint.Color; +import javafx.scene.paint.PhongMaterial; +import javafx.scene.shape.MeshView; +import javafx.scene.shape.TriangleMesh; +import javafx.scene.shape.VertexFormat; +import javafx.stage.Stage; +import org.junit.Before; +import org.junit.Test; +import test.robot.testharness.VisualTestBase; + +import java.util.stream.Stream; + +import static org.junit.Assume.assumeTrue; + +/** + * Basic TriangleMesh color validation. + */ +public class TriangleMeshPTCValidationTest extends VisualTestBase { + private Stage testStage; + private Scene testScene; + private MeshView meshView; + private TriangleMesh triMesh; + private PhongMaterial material; + private Group root; + + private static final double TOLERANCE = 0.07; + private static final int WIDTH = 800; + private static final int HEIGHT = 800; + private Color bgColor = Color.rgb(10, 10, 40); + + @Before + public void setupEach() { + assumeTrue(Platform.isSupported(ConditionalFeature.SCENE3D)); + } + + @Test(timeout = 15000) + public void testInvalidColorsLength() { + runAndWait(() -> { + testStage = getStage(); + testStage.setTitle("TriangleMesh PTC Validation Test"); + + // Intentionally set depth buffer to false to reduce test complexity + testScene = new Scene(buildScene(), WIDTH, HEIGHT, true); + testScene.setFill(bgColor); + addCamera(testScene); + buildSquare(); + // set invalid colors + triMesh.getColors().setAll(0f, 0f, 0f /*, 1.0f */); + testStage.setScene(testScene); + testStage.show(); + }); + waitFirstFrame(); + runAndWait(() -> { + + // Rendering nothing. Should receive warning from validatePoints + Color color = getColor(testScene, WIDTH / 3, WIDTH / 3); + assertColorEquals(bgColor, color, TOLERANCE); + }); + } + + @Test(timeout = 15000) + public void testColorsLengthChange() { + runAndWait(() -> { + testStage = getStage(); + testStage.setTitle("TriangleMesh PTC Validation Test"); + + // Intentionally set depth buffer to false to reduce test complexity + testScene = new Scene(buildScene(), WIDTH, HEIGHT, true); + testScene.setFill(bgColor); + addCamera(testScene); + buildSquare(); + testStage.setScene(testScene); + testStage.show(); + }); + waitFirstFrame(); + runAndWait(() -> { + + Color color = getColor(testScene, WIDTH / 3, WIDTH / 3); + assertColorEquals(Color.RED, color, TOLERANCE); + + // Valid change of points + triMesh.getPoints().setAll(0, 0, 1); + }); + waitFirstFrame(); + runAndWait(() -> { + + // Rendering nothing because faces is invalid. + // Should receive warning from validateFaces + Color color = getColor(testScene, WIDTH / 3, WIDTH / 3); + assertColorEquals(bgColor, color, TOLERANCE); + }); + } + + @Test(timeout = 15000) + public void testInvisibleMeshUpdateColors() { + runAndWait(() -> { + testStage = getStage(); + testStage.setTitle("TriangleMesh PTC Validation Test"); + + // Intentionally set depth buffer to false to reduce test complexity + testScene = new Scene(buildScene(), WIDTH, HEIGHT, true); + testScene.setFill(bgColor); + addCamera(testScene); + buildSquare(); + testStage.setScene(testScene); + testStage.show(); + }); + waitFirstFrame(); + runAndWait(() -> { + + // Rendering 2 Triangles that form a square (First triangle is red) + Color color = getColor(testScene, WIDTH / 3, WIDTH / 3); + assertColorEquals(Color.RED, color, TOLERANCE); + + // Second triangle is blue + color = getColor(testScene, WIDTH / 2 + 10, WIDTH / 2 + 10); + assertColorEquals(Color.BLUE, color, TOLERANCE); + + // set normal with invisible vertex colors. + triMesh.getColors().setAll(1F, 1F, 1F, 0F, 1F, 1F, 1F, 0F); + }); + waitFirstFrame(); + runAndWait(() -> { + + // Rendering nothing. No warning. + Color color = getColor(testScene, WIDTH / 3, WIDTH / 3); + assertColorEquals(bgColor, color, TOLERANCE); + + }); + } + + @Test(timeout = 15000) + public void testDegeneratedMeshUpdatePoints() { + runAndWait(() -> { + testStage = getStage(); + testStage.setTitle("TriangleMesh PTC Validation Test"); + + // Intentionally set depth buffer to false to reduce test complexity + testScene = new Scene(buildScene(), WIDTH, HEIGHT, true); + testScene.setFill(bgColor); + addCamera(testScene); + buildSquare(); + testStage.setScene(testScene); + testStage.show(); + }); + waitFirstFrame(); + runAndWait(() -> { + + // Rendering 2 Triangles that form a square (First triangle is red) + Color color = getColor(testScene, WIDTH / 3, WIDTH / 3); + assertColorEquals(Color.RED, color, TOLERANCE); + + // Second triangle is blue + color = getColor(testScene, WIDTH / 2 + 10, WIDTH / 2 + 10); + assertColorEquals(Color.BLUE, color, TOLERANCE); + + // set points that casuses a degenerated triangle + triMesh.getPoints().setAll( + 0.5f, -1.5f, 0f, + 0.5f, -1.5f, 0f, + 0.5f, 1.5f, 0f, + 0.5f, -1.5f, 0f); + }); + waitFirstFrame(); + runAndWait(() -> { + + Color color = getColor(testScene, WIDTH / 2 + 10, WIDTH / 2 + 10); + assertColorEquals(bgColor, color, TOLERANCE); + + }); + } + + void buildSquare() { + + float points[] = { + 1.5f, 1.5f, 0f, + 1.5f, -1.5f, 0f, + -1.5f, 1.5f, 0f, + -1.5f, -1.5f, 0f + }; + + float colors[] = { + 1f, 0f, 0f, 1f, 0f, 0f, 1f, 1f // Only the first normal is refered. + }; + + float texCoords[] = {0, 0 + }; + + int faces[] = { + 2, 0, 0, 1, 0, 0, 3, 0, 0, + 2, 0, 1, 0, 0, 1, 1, 0, 1,}; + + triMesh.getPoints().setAll(points); + triMesh.getColors().setAll(colors); + triMesh.getTexCoords().setAll(texCoords); + triMesh.getFaces().setAll(faces); + } + + private Group buildScene() { + triMesh = new TriangleMesh(VertexFormat.POINT_TEXCOORD_COLOR); + material = new PhongMaterial(); + material.setDiffuseColor(Color.WHITE); + meshView = new MeshView(triMesh); + meshView.setMaterial(material); + meshView.setScaleX(200); + meshView.setScaleY(200); + meshView.setScaleZ(200); + meshView.setTranslateX(400); + meshView.setTranslateY(400); + meshView.setTranslateZ(10); + + root = new Group(meshView); + return root; + } + + private PerspectiveCamera addCamera(Scene scene) { + PerspectiveCamera perspectiveCamera = new PerspectiveCamera(false); + scene.setCamera(perspectiveCamera); + return perspectiveCamera; + } +}