add box UV bbmodel support, enable unit tests, hide Share button outside library tab
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package com.razz.dfashion.bbmodel;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -12,4 +13,10 @@ public record BbCube (
|
||||
float inflate,
|
||||
Vector3f origin,
|
||||
Vector3f rotation,
|
||||
Map<String, BbFace> faces){}
|
||||
Map<String, BbFace> faces,
|
||||
@Nullable
|
||||
int[] uvOffset,
|
||||
boolean mirrorUv,
|
||||
@Nullable
|
||||
Boolean boxUv
|
||||
){}
|
||||
|
||||
@@ -64,7 +64,7 @@ public class BbModelParser {
|
||||
JsonObject res = root.has("resolution") ? root.getAsJsonObject("resolution") : null;
|
||||
int resW = res != null && res.has("width") ? res.get("width").getAsInt() : 64;
|
||||
int resH = res != null && res.has("height") ? res.get("height").getAsInt() : 64;
|
||||
|
||||
boolean metaBoxUv = meta != null && meta.has("box_uv") && meta.get("box_uv").getAsBoolean();
|
||||
List<BbCube> cubes = new ArrayList<>();
|
||||
List<BbLocator> locators = new ArrayList<>();
|
||||
|
||||
@@ -73,7 +73,10 @@ public class BbModelParser {
|
||||
JsonObject obj = e.getAsJsonObject();
|
||||
String type = obj.has("type") ? obj.get("type").getAsString() : "cube";
|
||||
switch (type) {
|
||||
case "cube" -> cubes.add(GSON.fromJson(obj, BbCube.class));
|
||||
case "cube" -> {
|
||||
BbCube raw = GSON.fromJson(obj, BbCube.class);
|
||||
cubes.add(expandBoxUvIfNeeded(raw, metaBoxUv));
|
||||
}
|
||||
case "locator" -> locators.add(GSON.fromJson(obj, BbLocator.class));
|
||||
// meshes, texture_meshes, null_objects, etc. silently dropped — renderer ignores them
|
||||
}
|
||||
@@ -85,7 +88,7 @@ public class BbModelParser {
|
||||
JsonArray outlinerArr = root.has("outliner") ? root.getAsJsonArray("outliner") : new JsonArray();
|
||||
List<BbOutlinerNode> outliner = GSON.fromJson(outlinerArr, new TypeToken<List<BbOutlinerNode>>(){}.getType());
|
||||
|
||||
Bbmodel model = new Bbmodel(resW, resH, cubes, locators, groups, outliner);
|
||||
Bbmodel model = new Bbmodel(resW, resH, metaBoxUv, cubes, locators, groups, outliner);
|
||||
validate(model);
|
||||
return model;
|
||||
}
|
||||
@@ -188,6 +191,44 @@ public class BbModelParser {
|
||||
if (!Float.isFinite(v)) throw new BadBbmodelException(label + " is not finite: " + v);
|
||||
}
|
||||
|
||||
private static BbCube expandBoxUvIfNeeded(BbCube c, boolean metaBoxUv) {
|
||||
boolean effectiveBoxUv = c.boxUv() != null ? c.boxUv() : metaBoxUv;
|
||||
if (!effectiveBoxUv) return c;
|
||||
if (c.uvOffset() == null) return c;
|
||||
if (c.faces() != null && !c.faces().isEmpty()) return c;
|
||||
|
||||
Map<String, BbFace> synth = buildBoxUvFaces(c);
|
||||
return new BbCube(
|
||||
c.uuid(), c.name(), c.from(), c.to(), c.inflate(),
|
||||
c.origin(), c.rotation(), synth,
|
||||
c.uvOffset(), c.mirrorUv(), c.boxUv()
|
||||
);
|
||||
}
|
||||
|
||||
private static Map<String, BbFace> buildBoxUvFaces(BbCube c) {
|
||||
float u = c.uvOffset()[0];
|
||||
float v = c.uvOffset()[1];
|
||||
float w = c.to().x - c.from().x;
|
||||
float h = c.to().y - c.from().y;
|
||||
float d = c.to().z - c.from().z;
|
||||
|
||||
Map<String, BbFace> faces = new java.util.LinkedHashMap<>();
|
||||
faces.put("up", new BbFace(new float[]{u + d, v, u + d + w, v + d}, 0, 0));
|
||||
faces.put("down", new BbFace(new float[]{u + d + w, v, u + d + 2*w, v + d}, 0, 0));
|
||||
faces.put("east", new BbFace(new float[]{u, v + d, u + d, v + d + h}, 0, 0));
|
||||
faces.put("north", new BbFace(new float[]{u + d, v + d, u + d + w, v + d + h}, 0, 0));
|
||||
faces.put("west", new BbFace(new float[]{u + d + w, v + d, u + d + w + d, v + d + h}, 0, 0));
|
||||
faces.put("south", new BbFace(new float[]{u + d + w + d, v + d, u + d + 2*w + d, v + d + h}, 0, 0));
|
||||
|
||||
if (Boolean.TRUE.equals(c.mirrorUv())) { // c.mirrorUv() is primitive boolean, just use it:
|
||||
BbFace e = faces.get("east");
|
||||
faces.put("east", faces.get("west"));
|
||||
faces.put("west", e);
|
||||
}
|
||||
return faces;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static class Vector3fDeserializer implements JsonDeserializer<Vector3f> {
|
||||
@Override
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.util.List;
|
||||
public record Bbmodel(
|
||||
int resolutionWidth,
|
||||
int resolutionHeight,
|
||||
boolean metaBoxUV,
|
||||
List<BbCube> elements,
|
||||
List<BbLocator> locators,
|
||||
List<BbGroup> groups,
|
||||
|
||||
@@ -120,6 +120,7 @@ public class ClosetScreen extends Screen {
|
||||
private final @Nullable ClosetBlockEntity closet;
|
||||
private CameraType previousCamera;
|
||||
private @Nullable Button leftArrow, rightArrow;
|
||||
private @Nullable Button shareButton;
|
||||
// Body/head yaw applied every tick. yRot (camera-facing yaw) is left untouched so
|
||||
// rotating doesn't pan the camera — only the player's visible body/head turn.
|
||||
private float displayYaw = 0f;
|
||||
@@ -286,16 +287,19 @@ public class ClosetScreen extends Screen {
|
||||
// Open the shared-cosmetic upload form. All validation + parsing happens in the
|
||||
// uploader's pipeline (SafePngReader + BbModelParser + BbmodelCodec); this button
|
||||
// just swaps the closet UI into the form's widgets.
|
||||
addRenderableWidget(Button.builder(
|
||||
shareButton = Button.builder(
|
||||
Component.literal("Share"),
|
||||
b -> openShareForm()
|
||||
).bounds(this.width - 140, y, 60, 20).build());
|
||||
).bounds(this.width - 140, y, 60, 20).build();
|
||||
shareButton.visible = SHARED_TAB.equals(selectedCategory);
|
||||
addRenderableWidget(shareButton);
|
||||
}
|
||||
|
||||
private void selectCategory(String cat) {
|
||||
selectedCategory = cat;
|
||||
cosmeticScroll = 0;
|
||||
lastKnownSkinCount = -1; // force the next skin-tab tick to re-evaluate the grid
|
||||
if (shareButton != null) shareButton.visible = SHARED_TAB.equals(cat);
|
||||
rebuildCosmeticRow();
|
||||
}
|
||||
|
||||
|
||||
@@ -74,11 +74,12 @@ public final class BbmodelCodec {
|
||||
private static Bbmodel readBbmodel(ByteBuf buf) {
|
||||
int w = VarInt.read(buf);
|
||||
int h = VarInt.read(buf);
|
||||
boolean metaBoxUv = false;
|
||||
List<BbCube> elements = readList(buf, BbmodelCodec::readCube);
|
||||
List<BbLocator> locators = readList(buf, BbmodelCodec::readLocator);
|
||||
List<BbGroup> groups = readList(buf, BbmodelCodec::readGroup);
|
||||
List<BbOutlinerNode> outliner = readList(buf, BbmodelCodec::readOutlinerNode);
|
||||
return new Bbmodel(w, h, elements, locators, groups, outliner);
|
||||
return new Bbmodel(w, h, metaBoxUv, elements, locators, groups, outliner);
|
||||
}
|
||||
|
||||
// ---- BbCube ----
|
||||
@@ -123,7 +124,7 @@ public final class BbmodelCodec {
|
||||
String key = readString(buf);
|
||||
faces.put(key, readFace(buf));
|
||||
}
|
||||
return new BbCube(uuid, name, from, to, inflate, origin, rotation, faces);
|
||||
return new BbCube(uuid, name, from, to, inflate, origin, rotation, faces, null, false, null);
|
||||
}
|
||||
|
||||
// ---- BbFace ----
|
||||
|
||||
Reference in New Issue
Block a user