mirror of
https://github.com/GeyserExtensionists/GeyserModelEnginePackGenerator.git
synced 2025-12-19 15:09:18 +00:00
new things
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,24 +35,16 @@ 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());
|
||||
newAnimations.add("animation." + modelId + "." + element.getKey(), element.getValue());
|
||||
}
|
||||
json.add("animations", newAnimations);
|
||||
|
||||
@@ -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 =
|
||||
"""
|
||||
{
|
||||
@@ -69,6 +95,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 =
|
||||
"""
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
// if (enable) {
|
||||
JsonArray partVisibility = new JsonArray();
|
||||
JsonObject visibilityDefault = new JsonObject();
|
||||
visibilityDefault.addProperty("*", true);
|
||||
partVisibility.add(visibilityDefault);
|
||||
|
||||
for (String bone : bones) {
|
||||
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, "");
|
||||
}
|
||||
iterator.set(s);
|
||||
originalId.put(s, o);
|
||||
}
|
||||
Collections.sort(sorted);
|
||||
for (String bone : sorted) {
|
||||
bone = originalId.get(bone);
|
||||
JsonObject visibilityItem = new JsonObject();
|
||||
visibilityItem.addProperty(bone, "query.property('" + modelId + ":" + bone + "')");
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user