我有一个示例3D应用程序(通过参考Javafx样本3DViewer构建),它有一个通过布局Boxes和Panes创建的表格:
表格居中于wrt(0,0,0)坐标,摄像机最初位于-z位置。
它具有基于物体的相机z位置的放大/缩小。 在放大/缩小对象的边界时,父对象增加/减少,即面部区域增大/减小。因此,当面部区域太小时,我们的想法是在我们有更多区域(总是限制在脸部内)和较少文本或没有文本时放置更多文本。我能够使用这个节点层次结构来做到这一点:
并在每次放大/缩小时按照Box调整窗格大小(以及管理vBox及其中的文本数量)。
现在的问题是,只要文本第一次添加到vBox,表boundsInParent就会给出不正确的结果(table image显示顶部的boundingBox)。在进一步放大/缩小时,会给出正确的boundingBox并且不会消失。
以下是UIpane3D类:
public class UIPane3D extends Pane
{
VBox textPane;
ArrayList<String> infoTextKeys = new ArrayList<>();
ArrayList<Text> infoTextValues = new ArrayList<>();
Rectangle bgCanvasRect = null;
final double fontSize = 16.0;
public UIPane3D() {
setMouseTransparent(true);
textPane = new VBox(2.0)
}
public void updateContent() {
textPane.getChildren().clear();
getChildren().clear();
for (Text textNode : infoTextValues) {
textPane.getChildren().add(textNode);
textPane.autosize();
if (textPane.getHeight() > getHeight()) {
textPane.getChildren().remove(textNode);
textPane.autosize();
break;
}
}
textPane.setTranslateY(getHeight() / 2 - textPane.getHeight() / 2.0);
bgCanvasRect = new Rectangle(getWidth(), getHeight());
bgCanvasRect.setFill(Color.web(Color.BURLYWOOD.toString(), 0.10));
bgCanvasRect.setVisible(true);
getChildren().addAll(bgCanvasRect, textPane);
}
public void resetInfoTextMap()
{
if (infoTextKeys != null || infoTextValues != null)
{
try
{
infoTextKeys.clear();
infoTextValues.clear();
} catch (Exception e){e.printStackTrace();}
}
}
public void updateInfoTextMap(String pKey, String pValue)
{
int index = -1;
boolean objectFound = false;
for (String string : infoTextKeys)
{
index++;
if(string.equals(pKey))
{
objectFound = true;
break;
}
}
if(objectFound)
{
infoTextValues.get(index).setText(pValue.toUpperCase());
}
else
{
if (pValue != null)
{
Text textNode = new Text(pValue.toUpperCase());
textNode.setFont(Font.font("Consolas", FontWeight.BLACK, FontPosture.REGULAR, fontSize));
textNode.wrappingWidthProperty().bind(widthProperty());
textNode.setTextAlignment(TextAlignment.CENTER);
infoTextKeys.add(pKey);
infoTextValues.add(textNode);
}
}
}
}
在所有操作之后最后调用的代码:
public void refreshBoundingBox()
{
if(boundingBox != null)
{
root3D.getChildren().remove(boundingBox);
}
PhongMaterial blueMaterial = new PhongMaterial();
blueMaterial.setDiffuseColor(Color.web(Color.CRIMSON.toString(), 0.25));
Bounds tableBounds = table.getBoundsInParent();
boundingBox = new Box(tableBounds.getWidth(), tableBounds.getHeight(), tableBounds.getDepth());
boundingBox.setMaterial(blueMaterial);
boundingBox.setTranslateX(tableBounds.getMinX() + tableBounds.getWidth()/2.0);
boundingBox.setTranslateY(tableBounds.getMinY() + tableBounds.getHeight()/2.0);
boundingBox.setTranslateZ(tableBounds.getMinZ() + tableBounds.getDepth()/2.0);
boundingBox.setMouseTransparent(true);
root3D.getChildren().add(boundingBox);
}
两件事:
第一次添加文本时,table3D的boundsInParent未正确更新。
将文本放在3D节点上的正确方法是什么?我不得不操纵很多东西来根据需要提供文本。
分享代码here。
答案 0 :(得分:3)
关于第一个问题,关于滚动新文本项后可以注意到的“跳转”:
在深入研究code之后,我注意到UIPane3D
有一个VBox textPane
,其中包含不同的Text节点。每次调用updateContent
时,它都会尝试添加文本节点,但会检查vbox的高度是否始终低于窗格的高度,否则节点将被删除:
for (Text textNode : infoTextValues) {
textPane.getChildren().add(textNode);
textPane.autosize();
if (textPane.getHeight() > getHeight()) {
textPane.getChildren().remove(textNode);
textPane.autosize();
break;
}
}
虽然这基本上是正确的,但是当你向场景中添加一个节点时,你不能立即得到textPane.getHeight()
,因为它尚未布局,你必须等到下一个脉冲。这就是为什么下次滚动时,高度正确并且边界框放置得很好。
强制布局并获得textNode
正确高度的一种方法是强制使用css和布局:
for (Text textNode : infoTextValues) {
textPane.getChildren().add(textNode);
// force css and layout
textPane.applyCss();
textPane.layout();
textPane.autosize();
if (textPane.getHeight() > getHeight()) {
textPane.getChildren().remove(textNode);
textPane.autosize();
break;
}
}
请注意:
此方法[
applyCss
]通常不需要直接调用,但可以与Parent.layout()
一起使用,以便在下一个脉冲之前调整节点大小,或者如果场景不在舞台中
关于第二个问题,关于将文本添加到3D形状的不同解决方案。
实际上,将(2D)文本放在3D形状之上是非常困难的,并且需要复杂的数学(顺便说一下,在项目中完成得非常好)。
有一种方法可以避免直接使用2D节点。
正好在以前的question中,我“写入”了一个图像,后来我用作3D形状的材质漫反射贴图。
内置3D Box
将相同的图像放入每张脸,因此不起作用。我们可以实现3D棱镜,或者我们可以使用FXyz3D library中的CuboidMesh
节点。
替换Box
中的UIPaneBoxGroup
:
final CuboidMesh contentShape;
UIPane3D displaypane = null;
PhongMaterial shader = new PhongMaterial();
final Color pColor;
public UIPaneBoxGroup(final double pWidth, final double pHeight, final double pDepth, final Color pColor) {
contentShape = new CuboidMesh(pWidth, pHeight, pDepth);
this.pColor = pColor;
contentShape.setMaterial(shader);
getChildren().add(contentShape);
addInfoUIPane();
}
并添加generateNet
方法:
private Image generateNet(String string) {
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
Label label5 = new Label(string);
label5.setFont(Font.font("Consolas", FontWeight.BLACK, FontPosture.REGULAR, 40));
GridPane.setHalignment(label5, HPos.CENTER);
grid.add(label5, 3, 1);
double w = contentShape.getWidth() * 10; // more resolution
double h = contentShape.getHeight() * 10;
double d = contentShape.getDepth() * 10;
final double W = 2 * d + 2 * w;
final double H = 2 * d + h;
ColumnConstraints col1 = new ColumnConstraints();
col1.setPercentWidth(d * 100 / W);
ColumnConstraints col2 = new ColumnConstraints();
col2.setPercentWidth(w * 100 / W);
ColumnConstraints col3 = new ColumnConstraints();
col3.setPercentWidth(d * 100 / W);
ColumnConstraints col4 = new ColumnConstraints();
col4.setPercentWidth(w * 100 / W);
grid.getColumnConstraints().addAll(col1, col2, col3, col4);
RowConstraints row1 = new RowConstraints();
row1.setPercentHeight(d * 100 / H);
RowConstraints row2 = new RowConstraints();
row2.setPercentHeight(h * 100 / H);
RowConstraints row3 = new RowConstraints();
row3.setPercentHeight(d * 100 / H);
grid.getRowConstraints().addAll(row1, row2, row3);
grid.setPrefSize(W, H);
grid.setBackground(new Background(new BackgroundFill(pColor, CornerRadii.EMPTY, Insets.EMPTY)));
new Scene(grid);
return grid.snapshot(null, null);
}
现在可以删除所有2D相关代码(包括displaypane
),并在滚动事件后获取图像:
public void refreshBomUIPane() {
Image net = generateNet(displaypane.getText());
shader.setDiffuseMap(net);
}
UIPane3D
中的位置:
public String getText() {
return infoTextKeys.stream().collect(Collectors.joining("\n"));
}
我还删除了边界框以获取此图片:
我没有玩过可以添加到VBox的文本节点的数量,字体大小以及避免在每个滚动上生成图像的策略:只有当文本发生变化时才应该这样做。因此,目前的方法非常缓慢,但可以显着改善,因为每个盒子只有三个可能的图像。