Place MeshVIew in Scene

旧巷老猫 提交于 2019-12-11 02:28:21

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!