diff --git a/build.gradle b/build.gradle index 48d842a..0a1aa1f 100644 --- a/build.gradle +++ b/build.gradle @@ -87,6 +87,9 @@ dependencies { modApi "dev.felnull:special-model-loader:1.4.0+pre.1" include "dev.felnull:special-model-loader:1.4.0+pre.1" + implementation "de.javagl:obj:0.4.0" + include "de.javagl:obj:0.4.0" + // SVG Rendering Library include implementation("com.formdev:svgSalamander:1.1.4") diff --git a/src/main/java/org/saturnclient/cosmetics/hat/HatFeatureRenderer.java b/src/main/java/org/saturnclient/cosmetics/hat/HatFeatureRenderer.java index 7608246..3656803 100644 --- a/src/main/java/org/saturnclient/cosmetics/hat/HatFeatureRenderer.java +++ b/src/main/java/org/saturnclient/cosmetics/hat/HatFeatureRenderer.java @@ -1,62 +1,34 @@ package org.saturnclient.cosmetics.hat; -import org.saturnclient.saturnclient.client.player.SaturnPlayer; -import org.saturnclient.saturnclient.SaturnClient; -import net.minecraft.client.render.OverlayTexture; +import java.io.IOException; + +import org.saturnclient.cosmetics.obj.ObjRenderer; + +import de.javagl.obj.Obj; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.entity.feature.FeatureRenderer; import net.minecraft.client.render.entity.feature.FeatureRendererContext; import net.minecraft.client.render.entity.model.PlayerEntityModel; import net.minecraft.client.render.entity.state.PlayerEntityRenderState; -import net.minecraft.client.render.item.ItemRenderer; import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.component.DataComponentTypes; -import net.minecraft.component.type.EquippableComponent; -import net.minecraft.entity.EquipmentSlot; -import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; -import net.minecraft.item.ModelTransformationMode; import net.minecraft.util.Identifier; public class HatFeatureRenderer extends FeatureRenderer { + static Identifier HAT = Identifier.of("saturnclient", "models/item/horns/horns.obj"); + Obj obj; + public HatFeatureRenderer(FeatureRendererContext context) { super(context); + try { + this.obj = ObjRenderer.loadObj(HAT); + } catch (IOException e) { + e.printStackTrace(); + } } @Override public void render(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, PlayerEntityRenderState state, float limbAngle, float limbDistance) { - ItemStack headItem = state.equippedHeadStack; - SaturnPlayer player = SaturnPlayer.get(state.name); - - if (!headItem.isEmpty() || state.invisible || player == null || player.hat.isEmpty()) { - return; - } - - matrices.push(); - - this.getContextModel().head.rotate(matrices); - matrices.multiply(net.minecraft.util.math.RotationAxis.POSITIVE_X.rotationDegrees(180.0f)); - matrices.multiply(net.minecraft.util.math.RotationAxis.POSITIVE_Y.rotationDegrees(180.0f)); - - ItemStack customHat = new ItemStack(Items.STICK); - customHat.set(DataComponentTypes.ITEM_MODEL, - Identifier.of("saturnclient:hat_" + player.hat)); - customHat.set(DataComponentTypes.EQUIPPABLE, - EquippableComponent.builder(EquipmentSlot.HEAD).build()); - - ItemRenderer itemRenderer = SaturnClient.client.getItemRenderer(); - - itemRenderer.renderItem( - customHat, - ModelTransformationMode.THIRD_PERSON_RIGHT_HAND, - light, - OverlayTexture.DEFAULT_UV, - matrices, - vertexConsumers, - null, - 0); - - matrices.pop(); + ObjRenderer.renderObj(this.obj, matrices, vertexConsumers, light, 0); } } \ No newline at end of file diff --git a/src/main/java/org/saturnclient/cosmetics/obj/ObjRenderer.java b/src/main/java/org/saturnclient/cosmetics/obj/ObjRenderer.java new file mode 100644 index 0000000..7c68a08 --- /dev/null +++ b/src/main/java/org/saturnclient/cosmetics/obj/ObjRenderer.java @@ -0,0 +1,74 @@ +package org.saturnclient.cosmetics.obj; + +import java.io.IOException; +import java.io.InputStream; + +import de.javagl.obj.FloatTuple; +import de.javagl.obj.Obj; +import de.javagl.obj.ObjFace; +import de.javagl.obj.ObjReader; +import de.javagl.obj.ObjUtils; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.Identifier; + +public class ObjRenderer { + public static Obj loadObj(Identifier objId) throws IOException { + try (InputStream is = MinecraftClient.getInstance().getResourceManager() + .getResource(objId).get().getInputStream()) { + return ObjUtils.convertToRenderable(ObjReader.read(is)); + } + } + + public static void renderObj(Obj obj, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, + int overlay) { + VertexConsumer consumer = vertexConsumers.getBuffer( + RenderLayer.getEntityAlpha(Identifier.of("textures/misc/white.png"))); + + MatrixStack.Entry entry = matrices.peek(); + + int faces = obj.getNumFaces(); + + for (int f = 0; f < faces; f++) { + ObjFace face = obj.getFace(f); + + for (int i = 0; i < 3; i++) { + writeVertex(obj, face, i, consumer, entry, light, overlay); + } + } + } + + /** Write a single vertex to the VertexConsumer using the OBJ FloatTuple */ + private static void writeVertex(Obj obj, ObjFace face, int index, VertexConsumer consumer, MatrixStack.Entry matrix, + int light, int overlay) { + FloatTuple pos = obj.getVertex(face.getVertexIndex(index)); + float x = pos.getX(); + float y = pos.getY(); + float z = pos.getZ(); + + // Default normal + float nx = 0f; + float ny = 1f; + float nz = 0f; + + // Use OBJ normal if available + int normalIndex = face.getNormalIndex(index); + if (normalIndex >= 0) { + FloatTuple n = obj.getNormal(normalIndex); + nx = n.getX(); + ny = n.getY(); + nz = n.getZ(); + } + + consumer.vertex(matrix, x, y, z) + .color(255, 255, 255, 255) + .texture(0f, 0f) + .overlay(overlay) + .light(light) + .normal(matrix, nx, ny, nz); + } +} \ No newline at end of file