This commit is contained in:
NekoCaffeine 2022-01-17 00:04:54 +08:00 committed by Yuhui Huang
parent 13d5e5e6af
commit 6d617216c7
5 changed files with 97 additions and 71 deletions

View File

@ -12,7 +12,6 @@ public class SkinAnimation {
protected int weight, left;
protected List<SkinTransition> transitions;
@Deprecated
public SkinAnimation() {
this.transitions = new ArrayList<>();
}

View File

@ -4,6 +4,7 @@ import javafx.scene.*;
import javafx.scene.image.Image;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.Material;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Shape3D;
@ -11,6 +12,8 @@ import javafx.scene.transform.Rotate;
import javafx.scene.transform.Scale;
import javafx.scene.transform.Translate;
import org.jetbrains.annotations.Nullable;
public class SkinCanvas extends Group {
public static final Image ALEX = new Image(SkinCanvas.class.getResourceAsStream("/assets/img/alex.png"));
@ -22,7 +25,7 @@ public class SkinCanvas extends Group {
public static final SkinCube STEVEN_LARM = new SkinCube(4, 12, 4, 16F / 64F, 16F / 64F, 32F / 64F, 48F / 64F, 0F, false);
public static final SkinCube STEVEN_RARM = new SkinCube(4, 12, 4, 16F / 64F, 16F / 64F, 40F / 64F, 16F / 64F, 0F, false);
protected Image srcSkin, skin;
protected Image srcSkin, skin, srcCape, cape;
protected boolean isSlim;
protected double preW, preH;
@ -45,6 +48,8 @@ public class SkinCanvas extends Group {
public final SkinCube llegInside = new SkinCube(4, 12, 4, 16F / 64F, 16F / 64F, 16F / 64F, 48F / 64F, 0F, false);
public final SkinCube rlegInside = new SkinCube(4, 12, 4, 16F / 64F, 16F / 64F, 0F, 16F / 64F, 0F, false);
public final SkinCube capeCube = new SkinCube(10, 16, 1, 22F / 64F, 17F / 32F, 0F, 0F, 0F, false);
public final SkinGroup head = new SkinGroup(
new Rotate(0, 0, headInside.getHeight() / 2, 0, Rotate.X_AXIS),
new Rotate(0, Rotate.Y_AXIS),
@ -82,6 +87,13 @@ public class SkinCanvas extends Group {
rlegOuter, rlegInside
);
public final SkinGroup capeGroup = new SkinGroup(
new Rotate(0, 0, -capeCube.getHeight() / 2, 0, Rotate.X_AXIS),
new Rotate(0, Rotate.Y_AXIS),
new Rotate(0, Rotate.Z_AXIS),
capeCube
);
protected PerspectiveCamera camera = new PerspectiveCamera(true);
protected Rotate xRotate = new Rotate(0, Rotate.X_AXIS);
@ -90,10 +102,10 @@ public class SkinCanvas extends Group {
protected Translate translate = new Translate(0, 0, -80);
protected Scale scale = new Scale(1, 1);
protected SkinAnimationPlayer animationplayer = new SkinAnimationPlayer();
protected SkinAnimationPlayer animationPlayer = new SkinAnimationPlayer();
public SkinAnimationPlayer getAnimationplayer() {
return animationplayer;
public SkinAnimationPlayer getAnimationPlayer() {
return animationPlayer;
}
public Image getSrcSkin() {
@ -104,20 +116,22 @@ public class SkinCanvas extends Group {
return skin;
}
public void updateSkin(Image skin, boolean isSlim) {
public void updateSkin(Image skin, boolean isSlim, final @Nullable Image cape) {
if (SkinHelper.isNoRequest(skin) && SkinHelper.isSkin(skin)) {
this.srcSkin = skin;
this.skin = SkinHelper.x32Tox64(skin);
this.srcCape = cape;
this.cape = cape == null ? null : cape.getWidth() < 256 ? SkinHelper.enlarge(cape, 4, 8) : cape;
int multiple = Math.max((int) (1024 / skin.getWidth()), 1);
if (multiple > 1)
this.skin = SkinHelper.enlarge(this.skin, multiple);
this.skin = SkinHelper.enlarge(this.skin, multiple, multiple);
if (this.isSlim != isSlim)
updateSkinModel(isSlim);
updateSkinModel(isSlim, cape != null);
bindMaterial(root);
}
}
protected void updateSkinModel(boolean isSlim) {
protected void updateSkinModel(boolean isSlim, boolean hasCape) {
this.isSlim = isSlim;
FunctionHelper.alwaysB(SkinMultipleCubes::setWidth, isSlim ? 3 : 4, larmOuter, rarmOuter);
FunctionHelper.alwaysB(SkinCube::setWidth, isSlim ? 3D : 4D, larmInside, rarmInside);
@ -134,6 +148,8 @@ public class SkinCanvas extends Group {
larm.getZRotate().setPivotX(-larmInside.getWidth() / 2);
rarm.getZRotate().setPivotX(+rarmInside.getWidth() / 2);
capeGroup.setVisible(hasCape);
}
public SkinCanvas(double preW, double preH) {
@ -149,17 +165,17 @@ public class SkinCanvas extends Group {
init();
}
protected Material createMaterial() {
protected Material createMaterial(final Image image) {
PhongMaterial material = new PhongMaterial();
material.setDiffuseMap(skin);
material.setDiffuseMap(image);
return material;
}
protected void bindMaterial(Group group) {
Material material = createMaterial();
Material material = createMaterial(skin);
for (Node node : group.getChildren())
if (node instanceof Shape3D)
((Shape3D) node).setMaterial(material);
((Shape3D) node).setMaterial(node == capeCube ? createMaterial(cape) : material);
else if (node instanceof SkinMultipleCubes)
((SkinMultipleCubes) node).updateSkin(skin);
else if (node instanceof Group)
@ -178,6 +194,11 @@ public class SkinCanvas extends Group {
lleg.setTranslateY(+(bodyInside.getHeight() + llegInside.getHeight()) / 2);
rleg.setTranslateY(+(bodyInside.getHeight() + rlegInside.getHeight()) / 2);
capeGroup.setTranslateY(+(capeCube.getHeight() - bodyOuter.getHeight()) / 2);
capeGroup.setTranslateZ(-(bodyInside.getDepth() + bodyOuter.getDepth()) / 2);
capeGroup.getTransforms().addAll(new Rotate(180, Rotate.Y_AXIS), new Rotate(10, Rotate.X_AXIS));
root.getTransforms().addAll(xRotate);
root.getChildren().addAll(
@ -186,15 +207,20 @@ public class SkinCanvas extends Group {
larm,
rarm,
lleg,
rleg
rleg,
capeGroup
);
updateSkin(skin, false);
updateSkin(skin, false, null);
return root;
}
protected SubScene createSubScene() {
Group group = new Group();
AmbientLight light = new AmbientLight(Color.WHITE);
group.getChildren().add(light);
group.getChildren().add(createPlayerModel());
group.getTransforms().add(zRotate);

View File

@ -1,9 +1,9 @@
package moe.mickey.minecraft.skin.fx;
import javafx.scene.image.Image;
import javafx.scene.shape.Mesh;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.TriangleMesh;
import org.apache.commons.lang3.ArrayUtils;
public class SkinCube extends MeshView {
@ -20,61 +20,60 @@ public class SkinCube extends MeshView {
width /= 2F;
height /= 2F;
depth /= 2F;
return new float[]{
-width, -height, depth, // P0
width, -height, depth, // P1
-width, height, depth, // P2
width, height, depth, // P3
-width, -height, -depth, // P4
width, -height, -depth, // P5
-width, height, -depth, // P6
width, height, -depth // P7
-width, -height, +depth, // P0
+width, -height, +depth, // P1
-width, +height, +depth, // P2
+width, +height, +depth, // P3
-width, -height, -depth, // P4
+width, -height, -depth, // P5
-width, +height, -depth, // P6
+width, +height, -depth // P7
};
}
public static float[] createTexCoords(float width, float height, float depth, float scaleX, float scaleY,
float startX, float startY, boolean isSlim) {
float startX, float startY, boolean isSlim) {
float x = (width + depth) * 2, y = height + depth, half_width = width / x * scaleX, half_depth = depth / x * scaleX,
top_x = depth / x * scaleX + startX, top_y = startY, arm4 = isSlim ? half_depth : half_width,
bottom_x = startX, middle_y = depth / y * scaleY + top_y, bottom_y = scaleY + top_y;
return new float[]{
top_x, top_y, // T0 ---
top_x + half_width, top_y, // T1 |
top_x + half_width * 2, top_y, // T2 ---
bottom_x, middle_y, // T3 ---
bottom_x + half_depth, middle_y, // T4 |
top_x + half_width, top_y, // T1 |
top_x + half_width * 2, top_y, // T2 ---
bottom_x, middle_y, // T3 ---
bottom_x + half_depth, middle_y, // T4 |
bottom_x + half_depth + half_width, middle_y, // T5 |
bottom_x + scaleX - arm4, middle_y, // T6 |
bottom_x + scaleX, middle_y, // T7 ---
bottom_x, bottom_y, // T8 ---
bottom_x + half_depth, bottom_y, // T9 |
bottom_x + scaleX - arm4, middle_y, // T6 |
bottom_x + scaleX, middle_y, // T7 ---
bottom_x, bottom_y, // T8 ---
bottom_x + half_depth, bottom_y, // T9 |
bottom_x + half_depth + half_width, bottom_y, // T10 |
bottom_x + scaleX - arm4, bottom_y, // T11 |
bottom_x + scaleX, bottom_y // T12 ---
bottom_x + scaleX - arm4, bottom_y, // T11 |
bottom_x + scaleX, bottom_y // T12 ---
};
}
public static int[] createFaces() {
int[] faces = new int[]{
int[] faces = {
// TOP
5, 0, 4, 1, 0, 5, //P5,T0, P4,T1, P0,T5
5, 0, 0, 5, 1, 4, //P5,T0, P0,T5, P1,T4
// RIGHT
0, 5, 4, 6, 6, 11, //P0,T4 ,P4,T3, P6,T8
0, 5, 6, 11, 2, 10, //P0,T4 ,P6,T8, P2,T9
0, 5, 4, 6, 6, 11, //P0,T4 ,P4,T3, P6,T8
0, 5, 6, 11, 2, 10, //P0,T4 ,P6,T8, P2,T9
// FRONT
1, 4, 0, 5, 2, 10, //P1,T5, P0,T4, P2,T9
1, 4, 2, 10, 3, 9, //P1,T5, P2,T9, P3,T10
1, 4, 0, 5, 2, 10, //P1,T5, P0,T4, P2,T9
1, 4, 2, 10, 3, 9, //P1,T5, P2,T9, P3,T10
// LEFT
5, 3, 1, 4, 3, 9, //P5,T6, P1,T5, P3,T10
5, 3, 3, 9, 7, 8, //P5,T6, P3,T10,P7,T11
// BACK
4, 6, 5, 7, 7, 12, //P4,T6, P5,T7, P7,T12
4, 6, 7, 12, 6, 11, //P4,T6, P7,T12,P6,T11
4, 6, 5, 7, 7, 12, //P4,T6, P5,T7, P7,T12
4, 6, 7, 12, 6, 11, //P4,T6, P7,T12,P6,T11
// BOTTOM
3, 5, 2, 6, 6, 2, //P3,T2, P2,T1, P6,T5
3, 5, 6, 2, 7, 1 //P3,T2, P6,T5, P7,T6
3, 5, 6, 2, 7, 1 //P3,T2, P6,T5, P7,T6
};
int[] copy = faces.clone();
@ -91,7 +90,6 @@ public class SkinCube extends MeshView {
private double width, height, depth;
private boolean isSlim;
private Image skin;
private Mesh model;
public SkinCube(float width, float height, float depth, float scaleX, float scaleY, float startX, float startY, float enlarge, boolean isSlim) {

View File

@ -10,12 +10,12 @@ public final class SkinHelper {
private SkinHelper() {
}
public static class PixelCopyer {
public static class PixelCopier {
protected Image srcImage;
protected WritableImage newImage;
public PixelCopyer(Image srcImage, WritableImage newImage) {
public PixelCopier(Image srcImage, WritableImage newImage) {
this.srcImage = srcImage;
this.newImage = newImage;
}
@ -70,53 +70,53 @@ public final class SkinHelper {
}
public static Image x32Tox64(Image srcSkin) {
if (srcSkin.getHeight() == 64)
if (srcSkin.getHeight() == srcSkin.getWidth())
return srcSkin;
WritableImage newSkin = new WritableImage((int) srcSkin.getWidth(), (int) srcSkin.getHeight() * 2);
PixelCopyer copyer = new PixelCopyer(srcSkin, newSkin);
PixelCopier copier = new PixelCopier(srcSkin, newSkin);
// HEAD & HAT
copyer.copy(0 / 64F, 0 / 32F, 0 / 64F, 0 / 64F, 64 / 64F, 16 / 32F);
copier.copy(0 / 64F, 0 / 32F, 0 / 64F, 0 / 64F, 64 / 64F, 16 / 32F);
// LEFT-LEG
x32Tox64(copyer, 0 / 64F, 16 / 32F, 16 / 64F, 48 / 64F, 4 / 64F, 12 / 32F, 4 / 64F);
x32Tox64(copier, 0 / 64F, 16 / 32F, 16 / 64F, 48 / 64F, 4 / 64F, 12 / 32F, 4 / 64F);
// RIGHT-LEG
copyer.copy(0 / 64F, 16 / 32F, 0 / 64F, 16 / 64F, 16 / 64F, 16 / 32F);
copier.copy(0 / 64F, 16 / 32F, 0 / 64F, 16 / 64F, 16 / 64F, 16 / 32F);
// BODY
copyer.copy(16 / 64F, 16 / 32F, 16 / 64F, 16 / 64F, 24 / 64F, 16 / 32F);
copier.copy(16 / 64F, 16 / 32F, 16 / 64F, 16 / 64F, 24 / 64F, 16 / 32F);
// LEFT-ARM
x32Tox64(copyer, 40 / 64F, 16 / 32F, 32 / 64F, 48 / 64F, 4 / 64F, 12 / 32F, 4 / 64F);
x32Tox64(copier, 40 / 64F, 16 / 32F, 32 / 64F, 48 / 64F, 4 / 64F, 12 / 32F, 4 / 64F);
// RIGHT-ARM
copyer.copy(40 / 64F, 16 / 32F, 40 / 64F, 16 / 64F, 16 / 64F, 16 / 32F);
copier.copy(40 / 64F, 16 / 32F, 40 / 64F, 16 / 64F, 16 / 64F, 16 / 32F);
return newSkin;
}
static void x32Tox64(PixelCopyer copyer, float srcX, float srcY, float toX, float toY, float width, float height, float depth) {
static void x32Tox64(PixelCopier copier, float srcX, float srcY, float toX, float toY, float width, float height, float depth) {
// TOP
copyer.copy(srcX + depth, srcY, toX + depth, toY, width, depth * 2, true, false);
copier.copy(srcX + depth, srcY, toX + depth, toY, width, depth * 2, true, false);
// BOTTOM
copyer.copy(srcX + depth + width, srcY, toX + depth + width, toY, width, depth * 2, true, false);
copier.copy(srcX + depth + width, srcY, toX + depth + width, toY, width, depth * 2, true, false);
// INS
copyer.copy(srcX, srcY + depth * 2, toX + width + depth, toY + depth, depth, height, true, false);
copier.copy(srcX, srcY + depth * 2, toX + width + depth, toY + depth, depth, height, true, false);
// OUTS
copyer.copy(srcX + width + depth, srcY + depth * 2, toX, toY + depth, depth, height, true, false);
copier.copy(srcX + width + depth, srcY + depth * 2, toX, toY + depth, depth, height, true, false);
// FRONT
copyer.copy(srcX + depth, srcY + depth * 2, toX + depth, toY + depth, width, height, true, false);
copier.copy(srcX + depth, srcY + depth * 2, toX + depth, toY + depth, width, height, true, false);
// BACK
copyer.copy(srcX + width + depth * 2, srcY + depth * 2, toX + width + depth * 2, toY + depth, width, height, true, false);
copier.copy(srcX + width + depth * 2, srcY + depth * 2, toX + width + depth * 2, toY + depth, width, height, true, false);
}
public static Image enlarge(Image srcSkin, int multiple) {
WritableImage newSkin = new WritableImage((int) srcSkin.getWidth() * multiple, (int) srcSkin.getHeight() * multiple);
public static Image enlarge(Image srcSkin, int multipleX, int multipleY) {
WritableImage newSkin = new WritableImage((int) srcSkin.getWidth() * multipleX, (int) srcSkin.getHeight() * multipleY);
PixelReader reader = srcSkin.getPixelReader();
PixelWriter writer = newSkin.getPixelWriter();
for (int x = 0, lenX = (int) srcSkin.getWidth(); x < lenX; x++)
for (int y = 0, lenY = (int) srcSkin.getHeight(); y < lenY; y++)
for (int mx = 0; mx < multiple; mx++)
for (int my = 0; my < multiple; my++) {
for (int mx = 0; mx < multipleX; mx++)
for (int my = 0; my < multipleY; my++) {
int argb = reader.getArgb(x, y);
writer.setArgb(x * multiple + mx, y * multiple + my, argb);
writer.setArgb(x * multipleX + mx, y * multipleY + my, argb);
}
return newSkin;

View File

@ -76,7 +76,7 @@ public class OfflineAccountSkinPane extends StackPane {
canvasPane.setPrefWidth(300);
canvasPane.setPrefHeight(300);
pane.setCenter(canvas);
canvas.getAnimationplayer().addSkinAnimation(new SkinAniWavingArms(100, 2000, 7.5, canvas), new SkinAniRunning(100, 100, 30, canvas));
canvas.getAnimationPlayer().addSkinAnimation(new SkinAniWavingArms(100, 2000, 7.5, canvas), new SkinAniRunning(100, 100, 30, canvas));
canvas.enableRotation(.5);
canvas.addEventHandler(DragEvent.DRAG_OVER, e -> {
@ -136,11 +136,14 @@ public class OfflineAccountSkinPane extends StackPane {
LOG.log(Level.WARNING, "Failed to load skin", exception);
Controllers.showToast(i18n("message.failed"));
} else {
if (result == null || result.getSkin() == null) {
canvas.updateSkin(getDefaultTexture(), isDefaultSlim());
if (result == null || result.getSkin() == null && result.getCape() == null) {
canvas.updateSkin(getDefaultTexture(), isDefaultSlim(), null);
return;
}
canvas.updateSkin(new Image(result.getSkin().getInputStream()), result.getModel() == TextureModel.ALEX);
canvas.updateSkin(
result.getSkin() != null ? new Image(result.getSkin().getInputStream()) : getDefaultTexture(),
result.getModel() == TextureModel.ALEX,
result.getCape() != null ? new Image(result.getCape().getInputStream()) : null);
}
}).start();
}, skinItem.selectedDataProperty(), cslApiField.textProperty(), skinSelector.valueProperty(), capeSelector.valueProperty());