#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; } void MobRenderer::render(shared_ptr _mob, double x, double y, double z, float rot, float a) { // 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. shared_ptr mob = dynamic_pointer_cast(_mob); 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); if (mob->isRiding() && dynamic_pointer_cast(mob->riding)) { shared_ptr riding = dynamic_pointer_cast(mob->riding); 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); } void MobRenderer::renderModel(shared_ptr mob, float wp, float ws, float bob, float headRotMinusBodyRot, float headRotx, float scale) { shared_ptr player = dynamic_pointer_cast(Minecraft::GetInstance()->player); 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); } } void MobRenderer::setupPosition(shared_ptr mob, double x, double y, double z) { glTranslatef((float) x, (float) y, (float) z); } void MobRenderer::setupRotations(shared_ptr mob, float bob, float bodyRot, float a) { glRotatef(180 - bodyRot, 0, 1, 0); if (mob->deathTime > 0) { 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); } } float MobRenderer::getAttackAnim(shared_ptr mob, float a) { return mob->getAttackAnim(a); } float MobRenderer::getBob(shared_ptr mob, float a) { return (mob->tickCount + a); } void MobRenderer::additionalRendering(shared_ptr mob, float a) { } int MobRenderer::prepareArmorOverlay(shared_ptr mob, int layer, float a) { return prepareArmor(mob, layer, a); } int MobRenderer::prepareArmor(shared_ptr mob, int layer, float a) { return -1; } void MobRenderer::prepareSecondPassArmor(shared_ptr mob, int layer, float a) { } float MobRenderer::getFlipDegrees(shared_ptr mob) { return 90; } int MobRenderer::getOverlayColor(shared_ptr mob, float br, float a) { return 0; } void MobRenderer::scale(shared_ptr mob, float a) { } void MobRenderer::renderName(shared_ptr mob, double x, double y, double z) { if (Minecraft::renderDebug()) { //renderNameTag(mob, _toString(mob->entityId), x, y, z, 64); } } // 4J Added parameter for color here so that we can colour players names void MobRenderer::renderNameTag(shared_ptr mob, const wstring& OriginalName, double x, double y, double z, int maxDist, int color /*= 0xffffffff*/) { 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; glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); Tesselator *t = Tesselator::getInstance(); int offs = 0; shared_ptr player = dynamic_pointer_cast(mob); if (player != NULL && app.isXuidDeadmau5( player->getXuid() ) ) offs = -10; wstring playerName; WCHAR wchName[2]; #if defined(__PS3__) // 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); swprintf(wchName, 2, L"%d",player->getPlayerIndex()+1); playerName=wchName; player->SetPlayerNameValidState(false); } break; case Player::ePlayerNameValid_True: playerName=OriginalName; break; case Player::ePlayerNameValid_False: memset(wchName,0,sizeof(WCHAR)*2); swprintf(wchName, 2, L"%d",player->getPlayerIndex()+1); playerName=wchName; 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(); 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); 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)); t->end(); glDepthFunc(GL_LEQUAL); glEnable(GL_TEXTURE_2D); 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); } glEnable(GL_LIGHTING); glDisable(GL_BLEND); glColor4f(1, 1, 1, 1); glPopMatrix(); }