问题
I want to display a 3D figures on a MeshView in JavaFX. Since I want to build a simple model viewer I created tabs for clarity. One of them contains a group (meshGroup) where I add a SubScene (with the MeshView in a group) on runtime. I'm not able to place that model in the middle of the AnchorPane in full available size. ( I also tried Pane & HBox ). Image
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.Group?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Viewer">
<center>
<TabPane prefHeight="200.0" prefWidth="200.0" tabClosingPolicy="UNAVAILABLE" BorderPane.alignment="CENTER">
<tabs>
<Tab text="Model">
<content>
<AnchorPane>
<children>
<Group fx:id="meshGroup" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>
</content>
</Tab>
<Tab text="Info">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" />
</content>
</Tab>
</tabs>
</TabPane>
</center>
<left>
<ListView prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER" />
</left>
</BorderPane>
The Controller looks like this:
package sample;
import javafx.animation.Interpolator;
import javafx.animation.RotateTransition;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.PointLight;
import javafx.scene.SubScene;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.CullFace;
import javafx.scene.shape.DrawMode;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.TriangleMesh;
import javafx.scene.transform.Rotate;
import javafx.util.Duration;
import java.net.URL;
import java.util.ResourceBundle;
public class Viewer implements Initializable {
@FXML
Group meshGroup;
private PerspectiveCamera camera;
private MeshView meshView;
private static final int VIEWPORT_SIZE = 500;
private static final double MODEL_SCALE_FACTOR = 40;
private static final double MODEL_X_OFFSET = 0;
private static final double MODEL_Y_OFFSET = 0;
private static final double MODEL_Z_OFFSET = VIEWPORT_SIZE * 21;
@Override
public void initialize(URL location, ResourceBundle resources) {
initCamera();
Platform.runLater(() -> showFigure());
}
private void initCamera() {
this.camera = new PerspectiveCamera(true);
this.camera.setNearClip(0.1);
this.camera.setFarClip(10000.0);
this.camera.setTranslateZ(-1000);
}
private Group buildScene() {
Group group = new Group();
meshView.setTranslateX(VIEWPORT_SIZE / 2 + MODEL_X_OFFSET);
meshView.setTranslateY(VIEWPORT_SIZE / 2 * 9.0 / 16 + MODEL_Y_OFFSET);
meshView.setTranslateZ(VIEWPORT_SIZE / 2 + MODEL_Z_OFFSET);
meshView.setScaleX(MODEL_SCALE_FACTOR);
meshView.setScaleY(MODEL_SCALE_FACTOR);
meshView.setScaleZ(MODEL_SCALE_FACTOR);
PointLight pointLight = new PointLight(Color.WHITE);
pointLight.setTranslateZ(VIEWPORT_SIZE / 2);
pointLight.setTranslateY(VIEWPORT_SIZE / 2);
group.getChildren().addAll(meshView, pointLight);
return group;
}
private SubScene createScene3D(Group group) {
SubScene scene3d = new SubScene(group, VIEWPORT_SIZE, VIEWPORT_SIZE * 9.0 / 16);
scene3d.setFill(Color.WHITE);
scene3d.setCamera(this.camera);
scene3d.setPickOnBounds(true);
return scene3d;
}
private void showFigure() {
meshView = buildMesh();
// Add MeshView to Group
Group meshInGroup = buildScene();
// Create SubScene
SubScene subScene = createScene3D(meshInGroup);
// Add subScene to meshGroup
this.meshGroup.getChildren().add(subScene);
RotateTransition rotate = rotate3dGroup(meshInGroup);
this.meshGroup.getChildren().add(createControls(rotate));
}
private HBox createControls(RotateTransition rotateTransition) {
CheckBox cull = new CheckBox("Cull Back");
meshView.cullFaceProperty().bind(
Bindings.when(
cull.selectedProperty())
.then(CullFace.BACK)
.otherwise(CullFace.NONE)
);
CheckBox wireframe = new CheckBox("Wireframe");
meshView.drawModeProperty().bind(
Bindings.when(
wireframe.selectedProperty())
.then(DrawMode.LINE)
.otherwise(DrawMode.FILL)
);
CheckBox rotate = new CheckBox("Rotate");
rotate.selectedProperty().addListener(observable -> {
if (rotate.isSelected()) {
rotateTransition.play();
} else {
rotateTransition.pause();
}
});
HBox controls = new HBox(10, rotate, cull, wireframe);
controls.setPadding(new Insets(10));
return controls;
}
private RotateTransition rotate3dGroup(Group group) {
RotateTransition rotate = new RotateTransition(Duration.seconds(10), group);
rotate.setAxis(Rotate.Y_AXIS);
rotate.setFromAngle(0);
rotate.setToAngle(360);
rotate.setInterpolator(Interpolator.LINEAR);
rotate.setCycleCount(RotateTransition.INDEFINITE);
return rotate;
}
private MeshView buildMesh() {
TriangleMesh mesh = new TriangleMesh();
float hw = 100 / 2f;
float hh = 100 / 2f;
float hd = 100 / 2f;
mesh.getPoints().addAll(
hw, hh, hd,
hw, hh, -hd,
hw, -hh, hd,
hw, -hh, -hd,
-hw, hh, hd,
-hw, hh, -hd,
-hw, -hh, hd,
-hw, -hh, -hd
);
mesh.getTexCoords().addAll(
100, 0,
200, 0,
0, 100,
100, 100,
200, 100,
300, 100,
400, 100,
0, 200,
100, 200,
200, 200,
300, 200,
400, 200,
100, 300,
200, 300
);
mesh.getFaces().addAll(
0, 10, 2, 5, 1, 9,
2, 5, 3, 4, 1, 9,
4, 7, 5, 8, 6, 2,
6, 2, 5, 8, 7, 3,
0, 13, 1, 9, 4, 12,
4, 12, 1, 9, 5, 8,
2, 1, 6, 0, 3, 4,
3, 4, 6, 0, 7, 3,
0, 10, 4, 11, 2, 5,
2, 5, 4, 11, 6, 6,
1, 9, 3, 4, 5, 8,
5, 8, 3, 4, 7, 3
);
return new MeshView(mesh);
}
}
I spent a lot of time on this but I'm not able to find a straight forward way to get rid of the MeshView. I appreciate any feedback.
回答1:
One easy solution to make your subscene resize with its parent is just binding its dimensions to those of the parent.
For instance:
private SubScene createScene3D(Group group) {
SubScene scene3d = new SubScene(group, VIEWPORT_SIZE, VIEWPORT_SIZE, true, SceneAntialiasing.BALANCED);
scene3d.widthProperty().bind(((AnchorPane)meshGroup.getParent()).widthProperty());
scene3d.heightProperty().bind(((AnchorPane)meshGroup.getParent()).heightProperty());
scene3d.setFill(Color.WHITE);
scene3d.setCamera(this.camera);
scene3d.setPickOnBounds(true);
return scene3d;
}
Note I've taken into account the AnchorPane
instead of the Group
where you add the subscene. Also note the use of depth buffer and antialiasing for better rendering.
Now that the subscene takes the whole tab area, you may have to reconsider the initial scaling and translations.
This will just place the box in the middle:
private static final double MODEL_SCALE_FACTOR = 6;
private void initCamera() {
this.camera = new PerspectiveCamera(true);
this.camera.setNearClip(0.1);
this.camera.setFarClip(10000.0);
this.camera.setTranslateZ(-2000);
}
private Group buildScene() {
Group group = new Group();
meshView.setScaleX(MODEL_SCALE_FACTOR);
meshView.setScaleY(MODEL_SCALE_FACTOR);
meshView.setScaleZ(MODEL_SCALE_FACTOR);
PointLight pointLight = new PointLight(Color.WHITE);
pointLight.setTranslateZ(-2*VIEWPORT_SIZE );
pointLight.setTranslateY(-2*VIEWPORT_SIZE );
group.getChildren().addAll(meshView, pointLight);
return group;
}
来源:https://stackoverflow.com/questions/30467440/place-meshview-in-scene