Issue
Many 3d programs uses an outline to hint the user when 3d object is selected .
is there a way to mimic that behavior in javafx ?
Solution
Outline with a scaled 3d object with front faces culled
In this aproach . A 3d object is instansiated with the class type of PickResult
node when mouse is entered on a Group
node , that object has the same sizes of the picked one and the same translations ( in this case is just x axis ) but is slightly scaled (1.1). So , the 3d object is bigger and it is in the same spot but is not overlaping the shape3d underneath beacuse any face looking toward the camera is culled with Culface.FRONT enum in Shape3d.setCullFace()
. Finally , when mouse exits pickresult node the 3d object is removed from group's children allowing next iterations . This is a single class functional javafx app you can try
App.java
public class App extends Application {
private Shape3D outLine;
@Override
public void start(Stage stage) {
Shape3D sphere = new Sphere(0.45);
PhongMaterial sMaterial = new PhongMaterial(Color.CORAL);
sphere.setMaterial(sMaterial);
sphere.setTranslateX(-1.1);
Shape3D box = new Box(0.9, 0.9, 0.9);
box.setTranslateX(1.1);
box.setMaterial(new PhongMaterial(Color.PALEGREEN));
Shape3D cylinder = new Cylinder(0.45, 0.8);
cylinder.setMaterial(new PhongMaterial(Color.PALEVIOLETRED));
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setTranslateZ(-5.5);
Group group3d = new Group(camera, box, sphere, cylinder);
Scene scene = new Scene(group3d, 640, 480, true, SceneAntialiasing.BALANCED);
group3d.setOnMouseExited((t) -> {
group3d.getChildren().remove(outLine);
});
group3d.setOnMouseEntered((t) -> {
PickResult pickResult = t.getPickResult();
Node intersectedNode = pickResult.getIntersectedNode();
if (intersectedNode instanceof Sphere) {
outLine = new Sphere(((Sphere) intersectedNode).getRadius());
outLine.setTranslateX(intersectedNode.getTranslateX());
group3d.getChildren().add(outLine);
outLine.setCullFace(CullFace.FRONT);
outLine.setScaleX(1.1);
outLine.setScaleY(1.1);
outLine.setScaleZ(1.1);
}
if (intersectedNode instanceof Cylinder) {
Cylinder c = (Cylinder) intersectedNode;
outLine = new Cylinder(c.getRadius(), c.getHeight(), c.getDivisions());
outLine.setTranslateX(c.getTranslateX());
group3d.getChildren().add(outLine);
outLine.setCullFace(CullFace.FRONT);
outLine.setScaleX(1.1);
outLine.setScaleY(1.1);
outLine.setScaleZ(1.1);
}
if (intersectedNode instanceof Box) {
Box b = (Box) intersectedNode;
outLine = new Box(b.getWidth(), b.getHeight(), b.getDepth());
outLine.setTranslateX(b.getTranslateX());
group3d.getChildren().add(outLine);
outLine.setCullFace(CullFace.FRONT);
outLine.setScaleX(1.1);
outLine.setScaleY(1.1);
outLine.setScaleZ(1.1);
}
});
scene.setCamera(camera);
scene.setFill(Color.AQUA);
stage.setScene(scene);
stage.setTitle("outline javafx");
stage.show();
}
public static void main(String[] args) {
launch();
}
}
Note Shape3d objects scale from its very center . If this method will be implemented with custom MeshView objects , those meshes needs to scale at its center as well
Answered By - Giovanni Contreras
Answer Checked By - Marie Seifert (JavaFixing Admin)