1 /* 2 * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.javafx.sg.prism; 27 28 import javafx.application.ConditionalFeature; 29 import javafx.application.Platform; 30 import javafx.scene.shape.CullFace; 31 import javafx.scene.shape.DrawMode; 32 import com.sun.javafx.geom.Vec3d; 33 import com.sun.javafx.geom.transform.Affine3D; 34 import com.sun.prism.Graphics; 35 import com.sun.prism.Material; 36 import com.sun.prism.MeshView; 37 import com.sun.prism.ResourceFactory; 38 39 /** 40 * TODO: 3D - Need documentation 41 */ 42 public abstract class NGShape3D extends NGNode { 43 private NGPhongMaterial material; 44 private DrawMode drawMode; 45 private CullFace cullFace; 46 private boolean materialDirty = false; 47 private boolean drawModeDirty = false; 48 NGTriangleMesh mesh; 49 private MeshView meshView; 50 51 public void setMaterial(NGPhongMaterial material) { 52 this.material = material; 53 materialDirty = true; 54 visualsChanged(); 55 } 56 public void setDrawMode(Object drawMode) { 57 this.drawMode = (DrawMode) drawMode; 58 drawModeDirty = true; 59 visualsChanged(); 60 } 61 62 public void setCullFace(Object cullFace) { 63 this.cullFace = (CullFace) cullFace; 64 visualsChanged(); 65 } 66 67 void invalidate() { 68 meshView = null; 69 visualsChanged(); 70 } 71 72 private void renderMeshView(Graphics g) { 73 74 //validate state 75 g.setup3DRendering(); 76 77 ResourceFactory rf = g.getResourceFactory(); 78 79 if (meshView == null && mesh != null) { 80 meshView = rf.createMeshView(mesh.createMesh(rf)); 81 materialDirty = drawModeDirty = true; 82 } 83 84 if (meshView == null || !mesh.validate()) { 85 return; 86 } 87 88 Material mtl = material.createMaterial(rf); 89 if (materialDirty) { 90 meshView.setMaterial(mtl); 91 materialDirty = false; 92 } 93 94 // NOTE: Always check determinant in case of mirror transform. 95 int cullingMode = cullFace.ordinal(); 96 if (cullFace.ordinal() != MeshView.CULL_NONE 97 && g.getTransformNoClone().getDeterminant() < 0) { 98 cullingMode = cullingMode == MeshView.CULL_BACK 99 ? MeshView.CULL_FRONT : MeshView.CULL_BACK; 100 } 101 meshView.setCullingMode(cullingMode); 102 103 if (drawModeDirty) { 104 meshView.setWireframe(drawMode == DrawMode.LINE); 105 drawModeDirty = false; 106 } 107 108 // Setup lights 109 int pointLightIdx = 0; 110 if (g.getLights() == null || g.getLights()[0] == null) { 111 // If no lights are in scene apply default light. Default light 112 // is a single point white point light at camera eye position. 113 meshView.setAmbientLight(0.0f, 0.0f, 0.0f); 114 Vec3d cameraPos = g.getCameraNoClone().getPositionInWorld(null); 115 meshView.setPointLight(pointLightIdx++, 116 (float)cameraPos.x, 117 (float)cameraPos.y, 118 (float)cameraPos.z, 119 1.0f, 1.0f, 1.0f, 1.0f); 120 } else { 121 float ambientRed = 0.0f; 122 float ambientBlue = 0.0f; 123 float ambientGreen = 0.0f; 124 125 for (int i = 0; i < g.getLights().length; i++) { 126 NGLightBase lightBase = g.getLights()[i]; 127 if (lightBase == null) { 128 // The array of lights can have nulls 129 break; 130 } else if (lightBase.affects(this)) { 131 float rL = lightBase.getColor().getRed(); 132 float gL = lightBase.getColor().getGreen(); 133 float bL = lightBase.getColor().getBlue(); 134 /* TODO: 3D 135 * There is a limit on the number of lights that can affect 136 * a 3D shape. (Currently we simply select the first 3) 137 * Thus it is important to select the most relevant lights. 138 * 139 * One such way would be to sort lights according to 140 * intensity, which becomes especially relevant when lights 141 * are attenuated. Only the most intense set of lights 142 * would be set. 143 * The approximate intesity a light will have on a given 144 * shape, could be defined by: 145 */ 146 // // Where d is distance from point light 147 // float attenuationFactor = 1/(c + cL * d + cQ * d * d); 148 // float intensity = rL * 0.299f + gL * 0.587f + bL * 0.114f; 149 // intensity *= attenuationFactor; 150 if (lightBase instanceof NGPointLight) { 151 NGPointLight light = (NGPointLight)lightBase; 152 if (rL != 0.0f || gL != 0.0f || bL != 0.0f) { 153 Affine3D lightWT = light.getWorldTransform(); 154 meshView.setPointLight(pointLightIdx++, 155 (float)lightWT.getMxt(), 156 (float)lightWT.getMyt(), 157 (float)lightWT.getMzt(), 158 rL, gL, bL, 1.0f); 159 } 160 } else if (lightBase instanceof NGAmbientLight) { 161 // Accumulate ambient lights 162 ambientRed += rL; 163 ambientGreen += gL; 164 ambientBlue += bL; 165 } 166 } 167 } 168 ambientRed = saturate(ambientRed); 169 ambientGreen = saturate(ambientGreen); 170 ambientBlue = saturate(ambientBlue); 171 meshView.setAmbientLight(ambientRed, ambientGreen, ambientBlue); 172 } 173 // TODO: 3D Required for D3D implementation of lights, which is limited to 3 174 while (pointLightIdx < 3) { 175 // Reset any previously set lights 176 meshView.setPointLight(pointLightIdx++, 0, 0, 0, 0, 0, 0, 0); 177 } 178 179 meshView.render(g); 180 } 181 182 // Clamp between [0, 1] 183 private static float saturate(float value) { 184 return value < 1.0f ? ((value < 0.0f) ? 0.0f : value) : 1.0f; 185 } 186 187 public void setMesh(NGTriangleMesh triangleMesh) { 188 this.mesh = triangleMesh; 189 meshView = null; 190 visualsChanged(); 191 } 192 193 @Override 194 protected void renderContent(Graphics g) { 195 if (!Platform.isSupported(ConditionalFeature.SCENE3D) || 196 material == null || 197 g instanceof com.sun.prism.PrinterGraphics) 198 { 199 return; 200 } 201 renderMeshView(g); 202 } 203 204 // This node requires 3D graphics state for rendering 205 @Override 206 boolean isShape3D() { 207 return true; 208 } 209 210 @Override 211 protected boolean hasOverlappingContents() { 212 return false; 213 } 214 215 @Override 216 public void release() { 217 // TODO: 3D - Need to release native resources 218 // material, mesh and meshview have native backing that need clean up. 219 } 220 }