Merge remote-tracking branch 'origin/main'

This commit is contained in:
zimzaza4
2024-09-27 22:30:54 +08:00
8 changed files with 268 additions and 86 deletions

View File

@@ -35,28 +35,14 @@ public class ExtensionMain implements Extension {
generatedPackZip = dataFolder().resolve("generated_pack.zip");
try (ZipOutputStream zipOutputStream = new ZipOutputStream(Files.newOutputStream(generatedPackZip))) {
// 压缩文件夹
ZipUtil.compressFolder(generatedPack, null, zipOutputStream);
} catch (IOException e) {
e.printStackTrace();
}
for (String entity : GeneratorMain.entityMap.keySet()) {
String id = "modelengine:" + entity;
GeyserUtils.addCustomEntity(id);
Geometry geometry = GeneratorMain.geometryMap.get(entity);
if (geometry == null) {
continue;
}
geometry.getBones().forEach(bone -> {
GeyserUtils.addProperty(id, entity + ":" + bone, Boolean.class);
});
GeyserUtils.addProperty(id, "modelengine:anim", Integer.class);
GeyserUtils.registerProperties(id);
for (Entity entity : GeneratorMain.entityMap.values()) {
entity.register();
}
}

View File

@@ -2,7 +2,6 @@ package re.imc.geysermodelenginepackgenerator;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import re.imc.geysermodelenginepackgenerator.generator.*;
@@ -16,7 +15,6 @@ import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class GeneratorMain {
public static final Map<String, Entity> entityMap = new HashMap<>();
@@ -56,9 +54,11 @@ public class GeneratorMain {
if (isAnimationFile(json)) {
Animation animation = new Animation();
animation.setPath(currentPath);
animation.load(json);
animation.setModelId(modelId);
animation.load(json);
animationMap.put(modelId, animation);
entity.setAnimation(animation);
}
if (isGeometryFile(json)) {
@@ -67,6 +67,7 @@ public class GeneratorMain {
geometry.setPath(currentPath);
geometry.setModelId(modelId);
geometryMap.put(modelId, geometry);
entity.setGeometry(geometry);
canAdd = true;
}
} catch (IOException ex) {
@@ -78,10 +79,10 @@ public class GeneratorMain {
File config = new File(folder, "config.properties");
try {
if (config.exists()) {
entity.getProperties().load(new FileReader(config));
entity.getConfig().load(new FileReader(config));
} else {
entity.getProperties().setProperty("enable-part-visibility", "false");
entity.getProperties().store(new FileWriter(config), "For some reasons, the part visibility render controller may cause client crash");
entity.getConfig().setProperty("enable-part-visibility", "false");
entity.getConfig().store(new FileWriter(config), "For some reasons, the part visibility render controller may cause client crash");
}
} catch (IOException ex) {
ex.printStackTrace();
@@ -132,19 +133,24 @@ public class GeneratorMain {
renderControllersFolder.mkdirs();
for (Map.Entry<String, Animation> entry : animationMap.entrySet()) {
entry.getValue().modify();
Geometry geo = geometryMap.get(entry.getKey());
if (geo != null) {
entry.getValue().addHeadBind(geo);
}
Path path = animationsFolder.toPath().resolve(entry.getValue().getPath() + entry.getKey() + ".animation.json");
Path pathController = animationControllersFolder.toPath().resolve(entry.getValue().getPath() + entry.getKey() + ".animation_controllers.json");
path.toFile().getParentFile().mkdirs();
if (path.toFile().exists()) {
continue;
}
AnimationController controller = new AnimationController();
controller.load(entry.getValue());
try {
Files.writeString(path, GSON.toJson(entry.getValue().getJson()), StandardCharsets.UTF_8);
Files.writeString(path, entry.getValue().getJson().toString(), StandardCharsets.UTF_8);
Files.writeString(pathController, controller.getJson().toString(), StandardCharsets.UTF_8);
} catch (IOException e) {
e.printStackTrace();
}
@@ -159,7 +165,7 @@ public class GeneratorMain {
continue;
}
try {
Files.writeString(path, GSON.toJson(entry.getValue().getJson()), StandardCharsets.UTF_8);
Files.writeString(path, entry.getValue().getJson().toString(), StandardCharsets.UTF_8);
} catch (IOException e) {
e.printStackTrace();
}
@@ -181,7 +187,7 @@ public class GeneratorMain {
for (Map.Entry<String, Entity> entry : entityMap.entrySet()) {
Entity entity = entry.getValue();
entity.getProperties().setProperty("render_controller", "controller.render." + entry.getKey());
entity.getConfig().setProperty("render_controller", "controller.render." + entry.getKey());
entity.modify();
Path entityPath = entityFolder.toPath().resolve(entity.getPath() + entry.getKey() + ".entity.json");
@@ -190,7 +196,7 @@ public class GeneratorMain {
continue;
}
try {
Files.writeString(entityPath, entity.getJson(), StandardCharsets.UTF_8);
Files.writeString(entityPath, entity.getJson().toString(), StandardCharsets.UTF_8);
} catch (IOException e) {
e.printStackTrace();
}
@@ -200,7 +206,7 @@ public class GeneratorMain {
String id = entity.getModelId();
if (!geometryMap.containsKey(id)) continue;
RenderController controller = new RenderController(id, geometryMap.get(id).getBones());
entity.setRenderController(controller);
Path renderPath = new File(renderControllersFolder, "controller.render." + id + ".json").toPath();
if (renderPath.toFile().exists()) {
continue;
@@ -212,6 +218,7 @@ public class GeneratorMain {
}
}
/*
File controller = new File(animationControllersFolder, "modelengine.animation_controller.json");
if (!controller.exists()) {
try {
@@ -221,6 +228,7 @@ public class GeneratorMain {
}
}
*/
}
private static boolean isGeometryFile(String json) {

View File

@@ -13,8 +13,10 @@ import re.imc.geysermodelenginepackgenerator.GeneratorMain;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
@Getter
@Setter
@@ -33,23 +35,44 @@ public class Animation {
String modelId;
JsonObject json;
@Getter
Set<String> animationIds = new HashSet<>();
String path;
public void load(String json) {
this.json = new JsonParser().parse(json).getAsJsonObject();
}
public void modify() {
public void load(String string) {
this.json = new JsonParser().parse(string).getAsJsonObject();
JsonObject newAnimations = new JsonObject();
for (Map.Entry<String, JsonElement> element : json.get("animations").getAsJsonObject().entrySet()) {
if (element.getKey().equals("spawn")) {
GeneratorMain.entityMap
.get(modelId).setHasSpawnAnimation(true);
}
if (element.getKey().equals("walk")) {
GeneratorMain.entityMap
.get(modelId).setHasWalkAnimation(true);
animationIds.add(element.getKey());
JsonObject animation = element.getValue().getAsJsonObject();
if (animation.has("loop")) {
if (animation.get("loop").getAsJsonPrimitive().isString()) {
if (animation.get("loop").getAsString().equals("hold_on_last_frame")) {
for (Map.Entry<String, JsonElement> bone : animation.get("bones").getAsJsonObject().entrySet()) {
for (Map.Entry<String, JsonElement> anim : bone.getValue().getAsJsonObject().entrySet()) {
float max = -1;
JsonObject end = null;
if (!anim.getValue().isJsonObject()) {
continue;
}
for (Map.Entry<String, JsonElement> timeline : anim.getValue().getAsJsonObject().entrySet()) {
float time = Float.parseFloat(timeline.getKey());
if (time > max) {
max = time;
if (timeline.getValue().isJsonObject()) {
end = timeline.getValue().getAsJsonObject();
}
}
}
if (end != null && end.get("lerp_mode").getAsString().equals("catmullrom")) {
end.addProperty("lerp_mode", "linear");
}
}
}
}
}
}
newAnimations.add("animation." + modelId + "." + element.getKey(), element.getValue());
}

View File

@@ -1,7 +1,33 @@
package re.imc.geysermodelenginepackgenerator.generator;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import lombok.Getter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class AnimationController {
public static final String CONTROLLER_TEMPLATE =
"""
{
"initial_state": "stop",
"states": {
"play": {
"animations": [
"%anim%"
],
"blend_transition": 0.1,
"transitions": [{ "stop": "%query% == 0"}]
},
"stop": {
"blend_transition": 0.1,
"transitions": [{ "play": "%query% != 0"}]
}
}
}""";
public static final String TEMPLATE =
"""
{
@@ -73,6 +99,31 @@ public class AnimationController {
}
}
}""";
@Getter
JsonObject json;
public void load(Animation animation) {
JsonObject root = new JsonObject();
json = root;
root.addProperty("format_version", "1.10.0");
JsonObject animationControllers = new JsonObject();
root.add("animation_controllers", animationControllers);
List<String> sorted = new ArrayList<>(animation.animationIds);
int i = 0;
Collections.sort(sorted);
for (String id : sorted) {
int n = (int) Math.pow(2, (i % 24));
JsonObject controller = new JsonParser().parse(CONTROLLER_TEMPLATE.replace("%anim%", id).replace("%query%", "math.mod(math.floor(query.property('modelengine:anim" + i / 24 + "') / " + n + "), 2)")).getAsJsonObject();
animationControllers.add("controller.animation." + animation.modelId + "." + id, controller);
i++;
}
}
/*
public static final String TEMPLATE =
"""

View File

@@ -1,11 +1,18 @@
package re.imc.geysermodelenginepackgenerator.generator;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import me.zimzaza4.geyserutils.geyser.GeyserUtils;
import re.imc.geysermodelenginepackgenerator.GeneratorMain;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
@Getter
@Setter
@@ -28,16 +35,10 @@ public class Entity {
"default": "%geometry%"
},
"animations": {
"idle": "animation.%entity_id%.idle",
"spawn": "animation.%entity_id%.%spawn%",
"walk": "animation.%entity_id%.%walk%",
"look_at_target": "%look_at_target%",
"modelengine_controller": "controller.animation.modelengine"
"look_at_target": "%look_at_target%"
},
"scripts": {
"animate": [
"modelengine_controller",
"look_at_target"
]
},
@@ -51,13 +52,25 @@ public class Entity {
String modelId;
String json;
JsonObject json;
boolean hasHeadAnimation = false;
boolean hasWalkAnimation = false;
boolean hasSpawnAnimation = false;
@Setter
@Getter
Animation animation;
@Setter
@Getter
Geometry geometry;
@Setter
@Getter
RenderController renderController;
String path;
Properties properties = new Properties();
Properties config = new Properties();
public Entity(String modelId) {
this.modelId = modelId;
@@ -65,27 +78,43 @@ public class Entity {
public void modify() {
String walk;
String spawn;
walk = spawn = "idle";
if (hasWalkAnimation) {
walk = "walk";
}
if (hasSpawnAnimation) {
spawn = "spawn";
}
json = TEMPLATE.replace("%entity_id%", modelId)
json = new JsonParser().parse(TEMPLATE.replace("%entity_id%", modelId)
.replace("%geometry%", "geometry.modelengine_" + modelId)
.replace("%texture%", "textures/entity/" + path + modelId)
.replace("%look_at_target%", "animation." + modelId + ".look_at_target")
.replace("%walk%", walk)
.replace("%spawn%", spawn)
.replace("%material%", properties.getProperty("material", "entity_alphatest_change_color"))
.replace("%render_controller%", properties.getProperty("render_controller", "controller.render.default"));
.replace("%material%", config.getProperty("material", "entity_alphatest_change_color"))
.replace("%render_controller%", config.getProperty("render_controller", "controller.render.default"))).getAsJsonObject();
JsonObject description = json.get("minecraft:client_entity").getAsJsonObject().get("description").getAsJsonObject();
JsonObject jsonAnimations = description.get("animations").getAsJsonObject();
JsonArray animate = description.get("scripts").getAsJsonObject().get("animate").getAsJsonArray();
if (animation != null) {
for (String animation : animation.animationIds) {
String controller = "controller.animation." + modelId + "." + animation;
animate.add(animation + "_control");
jsonAnimations.addProperty(animation, "animation." + modelId + "." + animation);
jsonAnimations.addProperty(animation + "_control", controller);
}
}
}
public void register() {
String id = "modelengine:" + modelId;
GeyserUtils.addCustomEntity(id);
if (geometry == null) {
return;
}
for (int i = 0; i < Math.ceil(geometry.getBones().size() / 24f); i++) {
GeyserUtils.addProperty(id, "modelengine:bone" + i, Integer.class);
}
if (animation != null) {
for (int i = 0; i < Math.ceil(animation.animationIds.size() / 24f); i++) {
GeyserUtils.addProperty(id, "modelengine:anim" + i, Integer.class);
}
}
GeyserUtils.registerProperties(id);
}
}

View File

@@ -6,10 +6,7 @@ import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.*;
@Getter
@Setter
@@ -17,9 +14,10 @@ import java.util.Locale;
@NoArgsConstructor
public class Geometry {
String modelId;
JsonObject json;
List<String> bones = new ArrayList<>();
Set<String> bones = new HashSet<>();
String path;
public void load(String json) {
@@ -44,11 +42,12 @@ public class Geometry {
String name = element.getAsJsonObject().get("name").getAsString().toLowerCase(Locale.ROOT);
element.getAsJsonObject().remove("name");
element.getAsJsonObject().addProperty("name", name);
if (name.equals("hitbox") ||
name.equals("shadow") ||
name.equals("mount") ||
name.startsWith("p_") ||
name.startsWith("b_") ||
name.startsWith("ob_")) {
iterator.remove();

View File

@@ -4,14 +4,15 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import re.imc.geysermodelenginepackgenerator.GeneratorMain;
import java.util.List;
import java.util.*;
public class RenderController {
public static final Set<String> NEED_REMOVE_WHEN_SORT = Set.of("pbody_", "plarm_", "prarm_", "plleg_", "prleg_", "phead_", "p_");
String modelId;
List<String> bones;
Set<String> bones;
public RenderController(String modelId, List<String> bones) {
public RenderController(String modelId, Set<String> bones) {
this.modelId = modelId;
this.bones = bones;
}
@@ -40,21 +41,37 @@ public class RenderController {
controller.add("textures", textures);
Entity entity = GeneratorMain.entityMap
.get(modelId);
boolean enable = Boolean.parseBoolean(entity.getProperties().getProperty("enable-part-visibility", "false"));
// boolean enable = Boolean.parseBoolean(entity.getConfig().getProperty("enable-part-visibility", "true"));
if (enable) {
JsonArray partVisibility = new JsonArray();
JsonObject visibilityDefault = new JsonObject();
visibilityDefault.addProperty("*", true);
partVisibility.add(visibilityDefault);
for (String bone : bones) {
JsonObject visibilityItem = new JsonObject();
visibilityItem.addProperty(bone, "query.property('" + modelId + ":" + bone + "')");
partVisibility.add(visibilityItem);
// if (enable) {
JsonArray partVisibility = new JsonArray();
JsonObject visibilityDefault = new JsonObject();
visibilityDefault.addProperty("*", true);
partVisibility.add(visibilityDefault);
int i = 0;
List<String> sorted = new ArrayList<>(bones);
Map<String, String> originalId = new HashMap<>();
ListIterator<String> iterator = sorted.listIterator();
while (iterator.hasNext()) {
String s = iterator.next();
String o = s;
for (String r : NEED_REMOVE_WHEN_SORT) {
s = s.replace(r, "");
}
controller.add("part_visibility", partVisibility);
iterator.set(s);
originalId.put(s, o);
}
Collections.sort(sorted);
for (String bone : sorted) {
bone = originalId.get(bone);
JsonObject visibilityItem = new JsonObject();
int n = (int) Math.pow(2, (i % 24));
visibilityItem.addProperty(bone, "math.mod(math.floor(query.property('modelengine:bone" + i / 24 + "') / " + n + "), 2) == 1");
partVisibility.add(visibilityItem);
i++;
}
controller.add("part_visibility", partVisibility);
//}
return root.toString();
}

View File

@@ -0,0 +1,69 @@
package re.imc.geysermodelenginepackgenerator.util;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
public class BooleanPacker {
public static final int MAX_BOOLEANS = 24;
public static int booleansToInt(List<Boolean> booleans) {
int result = 0;
int i = 1;
for (boolean b : booleans) {
if (b) {
result += i;
}
i *= 2;
}
return result;
}
public static int mapBooleansToInt(Map<String, Boolean> booleanMap) {
int result = 0;
int i = 1;
List<String> keys = new ArrayList<>(booleanMap.keySet());
Collections.sort(keys);
for (String key : keys) {
if (booleanMap.get(key)) {
result += i;
}
i *= 2;
}
return result;
}
public static List<Integer> booleansToInts(List<Boolean> booleans) {
List<Integer> results = new ArrayList<>();
int result = 0;
int i = 1;
int i1 = 1;
for (boolean b : booleans) {
if (b) {
result += i;
}
if (i1 % MAX_BOOLEANS == 0 || i1 == booleans.size()) {
results.add(result);
result = 0;
i = 1;
} else {
i *= 2;
}
i1++;
}
return results;
}
public static List<Integer> mapBooleansToInts(Map<String, Boolean> booleanMap) {
List<String> keys = new ArrayList<>(booleanMap.keySet());
List<Boolean> booleans = new ArrayList<>();
Collections.sort(keys);
for (String key : keys) {
booleans.add(booleanMap.get(key));
}
return booleansToInts(booleans);
}
}