aboutsummaryrefslogtreecommitdiff
path: root/Minecraft.Client
diff options
context:
space:
mode:
authordaoge_cmd <3523206925@qq.com>2026-03-03 22:12:59 +0800
committerdaoge_cmd <3523206925@qq.com>2026-03-04 14:55:13 +0800
commit575cc4ce6ea59ef2acee6f81181bc80f5ed4b7d9 (patch)
tree9fd9bd2e8e2e64d991bea12679d9781b8fac664b /Minecraft.Client
parentb1b4435c0101341b1aa196c7b13c939c02a9eaec (diff)
fix: fix horse texture rendering
Diffstat (limited to 'Minecraft.Client')
-rw-r--r--Minecraft.Client/Textures.cpp101
1 files changed, 99 insertions, 2 deletions
diff --git a/Minecraft.Client/Textures.cpp b/Minecraft.Client/Textures.cpp
index ab9db75d..b4817ee3 100644
--- a/Minecraft.Client/Textures.cpp
+++ b/Minecraft.Client/Textures.cpp
@@ -424,12 +424,109 @@ void Textures::bindTextureLayers(ResourceLocation *resource)
{
assert(resource->isPreloaded());
+ // Hack: 4JLibs on Windows does not currently reproduce Minecraft's layered horse texture path reliably.
+ // Merge the layers on the CPU and bind the cached result as a normal single texture instead.
+ wstring cacheKey = L"%layered%";
int layers = resource->getTextureCount();
-
for( int i = 0; i < layers; i++ )
{
- RenderManager.TextureBind(loadTexture(resource->getTexture(i)));
+ cacheKey += std::to_wstring(resource->getTexture(i));
+ cacheKey += L"/";
+ }
+
+ int id = -1;
+ bool inMap = ( idMap.find(cacheKey) != idMap.end() );
+ if( inMap )
+ {
+ id = idMap[cacheKey];
+ }
+ else
+ {
+ // Cache by layer signature so the merge cost is only paid once per horse texture combination.
+ intArray mergedPixels;
+ int mergedWidth = 0;
+ int mergedHeight = 0;
+ bool hasMergedPixels = false;
+
+ for( int i = 0; i < layers; i++ )
+ {
+ TEXTURE_NAME textureName = resource->getTexture(i);
+ if( textureName == (_TEXTURE_NAME)-1 )
+ {
+ continue;
+ }
+
+ wstring resourceName = wstring(preLoaded[textureName]) + L".png";
+ BufferedImage *image = readImage(textureName, resourceName);
+ if( image == NULL )
+ {
+ continue;
+ }
+
+ int width = image->getWidth();
+ int height = image->getHeight();
+ intArray layerPixels = loadTexturePixels(image);
+ delete image;
+
+ if( !hasMergedPixels )
+ {
+ mergedWidth = width;
+ mergedHeight = height;
+ mergedPixels = intArray(width * height);
+ memcpy(mergedPixels.data, layerPixels.data, width * height * sizeof(int));
+ hasMergedPixels = true;
+ }
+ else if( width == mergedWidth && height == mergedHeight )
+ {
+ for( int p = 0; p < width * height; p++ )
+ {
+ int dst = mergedPixels[p];
+ int src = layerPixels[p];
+
+ float srcAlpha = ((src >> 24) & 0xff) / 255.0f;
+ if( srcAlpha <= 0.0f )
+ {
+ continue;
+ }
+
+ float dstAlpha = ((dst >> 24) & 0xff) / 255.0f;
+ float outAlpha = srcAlpha + dstAlpha * (1.0f - srcAlpha);
+ if( outAlpha <= 0.0f )
+ {
+ mergedPixels[p] = 0;
+ continue;
+ }
+
+ float srcFactor = srcAlpha / outAlpha;
+ float dstFactor = (dstAlpha * (1.0f - srcAlpha)) / outAlpha;
+
+ int outA = (int)(outAlpha * 255.0f + 0.5f);
+ int outR = (int)((((src >> 16) & 0xff) * srcFactor) + (((dst >> 16) & 0xff) * dstFactor) + 0.5f);
+ int outG = (int)((((src >> 8) & 0xff) * srcFactor) + (((dst >> 8) & 0xff) * dstFactor) + 0.5f);
+ int outB = (int)(((src & 0xff) * srcFactor) + ((dst & 0xff) * dstFactor) + 0.5f);
+ mergedPixels[p] = (outA << 24) | (outR << 16) | (outG << 8) | outB;
+ }
+ }
+
+ delete[] layerPixels.data;
+ }
+
+ if( hasMergedPixels )
+ {
+ BufferedImage *mergedImage = new BufferedImage(mergedWidth, mergedHeight, BufferedImage::TYPE_INT_ARGB);
+ memcpy(mergedImage->getData(), mergedPixels.data, mergedWidth * mergedHeight * sizeof(int));
+ delete[] mergedPixels.data;
+ id = getTexture(mergedImage, C4JRender::TEXTURE_FORMAT_RxGyBzAw, false);
+ }
+ else
+ {
+ id = 0;
+ }
+
+ idMap[cacheKey] = id;
}
+
+ RenderManager.TextureBind(id);
}
void Textures::bind(int id)