ItemStackTileEntityRenderer(物品特殊渲染)
这一节中,我们将要来学习Item的特殊渲染:ItemStackTileEntityRenderer简称ISTER,它的作用和TileEntityRenderer非常类似,在以前ItemStackTileEntityRenderer甚至就是靠TileEntityRenderer实现的。
利用ISTER你可以实现一些非常酷的渲染效果,举例来说Create(机械动力)中的扳手,它会自动旋转的齿轮就是利用ISTER实现的。
首先我们先来看物品的代码
public class ObsidianWrench extends Item {
public ObsidianWrench() {
super(new Properties().group(ModGroup.itemGroup).setISTER(() -> {
return () -> {
return new ObsidianWrenchItemStackTileEntityRenderer();
};
}));
}
}
可以看到物品的代码其实很简单,这里唯一特别的地方就是我们调用了setISTER方法,为我们的物品绑定了一个setISTER。但是这样绑定还不能直接用,我还得替换物品原本的IBakedModel,并在其中启用ISTER。
接下来我们来看替换物品IBakedModel的代码
@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD, value = Dist.CLIENT)
public class ModBusEventHandler {
@SubscribeEvent
public static void onModelBaked(ModelBakeEvent event) {
Map<ResourceLocation, IBakedModel> modelRegistry = event.getModelRegistry();
ModelResourceLocation location = new ModelResourceLocation(ItemRegistry.obsidianWrench.get().getRegistryName(), "inventory");
IBakedModel existingModel = modelRegistry.get(location);
if (existingModel == null) {
throw new RuntimeException("Did not find Obsidian Hidden in registry");
} else if (existingModel instanceof ObsidianWrenchBakedModel) {
throw new RuntimeException("Tried to replaceObsidian Hidden twice");
} else {
ObsidianWrenchBakedModel obsidianWrenchBakedModel = new ObsidianWrenchBakedModel(existingModel);
event.getModelRegistry().put(location, obsidianWrenchBakedModel);
}
}
}
可以看到,我们同样监听了ModelBakeEvent,然后通过modelRegistry.get获取了默认的IBakedModel并将它传入我们新的IBakedModel中,然后调用event.getModelRegistry().put替换了原版的IBakedModel,这里也别忘了value = Dist.CLIENT。
接下来看我们的IBakedModel:
public class ObsidianWrenchBakedModel implements IBakedModel {
private IBakedModel existingModel;
public ObsidianWrenchBakedModel(IBakedModel existingModel) {
this.existingModel = existingModel;
}
@Nonnull
@Override
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, @Nonnull Random rand, @Nonnull IModelData extraData) {
throw new AssertionError("IForgeBakedModel::getQuads should never be called, only IForgeBakedModel::getQuads");
}
@Override
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, Random rand) {
return this.existingModel.getQuads(state, side, rand);
}
@Override
public boolean isAmbientOcclusion() {
return this.existingModel.isAmbientOcclusion();
}
@Override
public boolean isGui3d() {
return this.existingModel.isGui3d();
}
@Override
public boolean func_230044_c_() {
return this.existingModel.func_230044_c_();
}
@Override
public boolean isBuiltInRenderer() {
return true;
}
@Override
public TextureAtlasSprite getParticleTexture() {
return this.existingModel.getParticleTexture();
}
@Override
public ItemOverrideList getOverrides() {
return this.existingModel.getOverrides();
}
@Override
public IBakedModel handlePerspective(ItemCameraTransforms.TransformType cameraTransformType, MatrixStack mat) {
if (cameraTransformType == ItemCameraTransforms.TransformType.FIRST_PERSON_RIGHT_HAND || cameraTransformType == ItemCameraTransforms.TransformType.FIRST_PERSON_LEFT_HAND) {
return this;
}
return this.existingModel.handlePerspective(cameraTransformType, mat);
}
}
可以看到,这里和之前创建的IBakedModel并无太大差别。同样是保存了一个原本的IBakedModel。
这里我们来说说他们区别:
第一,对于物品的IBakedModel来说,只会调用IBakedModel#getQuads而不会调用IForgeBakedModel::getQuads,你可以和之前方块的IBakedModel做对比,可以发现刚好是相反的。
第二,对于物品,你可以通过 handlePerspective这个方块来选择不同TransformType下的IBakedModel,具体的TransformType请自行翻阅模型相关的Wiki,这里我们希望在第一人称的视角下用ISTER渲染我们的模型,所以在if语句中返回了this,注意只有当你这里返回了this的TransformType,才会启用ISTER渲染。
第三,你可以在getOverrides处理物品json文件中的Override,什么是Overrides请执行翻阅wiki。
第四,也是最重要的,如果你希望你的IBakedModel被ISTER渲染,你必须在isBuiltInRenderer返回true,如果你没有给你的物品中调用setISTER指定自定义的ISTER,默认会使用ItemStackTileEntityRenderer.instance渲染。
接下就是最为关键的部分,我们的ISTER
public class ObsidianWrenchItemStackTileEntityRenderer extends ItemStackTileEntityRenderer {
private int degree = 0;
@Override
public void render(ItemStack itemStackIn, MatrixStack matrixStackIn, IRenderTypeBuffer bufferIn, int combinedLightIn, int combinedOverlayIn) {
if (degree == 360) {
degree = 0;
}
degree++;
ItemRenderer itemRenderer = Minecraft.getInstance().getItemRenderer();
IBakedModel ibakedmodel = itemRenderer.getItemModelWithOverrides(itemStackIn, null, null);
matrixStackIn.push();
matrixStackIn.translate(0.5F, 0.5F, 0.5F);
float xOffset = -1 / 32f;
float zOffset = 0;
matrixStackIn.translate(-xOffset, 0, -zOffset);
matrixStackIn.rotate(Vector3f.YP.rotationDegrees(degree));
matrixStackIn.translate(xOffset, 0, zOffset);
itemRenderer.renderItem(itemStackIn, ItemCameraTransforms.TransformType.NONE, false, matrixStackIn, bufferIn, combinedLightIn, combinedOverlayIn, ibakedmodel.getBakedModel());
matrixStackIn.pop();
}
}
可以看到我们的ISTER类直接继承了ItemStackTileEntityRenderer,ItemStackTileEntityRenderer只有一个方法,即render方法,这里的render方法和之前我们学习过的TER里的render方法作用类似。你可以在这里渲染,你想要渲染的东西。
我只在这里渲染了,我们的物品模型然后在调整完位置之后。让模型旋转起来。
float xOffset = -1 / 32f;
float zOffset = 0;
matrixStackIn.translate(-xOffset, 0, -zOffset);
matrixStackIn.rotate(Vector3f.YP.rotationDegrees(degree));
matrixStackIn.translate(xOffset, 0, zOffset);
这段就是让模型能按其中轴线旋转的代码,来源是Create Mod 的WrenchItemRenderer。
因为render每一帧都会被调用一次。
if (degree == 360) {
degree = 0;
}
degree++;
所以你可以利用这样写出平滑的旋转动画。
到此,我们的物品特殊渲染就完成了。
打开游戏看看,你应该就能看到一个会自动旋转的物品了。