-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improvements #24
base: master
Are you sure you want to change the base?
Improvements #24
Changes from 2 commits
870a089
0801bac
e287f86
910645b
5896323
d519db9
8529ae5
1554421
72270fa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,6 @@ | ||
package net.camotoy.bedrockskinutility.client; | ||
|
||
import com.google.common.collect.Maps; | ||
import com.google.gson.JsonArray; | ||
import com.google.gson.JsonElement; | ||
import com.google.gson.JsonObject; | ||
import com.mojang.logging.LogUtils; | ||
import net.minecraft.client.model.HumanoidModel; | ||
import net.minecraft.client.model.geom.ModelPart; | ||
|
@@ -12,6 +9,12 @@ | |
import net.minecraft.client.model.geom.builders.CubeListBuilder; | ||
import net.minecraft.client.player.AbstractClientPlayer; | ||
import net.minecraft.core.Direction; | ||
import org.oryxel.cube.model.bedrock.BedrockGeometry; | ||
import org.oryxel.cube.model.bedrock.model.Bone; | ||
import org.oryxel.cube.model.bedrock.model.Cube; | ||
import org.oryxel.cube.parser.bedrock.BedrockGeometrySerializer; | ||
import org.oryxel.cube.util.ArrayUtil; | ||
import org.oryxel.cube.util.UVUtil; | ||
|
||
import java.util.*; | ||
|
||
|
@@ -21,156 +24,130 @@ public class GeometryUtil { | |
private static final Set<Direction> ALL_VISIBLE = EnumSet.allOf(Direction.class); | ||
|
||
public static BedrockPlayerEntityModel<AbstractClientPlayer> bedrockGeoToJava(SkinInfo info) { | ||
// There are some times when the skin image file is larger than the geometry UV points. | ||
// In this case, we need to scale UV calls | ||
// https://github.com/Camotoy/BedrockSkinUtility/issues/9 | ||
int uvHeight = info.getHeight(); | ||
int uvWidth = info.getWidth(); | ||
|
||
// Construct a list of all bones we need to translate | ||
List<JsonObject> bones = new ArrayList<>(); | ||
if (info.getGeometryRaw() == null || info.getGeometryRaw().isEmpty()) | ||
return null; | ||
|
||
BedrockGeometry geometry = null; | ||
|
||
try { | ||
String geometryName = info.getGeometryName().getAsJsonObject("geometry").get("default").getAsString(); | ||
if (info.getGeometry().get("format_version").getAsString().equals("1.8.0")) { | ||
for (JsonElement node : info.getGeometry().getAsJsonObject(geometryName).getAsJsonArray("bones")) { | ||
bones.add(node.getAsJsonObject()); | ||
} | ||
} else { // Seen with format_version 1.12.0 | ||
for (JsonElement node : info.getGeometry().getAsJsonArray("minecraft:geometry")) { | ||
JsonObject o = node.getAsJsonObject(); | ||
JsonObject description = o.get("description").getAsJsonObject(); | ||
if (!description.get("identifier").getAsString().equals(geometryName)) { | ||
continue; | ||
} else { | ||
uvHeight = description.get("texture_height").getAsInt(); | ||
uvWidth = description.get("texture_width").getAsInt(); | ||
} | ||
for (JsonElement subNode : o.get("bones").getAsJsonArray()) { | ||
bones.add(subNode.getAsJsonObject()); | ||
} | ||
} | ||
} | ||
geometry = BedrockGeometrySerializer.deserialize(info.getGeometryRaw()); | ||
} catch (Exception e) { | ||
LOGGER.error("Error while parsing geometry!"); | ||
e.printStackTrace(); | ||
// e.printStackTrace(); | ||
return null; | ||
} | ||
|
||
Map<String, PartInfo> stringToPart = new HashMap<>(); | ||
try { | ||
for (JsonObject bone : bones) { | ||
// Iterate through all bones | ||
String name = bone.get("name").getAsString(); | ||
|
||
JsonElement jsonParent = bone.get("parent"); | ||
JsonObject parentPart = null; | ||
String parent = null; | ||
if (jsonParent != null) { | ||
// Search through all bones to find the parent part | ||
parent = jsonParent.getAsString(); | ||
for (JsonObject otherBone : bones) { | ||
if (parent.equals(otherBone.get("name").getAsString())) { | ||
parentPart = otherBone; | ||
break; | ||
} | ||
} | ||
} | ||
if (geometry == null) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Brackets |
||
return null; | ||
|
||
List<ModelPart.Cube> cuboids = new ArrayList<>(); | ||
JsonArray pivot = bone.getAsJsonArray("pivot"); | ||
float pivotX = pivot.get(0).getAsFloat(); | ||
float pivotY = pivot.get(1).getAsFloat(); | ||
float pivotZ = pivot.get(2).getAsFloat(); | ||
|
||
JsonArray cubes = bone.getAsJsonArray("cubes"); | ||
if (cubes != null) { | ||
for (JsonElement node : cubes) { | ||
JsonObject cube = node.getAsJsonObject(); | ||
JsonElement mirrorNode = cube.get("mirror"); // Can be null on the llama skins in the Wandering Trader pack | ||
boolean mirrored = mirrorNode != null && mirrorNode.getAsBoolean(); | ||
JsonArray origin = cube.getAsJsonArray("origin"); | ||
float originX = origin.get(0).getAsFloat(); | ||
float originY = origin.get(1).getAsFloat(); | ||
float originZ = origin.get(2).getAsFloat(); | ||
JsonArray size = cube.getAsJsonArray("size"); | ||
float sizeX = size.get(0).getAsFloat(); | ||
float sizeY = size.get(1).getAsFloat(); | ||
float sizeZ = size.get(2).getAsFloat(); | ||
JsonArray uv = cube.getAsJsonArray("uv"); | ||
JsonElement inflateNode = cube.get("inflate"); // Again, the llama skin | ||
float inflate = inflateNode != null ? inflateNode.getAsFloat() : 0f; | ||
// I didn't use the below, but it may be a helpful reference in the future | ||
// The Y needs to be inverted, for whatever reason | ||
// https://github.com/JannisX11/blockbench/blob/8529c0adee8565f8dac4b4583c3473b60679966d/js/transform.js#L148 | ||
cuboids.add(new ModelPart.Cube((int) uv.get(0).getAsFloat(), (int) uv.get(1).getAsFloat(), | ||
(originX - pivotX), (-(originY + sizeY) + pivotY), (originZ - pivotZ), | ||
sizeX, sizeY, sizeZ, inflate, inflate, inflate, mirrored, uvHeight, uvWidth, ALL_VISIBLE)); | ||
} | ||
} | ||
int uvHeight = geometry.textureHeight(); | ||
int uvWidth = geometry.textureWidth(); | ||
|
||
Map<String, ModelPart> children = new HashMap<>(); | ||
ModelPart part = new ModelPart(cuboids, children); | ||
if (parentPart != null) { | ||
// This appears to be a difference between Bedrock and Java - pivots are carried over for us | ||
JsonArray parentPivot = parentPart.getAsJsonArray("pivot"); | ||
part.setPos(pivotX - parentPivot.get(0).getAsFloat(), | ||
pivotY - parentPivot.get(1).getAsFloat(), | ||
pivotZ - parentPivot.get(2).getAsFloat()); | ||
} else { | ||
part.setPos(pivotX, pivotY, pivotZ); | ||
} | ||
final Map<String, Bone> boneMap = new HashMap<>(); | ||
for (Bone bone : geometry.bones()) { | ||
boneMap.put(bone.name(), bone); | ||
} | ||
|
||
switch (name) { // Also do this with the overlays? Those are final, though. | ||
case "head", "hat", "rightArm", "body", "leftArm", "leftLeg", "rightLeg" -> parent = "root"; | ||
final Map<String, PartInfo> stringToPart = new HashMap<>(); | ||
for (Bone bone : geometry.bones()) { | ||
final List<ModelPart.Cube> cuboids = new ArrayList<>(); | ||
float pivotX = (float) bone.pivot()[0], pivotY = (float) bone.pivot()[1], pivotZ = (float) bone.pivot()[2]; | ||
Bone parentBone = null; | ||
if (!bone.parent().isEmpty()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Brackets |
||
parentBone = boneMap.get(bone.parent()); | ||
String parent = bone.parent(), name = bone.name(); | ||
|
||
for (Cube cube : bone.cubes()) { | ||
double[] uv = new double[3]; | ||
if (cube instanceof Cube.PerFaceCube perFaceCube) { // support for perface uv? | ||
uv = UVUtil.portToBoxUv(perFaceCube.uvMap(), perFaceCube.origin(), | ||
ArrayUtil.combineArray(perFaceCube.origin(), perFaceCube.size())); | ||
} else if (cube instanceof Cube.BoxCube boxCube) { | ||
uv = boxCube.uvOffset(); | ||
} | ||
|
||
name = adjustFormatting(name); | ||
float originX = (float) cube.origin()[0], originY = (float) cube.origin()[1], originZ = (float) cube.origin()[2]; | ||
float sizeX = (float) cube.size()[0], sizeY = (float) cube.size()[1], sizeZ = (float) cube.size()[2]; | ||
float inflate = (float) cube.inflate(); | ||
// I didn't use the below, but it may be a helpful reference in the future | ||
// The Y needs to be inverted, for whatever reason | ||
// https://github.com/JannisX11/blockbench/blob/8529c0adee8565f8dac4b4583c3473b60679966d/js/transform.js#L148 | ||
cuboids.add(new ModelPart.Cube((int) uv[0], (int) uv[1], | ||
(originX - pivotX), (-(originY + sizeY) + pivotY), (originZ - pivotZ), | ||
sizeX, sizeY, sizeZ, inflate, inflate, inflate, cube.mirror(), uvHeight, uvWidth, ALL_VISIBLE)); | ||
} | ||
|
||
Map<String, ModelPart> children = new HashMap<>(); | ||
ModelPart part = new ModelPart(cuboids, children); | ||
// set rotation (if there is one) | ||
part.setRotation((float) -bone.rotation()[0], (float) -bone.rotation()[1], (float) bone.rotation()[2]); | ||
|
||
if (parentBone != null) { | ||
// This appears to be a difference between Bedrock and Java - pivots are carried over for us | ||
part.setPos((float) (pivotX - parentBone.pivot()[0]), (float) (pivotY - parentBone.pivot()[1]), (float) (pivotZ - parentBone.pivot()[2])); | ||
} else part.setPos(pivotX, pivotY, pivotZ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Brackets |
||
|
||
stringToPart.put(name, new PartInfo(adjustFormatting(parent), part, children)); | ||
// Please lowercase this, it can be lowercase... | ||
switch (name.toLowerCase()) { // Also do this with the overlays? Those are final, though. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. toLowerCase(Locale.ROOT) |
||
case "head", "hat", "rightarm", "body", "leftarm", "leftleg", "rightleg" -> parent = "root"; | ||
} | ||
|
||
for (Map.Entry<String, PartInfo> entry : stringToPart.entrySet()) { | ||
if (entry.getValue().parent != null) { | ||
PartInfo parentPart = stringToPart.get(entry.getValue().parent); | ||
if (parentPart != null) { | ||
parentPart.children.put(entry.getKey(), entry.getValue().part); | ||
} | ||
name = adjustFormatting(name); | ||
|
||
stringToPart.put(name, new PartInfo(adjustFormatting(parent), part, children)); | ||
} | ||
|
||
PartInfo root = stringToPart.get("root"); | ||
|
||
for (Map.Entry<String, PartInfo> entry : stringToPart.entrySet()) { | ||
if (entry.getValue().parent != null) { | ||
PartInfo parentPart = stringToPart.get(entry.getValue().parent); | ||
if (parentPart != null) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Brackets |
||
parentPart.children.put(entry.getKey(), entry.getValue().part); | ||
else { | ||
if (root != null && entry.getValue().part != root.part) // put to root if you can't find the parent. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Brackets |
||
root.children.put(entry.getKey(), entry.getValue().part); | ||
} | ||
} | ||
} | ||
|
||
PartInfo root = stringToPart.get("root"); | ||
|
||
ensureAvailable(root.children, "ear"); | ||
root.children.computeIfAbsent("cloak", (string) -> // Required to allow a cape to render | ||
HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F).getRoot().addOrReplaceChild(string, | ||
CubeListBuilder.create() | ||
.texOffs(0, 0) | ||
.addBox(-5.0F, 0.0F, -1.0F, 10.0F, 16.0F, 1.0F, CubeDeformation.NONE, 1.0F, 0.5F), | ||
PartPose.offset(0.0F, 0.0F, 0.0F)).bake(64, 64)); | ||
ensureAvailable(root.children, "left_sleeve"); | ||
ensureAvailable(root.children, "right_sleeve"); | ||
ensureAvailable(root.children, "left_pants"); | ||
ensureAvailable(root.children, "right_pants"); | ||
ensureAvailable(root.children, "jacket"); | ||
|
||
// Create base model | ||
return new BedrockPlayerEntityModel<>(root.part); | ||
} catch (Exception e) { | ||
LOGGER.error("Error while parsing geometry into model!", e); | ||
if (root == null) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Brackets |
||
return null; | ||
} | ||
|
||
ensureAvailable(root.children, "ear"); | ||
root.children.computeIfAbsent("cloak", (string) -> // Required to allow a cape to render | ||
HumanoidModel.createMesh(CubeDeformation.NONE, 0.0F).getRoot().addOrReplaceChild(string, | ||
CubeListBuilder.create() | ||
.texOffs(0, 0) | ||
.addBox(-5.0F, 0.0F, -1.0F, 10.0F, 16.0F, 1.0F, CubeDeformation.NONE, 1.0F, 0.5F), | ||
PartPose.offset(0.0F, 0.0F, 0.0F)).bake(64, 64)); | ||
ensureAvailable(root.children, "left_sleeve"); | ||
ensureAvailable(root.children, "right_sleeve"); | ||
ensureAvailable(root.children, "left_pants"); | ||
ensureAvailable(root.children, "right_pants"); | ||
ensureAvailable(root.children, "jacket"); | ||
|
||
// Just to be safe, some model seems to have only head, arm, etc. I mean... | ||
ensureAvailable(root.children, "hat"); | ||
ensureAvailable(root.children, "body"); | ||
ensureAvailable(root.children, "left_arm"); | ||
ensureAvailable(root.children, "right_arm"); | ||
ensureAvailable(root.children, "left_leg"); | ||
ensureAvailable(root.children, "right_leg"); | ||
|
||
return new BedrockPlayerEntityModel<>(root.part, false); | ||
} | ||
|
||
private static String adjustFormatting(String name) { | ||
if (name == null) { | ||
return null; | ||
} | ||
|
||
return switch (name) { | ||
case "leftArm" -> "left_arm"; | ||
case "rightArm" -> "right_arm"; | ||
case "leftLeg" -> "left_leg"; | ||
case "rightLeg" -> "right_leg"; | ||
// it can be lowercase, use lowercase... | ||
return switch (name.toLowerCase()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. toLowerCase(Locale.ROOT) |
||
case "leftarm" -> "left_arm"; | ||
case "rightarm" -> "right_arm"; | ||
case "leftleg" -> "left_leg"; | ||
case "rightleg" -> "right_leg"; | ||
default -> name; | ||
}; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,7 +23,8 @@ public String getRefMapperConfig() { | |
|
||
@Override | ||
public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { | ||
if (mixinClassName.equals("net.camotoy.bedrockskinutility.client.mixin.CapeFeatureRendererMixin")) { | ||
if (mixinClassName.equals("net.camotoy.bedrockskinutility.client.mixin.CapeFeatureRendererMixin") || | ||
mixinClassName.equalsIgnoreCase("net.camotoy.bedrockskinutility.client.mixin.AbstractClientPlayer")) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will never be called as the class name is AbstractClientPlayerMixin |
||
boolean capes = FabricLoader.getInstance().getModContainer("capes").isPresent() | ||
|| FabricLoader.getInstance().getModContainer("cosmetica").isPresent(); | ||
if (capes) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Brackets