2026-03-01 12:16:08 +08:00
# include "stdafx.h"
# include "MobRenderer.h"
# include "MultiPlayerLocalPlayer.h"
# include "..\Minecraft.World\net.minecraft.world.entity.h"
# include "..\Minecraft.World\net.minecraft.world.entity.player.h"
# include "..\Minecraft.World\net.minecraft.world.entity.projectile.h"
# include "..\Minecraft.World\StringHelpers.h"
# include "..\Minecraft.World\Mth.h"
# include "entityRenderDispatcher.h"
MobRenderer : : MobRenderer ( Model * model , float shadow ) : EntityRenderer ( )
{
this - > model = model ;
this - > shadowRadius = shadow ;
this - > armor = NULL ;
}
void MobRenderer : : setArmor ( Model * armor )
{
this - > armor = armor ;
}
float MobRenderer : : rotlerp ( float from , float to , float a )
{
float diff = to - from ;
while ( diff < - 180 )
diff + = 360 ;
while ( diff > = 180 )
diff - = 360 ;
return from + a * diff ;
}
2026-03-02 15:58:20 +07:00
void MobRenderer : : render ( std : : shared_ptr < Entity > _mob , double x , double y , double z , float rot , float a )
2026-03-01 12:16:08 +08:00
{
// 4J - added - this used to use generics so the input parameter could be a mob (or derived type), but we aren't
// able to do that so dynamically casting to get the more specific type here.
2026-03-02 17:10:34 +07:00
std : : shared_ptr < Mob > mob = std : : dynamic_pointer_cast < Mob > ( _mob ) ;
2026-03-01 12:16:08 +08:00
glPushMatrix ( ) ;
glDisable ( GL_CULL_FACE ) ;
model - > attackTime = getAttackAnim ( mob , a ) ;
if ( armor ! = NULL ) armor - > attackTime = model - > attackTime ;
model - > riding = mob - > isRiding ( ) ;
if ( armor ! = NULL ) armor - > riding = model - > riding ;
model - > young = mob - > isBaby ( ) ;
if ( armor ! = NULL ) armor - > young = model - > young ;
// 4J - removed try/catch
// try
// {
float bodyRot = rotlerp ( mob - > yBodyRotO , mob - > yBodyRot , a ) ;
float headRot = rotlerp ( mob - > yHeadRotO , mob - > yHeadRot , a ) ;
2026-03-02 17:10:34 +07:00
if ( mob - > isRiding ( ) & & std : : dynamic_pointer_cast < Mob > ( mob - > riding ) )
2026-03-01 12:16:08 +08:00
{
2026-03-02 17:10:34 +07:00
std : : shared_ptr < Mob > riding = std : : dynamic_pointer_cast < Mob > ( mob - > riding ) ;
2026-03-01 12:16:08 +08:00
bodyRot = rotlerp ( riding - > yBodyRotO , riding - > yBodyRot , a ) ;
float headDiff = Mth : : wrapDegrees ( headRot - bodyRot ) ;
if ( headDiff < - 85 ) headDiff = - 85 ;
if ( headDiff > = 85 ) headDiff = + 85 ;
bodyRot = headRot - headDiff ;
if ( headDiff * headDiff > 50 * 50 )
{
bodyRot + = headDiff * 0.2f ;
}
}
float headRotx = ( mob - > xRotO + ( mob - > xRot - mob - > xRotO ) * a ) ;
setupPosition ( mob , x , y , z ) ;
float bob = getBob ( mob , a ) ;
setupRotations ( mob , bob , bodyRot , a ) ;
float _scale = 1 / 16.0f ;
glEnable ( GL_RESCALE_NORMAL ) ;
glScalef ( - 1 , - 1 , 1 ) ;
scale ( mob , a ) ;
glTranslatef ( 0 , - 24 * _scale - 0.125f / 16.0f , 0 ) ;
float ws = mob - > walkAnimSpeedO + ( mob - > walkAnimSpeed - mob - > walkAnimSpeedO ) * a ;
float wp = mob - > walkAnimPos - mob - > walkAnimSpeed * ( 1 - a ) ;
if ( mob - > isBaby ( ) )
{
wp * = 3.0f ;
}
if ( ws > 1 ) ws = 1 ;
MemSect ( 31 ) ;
bindTexture ( mob - > customTextureUrl , mob - > getTexture ( ) ) ;
MemSect ( 0 ) ;
glEnable ( GL_ALPHA_TEST ) ;
model - > prepareMobModel ( mob , wp , ws , a ) ;
renderModel ( mob , wp , ws , bob , headRot - bodyRot , headRotx , _scale ) ;
for ( int i = 0 ; i < MAX_ARMOR_LAYERS ; i + + )
{
int armorType = prepareArmor ( mob , i , a ) ;
if ( armorType > 0 )
{
armor - > prepareMobModel ( mob , wp , ws , a ) ;
armor - > render ( mob , wp , ws , bob , headRot - bodyRot , headRotx , _scale , true ) ;
if ( ( armorType & 0xf0 ) = = 16 )
{
prepareSecondPassArmor ( mob , i , a ) ;
armor - > render ( mob , wp , ws , bob , headRot - bodyRot , headRotx , _scale , true ) ;
}
// 4J - added condition here for rendering player as part of the gui. Avoiding rendering the glint here as it involves using its own blending, and for gui rendering
// we are globally blending to be able to offer user configurable gui opacity. Note that I really don't know why GL_BLEND is turned off at the end of the first
// armour layer anyway, or why alpha testing is turned on... but we definitely don't want to be turning blending off during the gui render.
if ( ! entityRenderDispatcher - > isGuiRender )
{
if ( ( armorType & 0xf ) = = 0xf ) //MGH - fix for missing enchantment glow
{
float time = mob - > tickCount + a ;
bindTexture ( TN__BLUR__MISC_GLINT ) ; // 4J was "%blur%/misc/glint.png"
glEnable ( GL_BLEND ) ;
float br = 0.5f ;
glColor4f ( br , br , br , 1 ) ;
glDepthFunc ( GL_EQUAL ) ;
glDepthMask ( false ) ;
for ( int j = 0 ; j < 2 ; j + + )
{
glDisable ( GL_LIGHTING ) ;
float brr = 0.76f ;
glColor4f ( 0.5f * brr , 0.25f * brr , 0.8f * brr , 1 ) ;
glBlendFunc ( GL_SRC_COLOR , GL_ONE ) ;
glMatrixMode ( GL_TEXTURE ) ;
glLoadIdentity ( ) ;
float uo = time * ( 0.001f + j * 0.003f ) * 20 ;
float ss = 1 / 3.0f ;
glScalef ( ss , ss , ss ) ;
glRotatef ( 30 - ( j ) * 60.0f , 0 , 0 , 1 ) ;
glTranslatef ( 0 , uo , 0 ) ;
glMatrixMode ( GL_MODELVIEW ) ;
armor - > render ( mob , wp , ws , bob , headRot - bodyRot , headRotx , _scale , false ) ;
}
glColor4f ( 1 , 1 , 1 , 1 ) ;
glMatrixMode ( GL_TEXTURE ) ;
glDepthMask ( true ) ;
glLoadIdentity ( ) ;
glMatrixMode ( GL_MODELVIEW ) ;
glEnable ( GL_LIGHTING ) ;
glDisable ( GL_BLEND ) ;
glDepthFunc ( GL_LEQUAL ) ;
}
glDisable ( GL_BLEND ) ;
}
glEnable ( GL_ALPHA_TEST ) ;
}
}
glDepthMask ( true ) ;
additionalRendering ( mob , a ) ;
float br = mob - > getBrightness ( a ) ;
int overlayColor = getOverlayColor ( mob , br , a ) ;
glActiveTexture ( GL_TEXTURE1 ) ;
glDisable ( GL_TEXTURE_2D ) ;
glActiveTexture ( GL_TEXTURE0 ) ;
if ( ( ( overlayColor > > 24 ) & 0xff ) > 0 | | mob - > hurtTime > 0 | | mob - > deathTime > 0 )
{
glDisable ( GL_TEXTURE_2D ) ;
glDisable ( GL_ALPHA_TEST ) ;
glEnable ( GL_BLEND ) ;
glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
glDepthFunc ( GL_EQUAL ) ;
// 4J - changed these renders to not use the compiled version of their models, because otherwise the render states set
// about (in particular the depth & alpha test) don't work with our command buffer versions
if ( mob - > hurtTime > 0 | | mob - > deathTime > 0 )
{
glColor4f ( br , 0 , 0 , 0.4f ) ;
model - > render ( mob , wp , ws , bob , headRot - bodyRot , headRotx , _scale , false ) ;
for ( int i = 0 ; i < MAX_ARMOR_LAYERS ; i + + )
{
if ( prepareArmorOverlay ( mob , i , a ) > = 0 )
{
glColor4f ( br , 0 , 0 , 0.4f ) ;
armor - > render ( mob , wp , ws , bob , headRot - bodyRot , headRotx , _scale , false ) ;
}
}
}
if ( ( ( overlayColor > > 24 ) & 0xff ) > 0 )
{
float r = ( ( overlayColor > > 16 ) & 0xff ) / 255.0f ;
float g = ( ( overlayColor > > 8 ) & 0xff ) / 255.0f ;
float b = ( ( overlayColor ) & 0xff ) / 255.0f ;
float aa = ( ( overlayColor > > 24 ) & 0xff ) / 255.0f ;
glColor4f ( r , g , b , aa ) ;
model - > render ( mob , wp , ws , bob , headRot - bodyRot , headRotx , _scale , false ) ;
for ( int i = 0 ; i < MAX_ARMOR_LAYERS ; i + + )
{
if ( prepareArmorOverlay ( mob , i , a ) > = 0 )
{
glColor4f ( r , g , b , aa ) ;
armor - > render ( mob , wp , ws , bob , headRot - bodyRot , headRotx , _scale , false ) ;
}
}
}
glDepthFunc ( GL_LEQUAL ) ;
glDisable ( GL_BLEND ) ;
glEnable ( GL_ALPHA_TEST ) ;
glEnable ( GL_TEXTURE_2D ) ;
}
glDisable ( GL_RESCALE_NORMAL ) ;
// }
//catch (Exception e) {
// // System.out.println("Failed: " + modelNames[model]);
// e.printStackTrace();
// }
glActiveTexture ( GL_TEXTURE1 ) ;
glEnable ( GL_TEXTURE_2D ) ;
glActiveTexture ( GL_TEXTURE0 ) ;
glEnable ( GL_CULL_FACE ) ;
glPopMatrix ( ) ;
MemSect ( 31 ) ;
renderName ( mob , x , y , z ) ;
MemSect ( 0 ) ;
}
2026-03-02 15:58:20 +07:00
void MobRenderer : : renderModel ( std : : shared_ptr < Entity > mob , float wp , float ws , float bob , float headRotMinusBodyRot , float headRotx , float scale )
2026-03-01 12:16:08 +08:00
{
2026-03-02 17:10:34 +07:00
std : : shared_ptr < Player > player = std : : dynamic_pointer_cast < Player > ( Minecraft : : GetInstance ( ) - > player ) ;
2026-03-01 12:16:08 +08:00
bindTexture ( mob - > customTextureUrl , mob - > getTexture ( ) ) ;
if ( ! mob - > isInvisible ( ) )
{
model - > render ( mob , wp , ws , bob , headRotMinusBodyRot , headRotx , scale , true ) ;
}
else if ( ! mob - > isInvisibleTo ( player ) )
{
glPushMatrix ( ) ;
glColor4f ( 1 , 1 , 1 , 0.15f ) ;
glDepthMask ( false ) ;
glEnable ( GL_BLEND ) ;
glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
glAlphaFunc ( GL_GREATER , 1.0f / 255.0f ) ;
model - > render ( mob , wp , ws , bob , headRotMinusBodyRot , headRotx , scale , true ) ;
glDisable ( GL_BLEND ) ;
glAlphaFunc ( GL_GREATER , .1f ) ;
glPopMatrix ( ) ;
glDepthMask ( true ) ;
}
else
{
model - > setupAnim ( wp , ws , bob , headRotMinusBodyRot , headRotx , scale ) ; //, mob);
}
}
2026-03-02 15:58:20 +07:00
void MobRenderer : : setupPosition ( std : : shared_ptr < Mob > mob , double x , double y , double z )
2026-03-01 12:16:08 +08:00
{
glTranslatef ( ( float ) x , ( float ) y , ( float ) z ) ;
}
2026-03-02 15:58:20 +07:00
void MobRenderer : : setupRotations ( std : : shared_ptr < Mob > mob , float bob , float bodyRot , float a )
2026-03-01 12:16:08 +08:00
{
glRotatef ( 180 - bodyRot , 0 , 1 , 0 ) ;
2026-03-02 15:58:20 +07:00
if ( mob - > deathTime > 0 )
2026-03-01 12:16:08 +08:00
{
float fall = ( mob - > deathTime + a - 1 ) / 20.0f * 1.6f ;
fall = ( float ) sqrt ( fall ) ;
if ( fall > 1 ) fall = 1 ;
glRotatef ( fall * getFlipDegrees ( mob ) , 0 , 0 , 1 ) ;
}
}
2026-03-02 15:58:20 +07:00
float MobRenderer : : getAttackAnim ( std : : shared_ptr < Mob > mob , float a )
2026-03-01 12:16:08 +08:00
{
return mob - > getAttackAnim ( a ) ;
}
2026-03-02 15:58:20 +07:00
float MobRenderer : : getBob ( std : : shared_ptr < Mob > mob , float a )
2026-03-01 12:16:08 +08:00
{
return ( mob - > tickCount + a ) ;
}
2026-03-02 15:58:20 +07:00
void MobRenderer : : additionalRendering ( std : : shared_ptr < Mob > mob , float a )
2026-03-01 12:16:08 +08:00
{
}
2026-03-02 15:58:20 +07:00
int MobRenderer : : prepareArmorOverlay ( std : : shared_ptr < Mob > mob , int layer , float a )
2026-03-01 12:16:08 +08:00
{
return prepareArmor ( mob , layer , a ) ;
}
2026-03-02 15:58:20 +07:00
int MobRenderer : : prepareArmor ( std : : shared_ptr < Mob > mob , int layer , float a )
2026-03-01 12:16:08 +08:00
{
return - 1 ;
}
2026-03-02 15:58:20 +07:00
void MobRenderer : : prepareSecondPassArmor ( std : : shared_ptr < Mob > mob , int layer , float a )
2026-03-01 12:16:08 +08:00
{
}
2026-03-02 15:58:20 +07:00
float MobRenderer : : getFlipDegrees ( std : : shared_ptr < Mob > mob )
2026-03-01 12:16:08 +08:00
{
return 90 ;
}
2026-03-02 15:58:20 +07:00
int MobRenderer : : getOverlayColor ( std : : shared_ptr < Mob > mob , float br , float a )
2026-03-01 12:16:08 +08:00
{
return 0 ;
}
2026-03-02 15:58:20 +07:00
void MobRenderer : : scale ( std : : shared_ptr < Mob > mob , float a )
2026-03-01 12:16:08 +08:00
{
}
2026-03-02 15:58:20 +07:00
void MobRenderer : : renderName ( std : : shared_ptr < Mob > mob , double x , double y , double z )
2026-03-01 12:16:08 +08:00
{
if ( Minecraft : : renderDebug ( ) )
{
//renderNameTag(mob, _toString<int>(mob->entityId), x, y, z, 64);
}
}
// 4J Added parameter for color here so that we can colour players names
2026-03-02 15:58:20 +07:00
void MobRenderer : : renderNameTag ( std : : shared_ptr < Mob > mob , const wstring & OriginalName , double x , double y , double z , int maxDist , int color /*= 0xffffffff*/ )
2026-03-01 12:16:08 +08:00
{
if ( app . GetGameSettings ( eGameSetting_DisplayHUD ) = = 0 )
{
// 4J-PB - turn off gamertag render
return ;
}
if ( app . GetGameHostOption ( eGameHostOption_Gamertags ) = = 0 )
{
// turn off gamertags if the host has set them off
return ;
}
float dist = mob - > distanceTo ( entityRenderDispatcher - > cameraEntity ) ;
if ( dist > maxDist )
{
return ;
}
Font * font = getFont ( ) ;
float size = 1.60f ;
float s = 1 / 60.0f * size ;
glPushMatrix ( ) ;
glTranslatef ( ( float ) x + 0 , ( float ) y + 2.3f , ( float ) z ) ;
glNormal3f ( 0 , 1 , 0 ) ;
glRotatef ( - this - > entityRenderDispatcher - > playerRotY , 0 , 1 , 0 ) ;
glRotatef ( this - > entityRenderDispatcher - > playerRotX , 1 , 0 , 0 ) ;
glScalef ( - s , - s , s ) ;
glDisable ( GL_LIGHTING ) ;
// 4J Stu - If it's beyond readable distance, then just render a coloured box
int readableDist = PLAYER_NAME_READABLE_FULLSCREEN ;
if ( ! RenderManager . IsHiDef ( ) )
{
readableDist = PLAYER_NAME_READABLE_DISTANCE_SD ;
}
else if ( app . GetLocalPlayerCount ( ) > 2 )
{
readableDist = PLAYER_NAME_READABLE_DISTANCE_SPLITSCREEN ;
}
float textOpacity = 1.0f ;
if ( dist > = readableDist )
{
int diff = dist - readableDist ;
textOpacity / = ( diff / 2 ) ;
if ( diff > readableDist ) textOpacity = 0.0f ;
}
if ( textOpacity < 0.0f ) textOpacity = 0.0f ;
if ( textOpacity > 1.0f ) textOpacity = 1.0f ;
2026-03-02 15:58:20 +07:00
2026-03-01 12:16:08 +08:00
glEnable ( GL_BLEND ) ;
glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
Tesselator * t = Tesselator : : getInstance ( ) ;
int offs = 0 ;
2026-03-02 17:10:34 +07:00
std : : shared_ptr < Player > player = std : : dynamic_pointer_cast < Player > ( mob ) ;
2026-03-01 12:16:08 +08:00
if ( player ! = NULL & & app . isXuidDeadmau5 ( player - > getXuid ( ) ) ) offs = - 10 ;
wstring playerName ;
WCHAR wchName [ 2 ] ;
# if defined(__PS3__) || defined(__ORBIS__)
// Check we have all the font characters for this player name
switch ( player - > GetPlayerNameValidState ( ) )
{
case Player : : ePlayerNameValid_NotSet :
if ( font - > AllCharactersValid ( OriginalName ) )
{
playerName = OriginalName ;
player - > SetPlayerNameValidState ( true ) ;
}
else
{
memset ( wchName , 0 , sizeof ( WCHAR ) * 2 ) ;
2026-03-02 15:58:20 +07:00
swprintf ( wchName , 2 , L " %d " , player - > getPlayerIndex ( ) + 1 ) ;
2026-03-01 12:16:08 +08:00
playerName = wchName ;
player - > SetPlayerNameValidState ( false ) ;
}
break ;
case Player : : ePlayerNameValid_True :
playerName = OriginalName ;
break ;
case Player : : ePlayerNameValid_False :
memset ( wchName , 0 , sizeof ( WCHAR ) * 2 ) ;
2026-03-02 15:58:20 +07:00
swprintf ( wchName , 2 , L " %d " , player - > getPlayerIndex ( ) + 1 ) ;
playerName = wchName ;
2026-03-01 12:16:08 +08:00
break ;
}
# else
playerName = OriginalName ;
# endif
if ( textOpacity > 0.0f )
{
glColor4f ( 1.0f , 1.0f , 1.0f , textOpacity ) ;
glDepthMask ( false ) ;
glDisable ( GL_DEPTH_TEST ) ;
glDisable ( GL_TEXTURE_2D ) ;
t - > begin ( ) ;
int w = font - > width ( playerName ) / 2 ;
if ( textOpacity < 1.0f )
{
t - > color ( color , 255 * textOpacity ) ;
}
else
{
t - > color ( 0.0f , 0.0f , 0.0f , 0.25f ) ;
}
t - > vertex ( ( float ) ( - w - 1 ) , ( float ) ( - 1 + offs ) , ( float ) ( 0 ) ) ;
t - > vertex ( ( float ) ( - w - 1 ) , ( float ) ( + 8 + offs + 1 ) , ( float ) ( 0 ) ) ;
t - > vertex ( ( float ) ( + w + 1 ) , ( float ) ( + 8 + offs + 1 ) , ( float ) ( 0 ) ) ;
t - > vertex ( ( float ) ( + w + 1 ) , ( float ) ( - 1 + offs ) , ( float ) ( 0 ) ) ;
t - > end ( ) ;
2026-03-02 15:58:20 +07:00
2026-03-01 12:16:08 +08:00
glEnable ( GL_DEPTH_TEST ) ;
glDepthMask ( true ) ;
glDepthFunc ( GL_ALWAYS ) ;
glLineWidth ( 2.0f ) ;
t - > begin ( GL_LINE_STRIP ) ;
t - > color ( color , 255 * textOpacity ) ;
t - > vertex ( ( float ) ( - w - 1 ) , ( float ) ( - 1 + offs ) , ( float ) ( 0 ) ) ;
t - > vertex ( ( float ) ( - w - 1 ) , ( float ) ( + 8 + offs + 1 ) , ( float ) ( 0 ) ) ;
t - > vertex ( ( float ) ( + w + 1 ) , ( float ) ( + 8 + offs + 1 ) , ( float ) ( 0 ) ) ;
t - > vertex ( ( float ) ( + w + 1 ) , ( float ) ( - 1 + offs ) , ( float ) ( 0 ) ) ;
t - > vertex ( ( float ) ( - w - 1 ) , ( float ) ( - 1 + offs ) , ( float ) ( 0 ) ) ;
t - > end ( ) ;
glDepthFunc ( GL_LEQUAL ) ;
glDepthMask ( false ) ;
glDisable ( GL_DEPTH_TEST ) ;
glEnable ( GL_TEXTURE_2D ) ;
font - > draw ( playerName , - font - > width ( playerName ) / 2 , offs , 0x20ffffff ) ;
glEnable ( GL_DEPTH_TEST ) ;
2026-03-02 15:58:20 +07:00
2026-03-01 12:16:08 +08:00
glDepthMask ( true ) ;
}
if ( textOpacity < 1.0f )
{
glColor4f ( 1.0f , 1.0f , 1.0f , 1.0f ) ;
glDisable ( GL_TEXTURE_2D ) ;
glDepthFunc ( GL_ALWAYS ) ;
t - > begin ( ) ;
int w = font - > width ( playerName ) / 2 ;
t - > color ( color , 255 ) ;
t - > vertex ( ( float ) ( - w - 1 ) , ( float ) ( - 1 + offs ) , ( float ) ( 0 ) ) ;
t - > vertex ( ( float ) ( - w - 1 ) , ( float ) ( + 8 + offs ) , ( float ) ( 0 ) ) ;
t - > vertex ( ( float ) ( + w + 1 ) , ( float ) ( + 8 + offs ) , ( float ) ( 0 ) ) ;
t - > vertex ( ( float ) ( + w + 1 ) , ( float ) ( - 1 + offs ) , ( float ) ( 0 ) ) ;
2026-03-02 15:58:20 +07:00
t - > end ( ) ;
2026-03-01 12:16:08 +08:00
glDepthFunc ( GL_LEQUAL ) ;
glEnable ( GL_TEXTURE_2D ) ;
2026-03-02 15:58:20 +07:00
2026-03-01 12:16:08 +08:00
glTranslatef ( 0.0f , 0.0f , - 0.04f ) ;
}
if ( textOpacity > 0.0f )
{
int textColor = ( ( ( int ) ( textOpacity * 255 ) < < 24 ) | 0xffffff ) ;
font - > draw ( playerName , - font - > width ( playerName ) / 2 , offs , textColor ) ;
}
2026-03-02 15:58:20 +07:00
2026-03-01 12:16:08 +08:00
glEnable ( GL_LIGHTING ) ;
glDisable ( GL_BLEND ) ;
glColor4f ( 1 , 1 , 1 , 1 ) ;
glPopMatrix ( ) ;
}