From db4f09ffb8aaa62c0457e2f5a87dd4ef79a0e5c0 Mon Sep 17 00:00:00 2001 From: DrPerkyLegit Date: Sun, 31 May 2026 11:30:27 -0400 Subject: [PATCH 1/4] items now render in the hand when they exist --- Minecraft.Client/ArmorStandRenderer.cpp | 77 ++++++++++++++++++++++++- Minecraft.Client/ArmorStandRenderer.h | 2 + 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/Minecraft.Client/ArmorStandRenderer.cpp b/Minecraft.Client/ArmorStandRenderer.cpp index 96337d92..77bc7c61 100644 --- a/Minecraft.Client/ArmorStandRenderer.cpp +++ b/Minecraft.Client/ArmorStandRenderer.cpp @@ -84,9 +84,22 @@ void ArmorStandRenderer::render(shared_ptr entity, double x, double y, double z, float rot, float a) { + shared_ptr mob = dynamic_pointer_cast(entity); + + float brightness = SharedConstants::TEXTURE_LIGHTING ? 1 : mob->getBrightness(a); + glColor3f(brightness, brightness, brightness); + shared_ptr item = mob->getCarriedItem(); + + prepareCarriedItem(mob, item); + LivingEntityRenderer::render(entity, x, y, z, rot, a); } +void ArmorStandRenderer::prepareCarriedItem(shared_ptr mob, shared_ptr item) +{ + armorLayer->armorModel1->holdingRightHand = armorLayer->armorModel2->holdingRightHand = item != nullptr ? 1 : 0; +} + void ArmorStandRenderer::renderModel(shared_ptr mob, float wp, float ws, float bob, float headRotMinusBodyRot, @@ -165,7 +178,69 @@ void ArmorStandRenderer::renderModel(shared_ptr mob, void ArmorStandRenderer::additionalRendering(shared_ptr mob, float a) { - + + std::shared_ptr item = mob->getCarriedItem(); + if (item != nullptr) + { + glPushMatrix(); + + ArmorStandModel* standModel = ((ArmorStandModel*)model); + + if (standModel->young) { + float s = 0.5f; + glTranslatef(0 / 16.0f, 10 / 16.0f, 0 / 16.0f); + glRotatef(-20, -1, 0, 0); + glScalef(s, s, s); + } + + + standModel->arm1->translateTo(1 / 16.0f); + glTranslatef(-1 / 16.0f, 7 / 16.0f, 1 / 16.0f); + + if (item->id < 256 && TileRenderer::canRender(Tile::tiles[item->id]->getRenderShape()) && item->id != Tile::barrier_Id) + { + float s = 8 / 16.0f; + glTranslatef(-0 / 16.0f, 3 / 16.0f, -5 / 16.0f); + s *= 0.75f; + glRotatef(20, 1, 0, 0); + glRotatef(45, 0, 1, 0); + glScalef(-s, -s, s); + } + else if (item->id == Item::bow_Id) + { + float s = 10 / 16.0f; + glTranslatef(0 / 16.0f, 2 / 16.0f, 5 / 16.0f); + glRotatef(-20, 0, 1, 0); + glScalef(s, -s, s); + glRotatef(-100, 1, 0, 0); + glRotatef(45, 0, 1, 0); + } + else if (Item::items[item->id]->isHandEquipped()) + { + float s = 10 / 16.0f; + glTranslatef(0, 3 / 16.0f, 0); + glScalef(s, -s, s); + glRotatef(-100, 1, 0, 0); + glRotatef(45, 0, 1, 0); + } + else + { + float s = 6 / 16.0f; + glTranslatef(+4 / 16.0f, +3 / 16.0f, -3 / 16.0f); + glScalef(s, s, s); + glRotatef(60, 0, 0, 1); + glRotatef(-90, 1, 0, 0); + glRotatef(20, 0, 0, 1); + } + + this->entityRenderDispatcher->itemInHandRenderer->renderItem(mob, item, 0); + if (item->getItem()->hasMultipleSpriteLayers()) + { + this->entityRenderDispatcher->itemInHandRenderer->renderItem(mob, item, 1); + } + + glPopMatrix(); + } } diff --git a/Minecraft.Client/ArmorStandRenderer.h b/Minecraft.Client/ArmorStandRenderer.h index 9742f31e..9eda1077 100644 --- a/Minecraft.Client/ArmorStandRenderer.h +++ b/Minecraft.Client/ArmorStandRenderer.h @@ -47,5 +47,7 @@ public: float headRotMinusBodyRot, float headRotx, float scale) override; virtual int prepareArmor(shared_ptr mob, int layer, float a) override; + void prepareCarriedItem(shared_ptr mob, shared_ptr item); + virtual void additionalRendering(shared_ptr mob, float a) override; }; \ No newline at end of file From 5857fab1f69ccb1d940822ae4d0a45eb3e2d65cc Mon Sep 17 00:00:00 2001 From: DrPerkyLegit Date: Sun, 31 May 2026 11:32:55 -0400 Subject: [PATCH 2/4] fix default arm rotations --- Minecraft.World/ArmorStand.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Minecraft.World/ArmorStand.cpp b/Minecraft.World/ArmorStand.cpp index 6fcde73a..eb7658a3 100644 --- a/Minecraft.World/ArmorStand.cpp +++ b/Minecraft.World/ArmorStand.cpp @@ -19,8 +19,8 @@ const Rotations ArmorStand::DEFAULT_HEAD_POSE (0.0f, 0.0f, 0.0f); const Rotations ArmorStand::DEFAULT_BODY_POSE (0.0f, 0.0f, 0.0f); -const Rotations ArmorStand::DEFAULT_LEFT_ARM_POSE (-10.0f, 0.0f, -10.0f); -const Rotations ArmorStand::DEFAULT_RIGHT_ARM_POSE (-15.0f, 0.0f, 10.0f); +const Rotations ArmorStand::DEFAULT_LEFT_ARM_POSE (-15.0f, 0.0f, 10.0f); +const Rotations ArmorStand::DEFAULT_RIGHT_ARM_POSE (-10.0f, 0.0f, -10.0f); const Rotations ArmorStand::DEFAULT_LEFT_LEG_POSE (-1.0f, 0.0f, -1.0f); const Rotations ArmorStand::DEFAULT_RIGHT_LEG_POSE (1.0f, 0.0f, 1.0f); From 78759b9f15a4a8760e08df4f86e02c65aa1dc562 Mon Sep 17 00:00:00 2001 From: DrPerkyLegit Date: Sun, 31 May 2026 12:36:32 -0400 Subject: [PATCH 3/4] render elytra on armor stands --- Minecraft.Client/ArmorStandModel.cpp | 1 - Minecraft.Client/ArmorStandRenderer.cpp | 41 +++++++++++++++++++++++++ Minecraft.World/ArmorStand.cpp | 4 +++ Minecraft.World/ArmorStand.h | 4 +++ 4 files changed, 49 insertions(+), 1 deletion(-) diff --git a/Minecraft.Client/ArmorStandModel.cpp b/Minecraft.Client/ArmorStandModel.cpp index c6349eb9..5232eb5c 100644 --- a/Minecraft.Client/ArmorStandModel.cpp +++ b/Minecraft.Client/ArmorStandModel.cpp @@ -8,7 +8,6 @@ ArmorStandModel::ArmorStandModel(float scale) : HumanoidModel(scale) { texWidth = 64; texHeight = 64; - head = new ModelPart(this, 0, 0); head->addBox(-1.0f, -7.0f, -1.0f, 2, 7, 2, scale); diff --git a/Minecraft.Client/ArmorStandRenderer.cpp b/Minecraft.Client/ArmorStandRenderer.cpp index 77bc7c61..15654c65 100644 --- a/Minecraft.Client/ArmorStandRenderer.cpp +++ b/Minecraft.Client/ArmorStandRenderer.cpp @@ -16,6 +16,7 @@ #include "PlayerRenderer.h" #include "../Minecraft.World/SkullItem.h" #include "../Minecraft.World/SkullTileEntity.h" +#include "../Minecraft.World/ElytraItem.h" static const float DEG_TO_RAD = 3.14159265f / 180.0f; @@ -179,6 +180,46 @@ void ArmorStandRenderer::renderModel(shared_ptr mob, void ArmorStandRenderer::additionalRendering(shared_ptr mob, float a) { + { + shared_ptr chestItem = mob->getEquipmentSlots()[ArmorStand::SLOT_CHEST]; + if (chestItem != nullptr && dynamic_cast(chestItem->getItem()) != nullptr) + { + static ResourceLocation elytraTexture(L"item/elytra.png"); + bindTexture(&elytraTexture); + + float brightness2 = SharedConstants::TEXTURE_LIGHTING ? 1 : mob->getBrightness(a); + glColor3f(brightness2, brightness2, brightness2); + + ArmorStandModel* standModel = ((ArmorStandModel*)this->model); + ArmorStand* stand = dynamic_cast(mob.get()); + + float wf = 0.2617994f; + float wf1 = -0.2617994f; + float wf2 = standModel->body->y; + float wf3 = 0.0f; + + stand->rotateElytraX += (wf - stand->rotateElytraX) * 0.3f; + stand->rotateElytraY += (wf3 - stand->rotateElytraY) * 0.3f; + stand->rotateElytraZ += (wf1 - stand->rotateElytraZ) * 0.3f; + + + standModel->elytraRight->y = wf2; + standModel->elytraRight->xRot = stand->rotateElytraX; + standModel->elytraRight->yRot = stand->rotateElytraY; + standModel->elytraRight->zRot = stand->rotateElytraZ; + + standModel->elytraLeft->y = wf2; + standModel->elytraLeft->xRot = stand->rotateElytraX; + standModel->elytraLeft->yRot = -stand->rotateElytraY; + standModel->elytraLeft->zRot = -stand->rotateElytraZ; + + glPushMatrix(); + glTranslatef(0, 0.0f, (2.0f + 0.125f) / 16.0f); + standModel->renderElytra(1 / 16.0f, true); + glPopMatrix(); + } + } + std::shared_ptr item = mob->getCarriedItem(); if (item != nullptr) { diff --git a/Minecraft.World/ArmorStand.cpp b/Minecraft.World/ArmorStand.cpp index eb7658a3..e83efe54 100644 --- a/Minecraft.World/ArmorStand.cpp +++ b/Minecraft.World/ArmorStand.cpp @@ -45,6 +45,10 @@ void ArmorStand::init() for (int i = 0; i < equipmentCount; i++) equipment[i] = nullptr; + rotateElytraX = 0.2617994f; + rotateElytraY = 0.0f; + rotateElytraZ = -0.2617994f; + headPose = DEFAULT_HEAD_POSE; bodyPose = DEFAULT_BODY_POSE; leftArmPose = DEFAULT_LEFT_ARM_POSE; diff --git a/Minecraft.World/ArmorStand.h b/Minecraft.World/ArmorStand.h index 629d08aa..1e18cb47 100644 --- a/Minecraft.World/ArmorStand.h +++ b/Minecraft.World/ArmorStand.h @@ -56,6 +56,10 @@ private: bool isMarkerFlag; bool noPhysics; float standDamage; +public: + float rotateElytraX; + float rotateElytraY; + float rotateElytraZ; public: long long lastHit; From f8c93495107224ec0cb5dce9efd747a231f0b4aa Mon Sep 17 00:00:00 2001 From: DrPerkyLegit Date: Sun, 31 May 2026 13:57:04 -0400 Subject: [PATCH 4/4] items render correctly on the head --- Minecraft.Client/CustomHeadLayer.cpp | 142 ++++++++++++++++++++++++++- Minecraft.Client/CustomHeadLayer.h | 5 +- 2 files changed, 141 insertions(+), 6 deletions(-) diff --git a/Minecraft.Client/CustomHeadLayer.cpp b/Minecraft.Client/CustomHeadLayer.cpp index 72f9955a..44c062c2 100644 --- a/Minecraft.Client/CustomHeadLayer.cpp +++ b/Minecraft.Client/CustomHeadLayer.cpp @@ -13,11 +13,19 @@ #include "../Minecraft.World/SkullTileEntity.h" #include "../Minecraft.World/LivingEntity.h" #include "../Minecraft.World/Facing.h" +#include "../Minecraft.Client/SimpleIcon.h" +#include "../Minecraft.World/AirTile.h" CustomHeadLayer::CustomHeadLayer(ModelPart* headPart, LivingEntityRenderer* parentRenderer) : headPart(headPart), parentRenderer(parentRenderer) { + tileRenderer = new TileRenderer(); + +} + +CustomHeadLayer::~CustomHeadLayer() { + delete tileRenderer; } int CustomHeadLayer::colorsOnDamage() @@ -90,12 +98,136 @@ void CustomHeadLayer::render(shared_ptr mob, glRotatef(90.0f, 0.0f, 1.0f, 0.0f); - Minecraft* mc = Minecraft::GetInstance(); - if (mc) { - auto* iihr = mc->getItemInHandRenderer(); - if (iihr) - iihr->renderItem(mob, helmet, 0, true); + // 4J - code borrowed from render method below, although not factoring in brightness as that should already be being taken into account + // by texture lighting. This is for colourising things held in 3rd person view. + if ((item != nullptr)) { + int col = Item::items[item->id]->getColor(helmet, 0); + float red = ((col >> 16) & 0xff) / 255.0f; + float g = ((col >> 8) & 0xff) / 255.0f; + float b = ((col) & 0xff) / 255.0f; + + glColor4f(red, g, b, 1); + } + + Minecraft* minecraft = Minecraft::GetInstance(); + + glPushMatrix(); + + Tile* tile = Tile::tiles[item->id]; + if ((item->getIconType() == Icon::TYPE_TERRAIN && tile != nullptr && TileRenderer::canRender(tile->getRenderShape())) && item->id != AirTile::barrier_Id) + { + MemSect(31); + minecraft->textures->bindTexture(minecraft->textures->getTextureLocation(Icon::TYPE_TERRAIN)); + MemSect(0); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + tileRenderer->renderTile(Tile::tiles[item->id], helmet->getAuxValue(), SharedConstants::TEXTURE_LIGHTING ? 1.0f : mob->getBrightness(1)); // 4J - change brought forward from 1.8.2 + glDisable(GL_BLEND); + } + else + { + MemSect(31); + Icon* icon = mob->getItemInHandIcon(helmet, 0); + if (icon == nullptr) + { + glPopMatrix(); + MemSect(0); + return; + } + + bool bIsTerrain = item->getIconType() == Icon::TYPE_TERRAIN; + minecraft->textures->bindTexture(minecraft->textures->getTextureLocation(item->getIconType())); + + MemSect(0); + Tesselator* t = Tesselator::getInstance(); + + // Consider forcing the mipmap LOD level to use, if this is to be rendered from a larger than standard source texture. + int iconWidth = icon->getWidth(); + int LOD = -1; // Default to not doing anything special with LOD forcing + if (iconWidth == 32) + { + LOD = 1; // Force LOD level 1 to achieve texture reads from 256x256 map + } + else if (iconWidth == 64) + { + LOD = 2; // Force LOD level 2 to achieve texture reads from 256x256 map + } + RenderManager.StateSetForceLOD(LOD); + + // 4J Original comment + // Yes, these are backwards. + // No, I don't know why. + // 4J Stu - Make them the right way round...u coords were swapped + float u0 = icon->getU0(); + float u1 = icon->getU1(); + float v0 = icon->getV0(); + float v1 = icon->getV1(); + + float xo = 0.0f; + float yo = 0.3f; + + // Re position height of held item if skin is small + if (mob->getAnimOverrideBitmask() & (1 << HumanoidModel::eAnim_SmallModel)) + { + if (mob->isRiding()) + { + std::shared_ptr ridingEntity = mob->riding; + if (ridingEntity != nullptr) // Safety check; + { + yo += 0.3f; // reverts the change in Boat.cpp for smaller models. + } + } + } + + glEnable(GL_RESCALE_NORMAL); + glTranslatef(-xo, -yo, 0); + glScalef(1, 1, 1); + + glRotatef(90, 0, 1, 0); + glTranslatef(-7.5f / 16.0f, 9 / 16.0f, 0); + glTranslatef(0, 0.03f, 0); + float dd = 1 / 16.0f; + + ItemInHandRenderer::renderItem3D(t, u0, v0, u1, v1, icon->getSourceWidth(), icon->getSourceHeight(), 1 / 16.0f, false, bIsTerrain); + + if (item != nullptr && helmet->isFoil()) + { + glDepthFunc(GL_EQUAL); + glDisable(GL_LIGHTING); + minecraft->textures->bindTexture(&ItemInHandRenderer::ENCHANT_GLINT_LOCATION); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_COLOR, GL_ONE); + float br = 0.76f; + glColor4f(0.5f * br, 0.25f * br, 0.8f * br, 1); // MGH - for some reason this colour isn't making it through to the render, so I've added to the tesselator for the glint geom above + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + float ss = 1 / 8.0f; + glScalef(ss, ss, ss); + float sx = Minecraft::currentTimeMillis() % (3000) / (3000.0f) * 8; + glTranslatef(sx, 0, 0); + glRotatef(-50, 0, 0, 1); + + ItemInHandRenderer::renderItem3D(t, 0, 0, 1, 1, 256, 256, 1 / 16.0f, true, bIsTerrain); + glPopMatrix(); + glPushMatrix(); + glScalef(ss, ss, ss); + sx = System::currentTimeMillis() % (3000 + 1873) / (3000 + 1873.0f) * 8; + glTranslatef(-sx, 0, 0); + glRotatef(10, 0, 0, 1); + ItemInHandRenderer::renderItem3D(t, 0, 0, 1, 1, 256, 256, 1 / 16.0f, true, bIsTerrain); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glDisable(GL_BLEND); + glEnable(GL_LIGHTING); + glDepthFunc(GL_LEQUAL); + } + + RenderManager.StateSetForceLOD(-1); + + glDisable(GL_RESCALE_NORMAL); + } + glPopMatrix(); } } diff --git a/Minecraft.Client/CustomHeadLayer.h b/Minecraft.Client/CustomHeadLayer.h index da6814c6..1d09cc0e 100644 --- a/Minecraft.Client/CustomHeadLayer.h +++ b/Minecraft.Client/CustomHeadLayer.h @@ -4,14 +4,17 @@ class ModelPart; class LivingEntity; class LivingEntityRenderer; +class TileRenderer; class CustomHeadLayer : public RenderLayer { public: ModelPart* headPart; LivingEntityRenderer* parentRenderer; + TileRenderer* tileRenderer; + CustomHeadLayer(ModelPart* headPart, LivingEntityRenderer* parentRenderer); - virtual ~CustomHeadLayer() {} + virtual ~CustomHeadLayer(); virtual int colorsOnDamage() override; virtual void render(shared_ptr mob,