Skip to content

Commit

Permalink
完成模型切换(未添加到屏幕)
Browse files Browse the repository at this point in the history
  • Loading branch information
CSneko committed Nov 10, 2024
1 parent cc5bc48 commit 5135a41
Show file tree
Hide file tree
Showing 13 changed files with 178 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import net.minecraft.world.entity.player.Player;
import org.ayamemc.ayame.client.renderer.GeoPlayerRender;
import org.ayamemc.ayame.model.AyameModelCache;
import org.ayamemc.ayame.model.AyameModelType;
import org.ayamemc.ayame.model.ModelType;

public class PlayerModelAPI {
/**
Expand All @@ -32,7 +32,7 @@ public class PlayerModelAPI {
* @param player 玩家
* @param model 模型
*/
public static void switchModel(Player player, AyameModelType model) {
public static void switchModel(Player player, ModelType model) {
GeoPlayerRender.GeoPlayerModel.switchModel(player, model);
}

Expand All @@ -42,7 +42,7 @@ public static void switchModel(Player player, AyameModelType model) {
* @param player 玩家
* @param model 模型
*/
public static void switchModelOnClient(Player player, AyameModelType model) {
public static void switchModelOnClient(Player player, ModelType model) {
AyameModelCache.setPlayerModel(player, model);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
import net.minecraft.resources.ResourceLocation;
import org.ayamemc.ayame.client.api.ModelResourceAPI;
import org.ayamemc.ayame.model.AyameModelCache;
import org.ayamemc.ayame.model.AyameModelType;
import org.ayamemc.ayame.model.ModelType;
import org.ayamemc.ayame.model.resource.IModelResource;
import org.ayamemc.ayame.util.ConfigUtil;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -70,7 +70,7 @@ public class ModelSelectMenuScreen extends AyameMainScreen {
protected static final Path MODEL_DIR = Path.of("config/ayame/models/");
public final boolean skipWarningOnce;
public final List<IModelResource> modelResources;
public @Nullable AyameModelType selectedModel = AyameModelCache.getPlayerModel(Minecraft.getInstance().player);
public @Nullable ModelType selectedModel = AyameModelCache.getPlayerModel(Minecraft.getInstance().player);
public @Nullable CloseCallback closeCallback;
public @Nullable SwitchModelCallback switchModelCallback;

Expand Down Expand Up @@ -242,14 +242,14 @@ public void onClose() {
*/
@FunctionalInterface
public interface CloseCallback {
void close(List<IModelResource> modelResources, @Nullable AyameModelType selectedModel);
void close(List<IModelResource> modelResources, @Nullable ModelType selectedModel);
}

/**
* 模型切换回调函数的接口,允许在模型切换时进行自定义处理。
*/
@FunctionalInterface
public interface SwitchModelCallback {
void switchModel(List<IModelResource> modelResources, @Nullable AyameModelType selectedModel);
void switchModel(List<IModelResource> modelResources, @Nullable ModelType selectedModel);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
import net.minecraft.world.entity.Pose;
import net.minecraft.world.entity.player.Player;
import org.ayamemc.ayame.model.AyameModelCache;
import org.ayamemc.ayame.model.AyameModelType;
import org.ayamemc.ayame.model.ModelType;
import org.jetbrains.annotations.Nullable;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.model.GeoModel;
Expand Down Expand Up @@ -71,9 +71,9 @@ public GeoPlayerModel() {
/**
* 将玩家模型切换为对应外观,TODO: 同时告诉服务器
*
* @param model 传入{@link AyameModelType}类型的模型资源
* @param model 传入{@link ModelType}类型的模型资源
*/
public static void switchModel(Player player, AyameModelType model) {
public static void switchModel(Player player, ModelType model) {
AyameModelCache.setPlayerModel(player, model);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Custom player model mod. Powered by GeckoLib.
* Copyright (C) 2024 CrystalNeko, HappyRespawnanchor, pertaz(Icon Designer)
*
* This file is part of Ayame.
*
* Ayame is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Ayame is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ayame. If not, see <https://www.gnu.org/licenses/>.
*/

package org.ayamemc.ayame.client.util;

import com.google.gson.JsonObject;
import com.mojang.blaze3d.platform.NativeImage;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.GsonHelper;
import org.ayamemc.ayame.model.DefaultModelType;
import org.ayamemc.ayame.model.resource.IModelResource;
import org.jetbrains.annotations.NotNull;
import software.bernie.geckolib.cache.GeckoLibCache;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.loading.json.raw.Model;
import software.bernie.geckolib.loading.json.typeadapter.KeyFramesAdapter;
import software.bernie.geckolib.loading.object.BakedAnimations;
import software.bernie.geckolib.loading.object.BakedModelFactory;
import software.bernie.geckolib.loading.object.GeometryTree;

import java.io.IOException;
import java.util.Map;
import static org.ayamemc.ayame.Ayame.MOD_ID;
/**
* 用于向GeckoLib缓存和贴图写入新模型的工具类
*
* @see GeckoLibCache
*/
@Environment(EnvType.CLIENT)
public class ModelResourceWriterUtil {
/**
* @param modelRes 模型资源
* @return 未完成的模型构建器
*/
public static DefaultModelType.Builder addModelResource(@NotNull IModelResource modelRes) {
addBakedModel(modelRes.createModelResourceLocation(), modelRes);
addBakedAnimation(modelRes.createAnimationResourceLocation(), modelRes);
addTexture(modelRes.createTextureResourceLocation(), modelRes);
return DefaultModelType.Builder.create()
.setGeoModel(modelRes.createModelResourceLocation())
.setAnimation(modelRes.createAnimationResourceLocation())
.setTexture(modelRes.createTextureResourceLocation());
}
/**
* 向模型缓存中添加新条目
*
* @param resourceLocation 传入{@link ResourceLocation}类型的文件路径
* @param modelRes 模型资源
*/
public static void addBakedModel(ResourceLocation resourceLocation, @NotNull IModelResource modelRes) {
Map<ResourceLocation, BakedGeoModel> models = GeckoLibCache.getBakedModels();
// 如果已经存在了
if (models.containsKey(resourceLocation)) return;
Model m = KeyFramesAdapter.GEO_GSON.fromJson(GsonHelper.fromJson(KeyFramesAdapter.GEO_GSON, modelRes.getModelJson(modelRes.getDefault()).toString(), JsonObject.class), Model.class);
BakedGeoModel bakedGeoModel = BakedModelFactory.getForNamespace(MOD_ID).constructGeoModel(GeometryTree.fromModel(m));
models.put(resourceLocation, bakedGeoModel);
}
/**
* 向动画缓存中添加新条目
*
* @param resourceLocation 传入{@link ResourceLocation}类型的文件路径
* @param modelRes 模型资源
* @see ResourceLocation
*/
public static void addBakedAnimation(ResourceLocation resourceLocation, @NotNull IModelResource modelRes) {
Map<ResourceLocation, BakedAnimations> animations = GeckoLibCache.getBakedAnimations();
if (animations.containsKey(resourceLocation)) return;
BakedAnimations ani = KeyFramesAdapter.GEO_GSON.fromJson(GsonHelper.getAsJsonObject(modelRes.getAnimationJson(modelRes.getDefault()).toGson(), "animations"), BakedAnimations.class);
animations.put(resourceLocation, ani);
}
/**
* 注册贴图
*
* @param resourceLocation 传入{@link ResourceLocation}类型的文件路径
* @param modelRes 模型资源
*/
public static void addTexture(ResourceLocation resourceLocation, @NotNull IModelResource modelRes) {
try {
Minecraft.getInstance().getTextureManager().register(resourceLocation, new DynamicTexture(NativeImage.read(modelRes.getTexture(modelRes.getDefault()))));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@
*/
public class AyameModelCache {
// 考虑到未来可能涩及多线程操作,所以使用 ConcurrentHashMap
public static Map<Player, AyameModelType> playerModelCache = new ConcurrentHashMap<>();
public static Map<Player, ModelType> playerModelCache = new ConcurrentHashMap<>();

public static void setPlayerModel(Player player, AyameModelType model) {
public static void setPlayerModel(Player player, ModelType model) {
playerModelCache.put(player, model);
}

Expand All @@ -48,7 +48,7 @@ public static void removePlayerModel(Player player) {
* @return 玩家模型
*/
@NotNull
public static AyameModelType getPlayerModel(Player player) {
public static ModelType getPlayerModel(Player player) {
return playerModelCache.getOrDefault(player, DefaultModels.DEFAULT_MODEL);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
* @param metaData 模型元数据
*/

public record DefaultAyameModelType(ResourceLocation geoModel, ResourceLocation animation, ResourceLocation texture,
IndexData.ModelMetaData metaData) implements AyameModelType {
public record DefaultModelType(ResourceLocation geoModel, ResourceLocation animation, ResourceLocation texture,
IndexData.ModelMetaData metaData) implements ModelType {


@Override
Expand Down Expand Up @@ -86,8 +86,8 @@ public Builder setMetaData(IndexData.ModelMetaData metaData) {
return this;
}

public DefaultAyameModelType build() {
return new DefaultAyameModelType(geoModel, animation, texture, metaData);
public DefaultModelType build() {
return new DefaultModelType(geoModel, animation, texture, metaData);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,15 @@
import org.ayamemc.ayame.util.FileUtil;

import java.nio.file.Path;
import java.util.List;

import static org.ayamemc.ayame.util.ResourceLocationHelper.withAyameNamespace;

public class DefaultModels {
public static final String MODEL_PATH = "config/ayame/models/";
public static final AyameModelType DEFAULT_MODEL = DefaultAyameModelType.Builder.create()
.setGeoModel(withAyameNamespace("geo/ayame/default"))
.setAnimation(withAyameNamespace("animations/ayame/default"))
.setTexture(withAyameNamespace("textures/ayame/default"))
public static final ModelType DEFAULT_MODEL = DefaultModelType.Builder.create()
.setGeoModel(withAyameNamespace("geo/ayame/default.json"))
.setAnimation(withAyameNamespace("animations/ayame/default.json"))
.setTexture(withAyameNamespace("textures/ayame/default.png"))
.setMetaData(IndexData.ModelMetaData.Builder.create()
.setName("default")
.setAuthors(new String[]{"CrystalNeko"})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
///
/// 模型类型指的是使用了哪种模型,例如ayame的模型类型为`ayame`,兼容ysm的为`ysm`,值与{@link ModelMetaData#type()}的值对应

public interface AyameModelType {
public interface ModelType {
/**
* 从geckolib缓存中获取模型资源
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,44 @@

package org.ayamemc.ayame.model.resource;

import org.ayamemc.ayame.model.AyameModelType;
import net.minecraft.resources.ResourceLocation;
import org.ayamemc.ayame.model.AyameModelCache;
import org.ayamemc.ayame.model.DefaultModelType;
import org.ayamemc.ayame.model.ModelType;
import org.ayamemc.ayame.model.IndexData;
import org.ayamemc.ayame.util.JsonInterpreter;
import org.ayamemc.ayame.util.TODO;

import java.io.File;
import java.nio.file.Path;
import java.io.InputStream;
import java.util.List;
import static org.ayamemc.ayame.util.ResourceLocationHelper.withAyameNamespace;

public interface IModelResource {
// TODO 完成
IndexData.ModelMetaData getMetaData();
default String getName(){
return getMetaData().name();
}

List<IndexData.ModelData> getModels();

JsonInterpreter getModelJson(IndexData.ModelData model);
JsonInterpreter getAnimationJson(IndexData.ModelData model);
InputStream getTexture(IndexData.ModelData model);

default IndexData.ModelData getDefault() {
return getModels().getFirst();
}
default ResourceLocation createModelResourceLocation(){
return withAyameNamespace("geo/" + getName() + ".json");
}
default ResourceLocation createAnimationResourceLocation(){
return withAyameNamespace("animations/" + getName() + ".json");
}
default ResourceLocation createTextureResourceLocation(){
return withAyameNamespace("textures/" + getName() + ".png");
}

static IModelResource fromFile(File file){
throw new TODO("create model from file");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import java.util.concurrent.CopyOnWriteArrayList;

/**
* 客户端模型资源的缓存
* 模型资源的缓存
*/
public class ModelResourceCache {
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.ayamemc.ayame.util.TODO;
import org.jetbrains.annotations.ApiStatus;

import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipFile;
Expand Down Expand Up @@ -105,5 +106,9 @@ public JsonInterpreter getIndexJson(){
public String getFormat(){
return getIndexJson().getString("format");
}

public InputStream getContent(String path){
return FileUtil.getInputStreamFromZip(zipFile, path);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
package org.ayamemc.ayame.model.resource;

import org.ayamemc.ayame.model.IndexData;
import org.ayamemc.ayame.util.JsonInterpreter;

import java.io.InputStream;
import java.util.List;

public class SimpleModelResource implements IModelResource{
Expand Down Expand Up @@ -50,4 +52,19 @@ public List<IndexData.ModelData> getModels() {
.build()
);
}

@Override
public JsonInterpreter getModelJson(IndexData.ModelData model) {
return JsonInterpreter.of(modelFile.getContent(model.model()));
}

@Override
public JsonInterpreter getAnimationJson(IndexData.ModelData model) {
return JsonInterpreter.of(modelFile.getContent(model.animation()));
}

@Override
public InputStream getTexture(IndexData.ModelData model) {
return modelFile.getContent(model.texture());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,15 @@
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
import net.minecraft.client.KeyMapping;
import net.minecraft.client.Minecraft;
import org.ayamemc.ayame.client.AyameClient;
import org.ayamemc.ayame.client.api.PlayerModelAPI;
import org.ayamemc.ayame.client.gui.screen.ModelSelectMenuScreen;
import org.ayamemc.ayame.client.handler.EventHandler;
import org.ayamemc.ayame.client.util.ModelResourceWriterUtil;
import org.ayamemc.ayame.fabric.client.util.AyameTMSKeyMappings;
import org.ayamemc.ayame.mixin.PlayerMixin;
import org.ayamemc.ayame.model.DefaultModels;
import org.ayamemc.ayame.util.JavaUtil;
import org.ayamemc.ayame.util.TranslatableName;
import org.jetbrains.annotations.Nullable;
Expand Down

0 comments on commit 5135a41

Please sign in to comment.