Is there a way to paint a 3d texture on a 3d surface like blender ?
Painting a texture in 3d scene
getPickResult().getIntersectedTexCoord()
from mouseEvent will return a point2d wich is an uv coordinate (from 0 to 1 ). In order to map it to the image pixels ; we have to multilpy u & v by image height and width (800 in this case). Once we have pixel coordinates we can paint with pixelWriter.setPixels()
method . the third and fourth arguments in setPixel corresponding to the rectangle that is used to draw , in this case is a 12*12 square . Therefore , an argb array 144 length is needed to fill the square in setPixels
with color values given by colorpicker result .
ImageView and DifuseMap share the same WritableImage that is why painting the Shape3d will update image in imageView as well
This is a single class javafx functional app you can try . you can rotate with right mouse button and painting with left button
public class App extends Application {
private boolean paintMode;
private boolean rotaionMode;
private double anchorX;
private final WritableImage writableImage = new WritableImage(800, 800);
private int[] colorArray;
private PixelWriter pixelWriter = writableImage.getPixelWriter();
@Override
public void start(Stage stage) {
// this loop will paint all writableImage object pixel with Color.WHEAT
for (int i = 0; i < 800; i++) {
for (int j = 0; j < 800; j++) {
pixelWriter.setColor(i, j, Color.WHEAT);
}
}
// giving a starting value to colorPicker
ColorPicker picker = new ColorPicker(Color.AQUAMARINE);
// giving initial valuest to colorArray
colorArray = getColorArray(picker.getValue());
picker.setOnAction(e -> colorArray = getColorArray(picker.getValue()));
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setTranslateZ(-30);
Shape3D sphere = new Sphere(6);
PhongMaterial material = new PhongMaterial();
material.setDiffuseMap(writableImage);
material.setDiffuseColor(Color.CORAL);
sphere.setMaterial(material);
sphere.setOnMousePressed(e -> {
if (e.isSecondaryButtonDown()) {
anchorX = e.getSceneX();
}
rotaionMode = e.isSecondaryButtonDown();
paintMode = e.isPrimaryButtonDown();
});
sphere.setOnMouseDragged(e -> {
// painting if primary button is pressed
Point2D intersectedTexCoord = e.getPickResult().getIntersectedTexCoord();
if (paintMode && intersectedTexCoord != null) {
// uv cordinates to writableImage x y indices
int x = (int) (intersectedTexCoord.getX() * 800);
int y = (int) (intersectedTexCoord.getY() * 800);
// the rectangle of pixelWritter is 12*12 therefore an array 144 length of argb is needed
pixelWriter.setPixels(x, y, 12, 12, PixelFormat.getIntArgbInstance(), colorArray, 0, 0);
}
// rotate if secondary button is pressred
if (rotaionMode) {
sphere.setRotationAxis(Rotate.Y_AXIS);
sphere.setRotate(sphere.getRotate() + (e.getSceneX() - anchorX) * -0.05);
}
});
// writableImage in imageView and difuseMap
ImageView imageView = new ImageView(writableImage);
imageView.setFitHeight(300);
imageView.setFitWidth(300);
Group group = new Group(camera, sphere);
SubScene subScene = new SubScene(group, 300, 300, true, SceneAntialiasing.BALANCED);
subScene.setFill(Color.AQUAMARINE);
subScene.setCamera(camera);
HBox hBox = new HBox(picker, subScene, imageView);
hBox.setAlignment(Pos.CENTER);
hBox.setSpacing(30);
Scene scene = new Scene(hBox, 800, 600);
stage.setTitle("painting 3d texture");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
public int[] getColorArray(Color color) {
// get color values in doubles
double alphaD = color.getOpacity();
double redD = color.getRed();
double greenD = color.getGreen();
double blueD = color.getBlue();
// double to int
int alpha = (int) Math.round(255 * alphaD);
int red = (int) Math.round(255 * redD);
int green = (int) Math.round(255 * greenD);
int blue = (int) Math.round(255 * blueD);
// shifting int on hex
alpha = (alpha << 24) & 0xFF000000;
red = (red << 16) & 0x00FF0000;
green = (green << 8) & 0x0000FF00;
blue = blue & 0x000000FF;
// argb value
int argb = alpha | red | green | blue;
// the rectangle of pixelWritter is 12*12 therefore an array 144 length of argb is needed
int[] colors = new int[144];
for (int i = 0; i < colors.length; i++) {
colors[i] = argb;
}
return colors;
}
}