/******************************************************************** Minecraft: Pocket Edition - Decompilation Project Copyright (C) 2023 iProgramInCpp The following code is licensed under the BSD 1 clause license. SPDX-License-Identifier: BSD-1-Clause ********************************************************************/ #include "thirdparty/GL/GL.hpp" #include "GameRenderer.hpp" #include "client/app/Minecraft.hpp" #include "client/player/input/Multitouch.hpp" #include "Frustum.hpp" #include "renderer/GL/GL.hpp" static int t_keepHitResult; // that is its address in v0.1.1j int t_keepPic; void GameRenderer::_init() { //ItemInHandRenderer* m_pItemInHandRenderer = nullptr; field_8 = 0.0f; field_C = 0; field_10 = nullptr; field_14 = 0.0f; field_18 = 0.0f; field_1C = 0.0f; field_20 = 0.0f; field_24 = 0.0f; field_28 = 0.0f; field_2C = 4.0f; field_30 = 4.0f; field_34 = 0.0f; field_38 = 0.0f; field_3C = 0.0f; field_40 = 0.0f; field_44 = 1.0f; field_48 = 0.0f; field_4C = 0.0f; field_50 = 0.0f; field_54 = 0.0f; field_58 = 0.0f; field_5C = 0.0f; field_60 = 0.0f; field_64 = 0.0f; field_68 = 0.0f; field_6C = 0.0f; field_70 = 0.0f; field_74 = 0.0f; field_78 = 0.0f; field_7C = 0.0f; field_80 = 0.0f; field_84 = 0.0f; m_lastUpdatedMS = 0; m_shownFPS = 0; m_shownChunkUpdates = 0; } GameRenderer::GameRenderer(Minecraft* pMinecraft) : m_pMinecraft(pMinecraft) { _init(); saveMatrices(); m_pItemInHandRenderer = new ItemInHandRenderer(pMinecraft); EntityRenderDispatcher::getInstance()->m_pItemInHandRenderer = m_pItemInHandRenderer; } GameRenderer::~GameRenderer() { delete m_pItemInHandRenderer; } void GameRenderer::zoomRegion(float a, float b, float c) { field_44 = a; field_48 = b; field_4C = c; } void GameRenderer::unZoomRegion() { field_44 = 1.0f; } void GameRenderer::setupCamera(float f, int i) { field_8 = float(256 >> m_pMinecraft->getOptions()->m_iViewDistance); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (m_pMinecraft->getOptions()->m_bAnaglyphs) { glTranslatef(float(1 - 2 * i) * 0.07f, 0.0f, 0.0f); } if (field_44 != 1.0) { glTranslatef(field_48, -field_4C, 0.0); glScalef(field_44, field_44, 1.0); } float fov = getFov(f); gluPerspective(fov, float(Minecraft::width) / float(Minecraft::height), 0.05f, field_8); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); if (m_pMinecraft->getOptions()->m_bAnaglyphs) { glTranslatef(float(2 * i - 1) * 0.1f, 0.0f, 0.0f); } bobHurt(f); if (m_pMinecraft->getOptions()->m_bViewBobbing) bobView(f); moveCameraToPlayer(f); } void GameRenderer::moveCameraToPlayer(float f) { Mob* pMob = m_pMinecraft->m_pMobPersp; float headHeightDiff = pMob->field_84 - 1.62f; float posX = Lerp(pMob->field_3C.x, pMob->m_pos.x, f); float posY = Lerp(pMob->field_3C.y, pMob->m_pos.y, f); float posZ = Lerp(pMob->field_3C.z, pMob->m_pos.z, f); glRotatef(field_5C + f * (field_58 - field_5C), 0.0f, 0.0f, 1.0f); if (m_pMinecraft->getOptions()->m_bThirdPerson) { float v11 = field_30 + (field_2C - field_30) * f; if (m_pMinecraft->getOptions()->field_241) { glTranslatef(0.0f, 0.0f, -v11); glRotatef(field_38 + (field_34 - field_38) * f, 1.0f, 0.0f, 0.0f); glRotatef(field_40 + (field_3C - field_40) * f, 0.0f, 1.0f, 0.0f); } else { float mob_yaw = pMob->m_yaw; float mob_pitch = pMob->m_pitch; float pitchRad = mob_pitch / 180.0f * float(M_PI); float aX = posX - (-(Mth::sin(mob_yaw / 180.0f * float(M_PI)) * Mth::cos(pitchRad)) * v11); float aY = posY + (Mth::sin(pitchRad) * v11); float aZ = posZ - ((Mth::cos(mob_yaw / 180.0f * float(M_PI)) * Mth::cos(pitchRad)) * v11); for (int i = 0; i < 8; i++) { float offsX = (2 * (i & 1) - 1) * 0.1f; float offsY = ((i & 2) - 1) * 0.1f; float offsZ = (2 * ((i >> 2) & 1) - 1) * 0.1f; HitResult hr = m_pMinecraft->m_pLevel->clip( Vec3(posX + offsX, posY + offsY, posZ + offsZ), Vec3(aX + offsX + offsZ, aY + offsY, aZ + offsZ) // @NOTE: Not sure why it adds offsZ to offsX. ); if (hr.m_hitType != HitResult::NONE) { float dX = posX - hr.m_hitPos.x; float dY = posY - hr.m_hitPos.y; float dZ = posZ - hr.m_hitPos.z; float dist = sqrtf(dX * dX + dY * dY + dZ * dZ); if (v11 > dist) v11 = dist; } } // @HUH: Why the hell is it rotating by 0 glRotatef(pMob->m_pitch - mob_pitch, 1.0f, 0.0f, 0.0f); glRotatef(pMob->m_yaw - mob_yaw, 0.0f, 1.0f, 0.0f); glTranslatef(0.0, 0.0, -v11); glRotatef(mob_yaw - pMob->m_yaw, 0.0f, 1.0f, 0.0f); glRotatef(mob_pitch - pMob->m_pitch, 1.0f, 0.0f, 0.0f); } } else { glTranslatef(0.0f, 0.0f, -0.1f); } if (!m_pMinecraft->getOptions()->field_241) { glRotatef(pMob->field_60 + f * (pMob->m_pitch - pMob->field_60), 1.0f, 0.0f, 0.0f); glRotatef(pMob->field_5C + f * (pMob->m_yaw - pMob->field_5C) + 180.0f, 0.0f, 1.0f, 0.0f); } glTranslatef(0.0f, headHeightDiff, 0.0f); } void GameRenderer::saveMatrices() { glGetFloatv(GL_PROJECTION_MATRIX, m_matrix_projection); glGetFloatv(GL_MODELVIEW_MATRIX, m_matrix_model_view); } void GameRenderer::setupGuiScreen() { float x = Gui::InvGuiScale * Minecraft::width; float y = Gui::InvGuiScale * Minecraft::height; glClear(GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); xglOrthof(0, x, y, 0, 2000.0f, 3000.0f); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.0f, 0.0f, -2000.0f); } void GameRenderer::bobHurt(float f) { Mob* pMob = m_pMinecraft->m_pMobPersp; if (pMob->m_health <= 0) glRotatef(-8000.0f / (float(pMob->field_110) + f + 200.0f) + 40.0f, 0.0f, 0.0f, 1.0f); if (pMob->field_104 > 0) { float val = (pMob->field_104 - f) / pMob->field_108; glRotatef(-pMob->field_10C, 0.0f, 1.0f, 0.0f); glRotatef(Mth::sin(val * val * val * val * 3.1416f) * -14.0f, 0.0f, 0.0f, 1.0f); glRotatef(pMob->field_10C, 0.0f, 1.0f, 0.0f); } } void GameRenderer::bobView(float f) { if (!m_pMinecraft->m_pMobPersp->isPlayer()) return; Player* player = (Player*)m_pMinecraft->m_pMobPersp; float f1 = Lerp(player->field_B9C, player->field_BA0, f); float f2 = Lerp(player->field_118, player->field_11C, f); // @NOTE: Multiplying by M_PI inside of the paren makes it stuttery for some reason? Anyways it works now :) float f3 = -(player->field_94 + (player->field_94 - player->field_90) * f) * float(M_PI); float f4 = Mth::sin(f3); float f5 = Mth::cos(f3); glTranslatef((f4 * f1) * 0.5f, -fabsf(f5 * f1), 0.0f); float f6 = Mth::sin(f3); glRotatef((f6 * f1) * 3.0f, 0.0f, 0.0f, 1.0f); float f7 = Mth::cos(f3 - 0.2f); glRotatef(fabsf(f7 * f1) * 5.0f, 1.0f, 0.0f, 0.0f); glRotatef(f2, 1.0f, 0.0f, 0.0f); } void GameRenderer::setupClearColor(float f) { Minecraft* pMC = m_pMinecraft; Level* pLevel = pMC->m_pLevel; Mob* pMob = pMC->m_pMobPersp; float x1 = 1.0f - powf(1.0f / float(4 - pMC->getOptions()->m_iViewDistance), 0.25f); Vec3 skyColor = pLevel->getSkyColor(pMob, f), fogColor = pLevel->getFogColor(f); //@BUG: double set to these? field_60 = fogColor.x; field_64 = fogColor.y; field_60 = fogColor.x + (skyColor.x - fogColor.x) * x1; field_64 = fogColor.y + (skyColor.y - fogColor.y) * x1; field_68 = fogColor.z + (skyColor.z - fogColor.z) * x1; if (pMob->isUnderLiquid(Material::water)) { field_60 = 0.02f; field_64 = 0.02f; field_68 = 0.2f; } else if (pMob->isUnderLiquid(Material::lava)) { field_60 = 0.6f; field_64 = 0.1f; field_68 = 0.0f; } float x2 = field_6C + (field_70 - field_6C) * f; field_60 *= x2; field_64 *= x2; field_68 *= x2; if (pMC->getOptions()->m_bAnaglyphs) { float r = (field_60 * 30.0f + field_64 * 59.0f + field_68 * 11.0f) / 100.0f; float g = (field_60 * 30.0f + field_64 * 70.0f) / 100.0f; float b = (field_60 * 30.0f + field_68 * 70.0f) / 100.0f; field_60 = r; field_64 = g; field_68 = b; } glClearColor(field_60, field_64, field_68, 1.0f); } #ifndef ORIGINAL_CODE void GameRenderer::renderNoCamera() { glClearColor(1.0f, 1.0f, 1.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } #endif void GameRenderer::setupFog(int i) { float fog_color[4]; fog_color[0] = field_60; fog_color[1] = field_64; fog_color[2] = field_68; fog_color[3] = 1.0f; glFogfv(GL_FOG_COLOR, fog_color); glNormal3f(0.0f, -1.0f, 0.0f); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); if (m_pMinecraft->m_pMobPersp->isUnderLiquid(Material::water)) { #if defined(ORIGINAL_CODE) || defined(__ANDROID__) glFogx(GL_FOG_MODE, GL_EXP); #else glFogi(GL_FOG_MODE, GL_EXP); #endif glFogf(GL_FOG_DENSITY, 0.1f); } else if (m_pMinecraft->m_pMobPersp->isUnderLiquid(Material::lava)) { #if defined(ORIGINAL_CODE) || defined(__ANDROID__) glFogx(GL_FOG_MODE, GL_EXP); #else glFogi(GL_FOG_MODE, GL_EXP); #endif glFogf(GL_FOG_DENSITY, 2.0f); } else { #if defined(ORIGINAL_CODE) || defined(__ANDROID__) glFogx(GL_FOG_MODE, GL_LINEAR); #else glFogi(GL_FOG_MODE, GL_LINEAR); #endif glFogf(GL_FOG_START, field_8 * 0.25f); glFogf(GL_FOG_END, field_8); if (i < 0) { glFogf(GL_FOG_START, 0.0f); glFogf(GL_FOG_END, field_8 * 0.8f); } if (m_pMinecraft->m_pLevel->m_pDimension->field_C) { glFogf(GL_FOG_START, 0.0f); } } glEnable(GL_COLOR_MATERIAL); } float GameRenderer::getFov(float f) { Mob* pMob = m_pMinecraft->m_pMobPersp; float x1 = 70.0f; if (pMob->isUnderLiquid(Material::water)) x1 = 60.0f; if (pMob->m_health <= 0) { float x2 = 1.0f + (-500.0f / ((pMob->field_110 + f) + 500.0f)); x1 /= (1.0f + 2.0f * x2); } return field_54 + x1 + f * (field_50 - field_54); } void GameRenderer::renderLevel(float f) { if (!m_pMinecraft->m_pMobPersp) { m_pMinecraft->m_pMobPersp = m_pMinecraft->m_pLocalPlayer; if (!m_pMinecraft->m_pMobPersp) { #ifndef ORIGINAL_CODE renderNoCamera(); #endif return; } } pick(f); Mob* pMob = m_pMinecraft->m_pMobPersp; Vec3 fCamPos; fCamPos.x = pMob->field_98.x + (pMob->m_pos.x - pMob->field_98.x) * f; fCamPos.y = pMob->field_98.y + (pMob->m_pos.y - pMob->field_98.y) * f; fCamPos.z = pMob->field_98.z + (pMob->m_pos.z - pMob->field_98.z) * f; bool bAnaglyph = m_pMinecraft->getOptions()->m_bAnaglyphs; LevelRenderer* pLR = m_pMinecraft->m_pLevelRenderer; ParticleEngine* pPE = m_pMinecraft->m_pParticleEngine; for (int i = 0; i < 2; i++) { if (bAnaglyph) { if (i > 0) glColorMask(true, false, false, false); else glColorMask(false, true, true, false); } glViewport(0, 0, Minecraft::width, Minecraft::height); setupClearColor(f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_CULL_FACE); setupCamera(f, i); saveMatrices(); /* if (m_pMinecraft->getOptions()->m_iViewDistance <= 1) { #ifndef ORIGINAL_CODE // @NOTE: For whatever reason, Minecraft doesn't enable GL_FOG right away. // It appears to work in bluestacks for whatever reason though... glEnable(GL_FOG); #endif setupFog(-1); pLR->renderSky(f); } */ glEnable(GL_FOG); setupFog(1); if (m_pMinecraft->getOptions()->m_bAmbientOcclusion) glShadeModel(GL_SMOOTH); Frustum& frust = Frustum::frustum; Frustum::doOurJobInGameRenderer(); FrustumCuller frustumCuller; frustumCuller.m_frustumData.x = frust; frustumCuller.prepare(fCamPos.x, fCamPos.y, fCamPos.z); pLR->cull(&frustumCuller, f); pLR->updateDirtyChunks(pMob, false); // TODO[v0.6.1]: what is (this+4)+63 (byte)? prepareAndRenderClouds(pLR, f); setupFog(0); glEnable(GL_FOG); m_pMinecraft->m_pTextures->loadAndBindTexture(C_TERRAIN_NAME); // render the opaque layer pLR->render(pMob, 0, f); glShadeModel(GL_FLAT); pLR->renderEntities(pMob->getPos(f), &frustumCuller, f); pPE->render(pMob, f); // @BUG: The original demo calls GL_BLEND. We really should be enabling GL_BLEND. //glEnable(GL_ALPHA); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); setupFog(0); #ifndef ORIGINAL_CODE glShadeModel(GL_SMOOTH); #endif glEnable(GL_BLEND); glDisable(GL_CULL_FACE); // glDepthMask(false); -- added in 0.1.1j. Introduces more issues than fixes // render the alpha layer m_pMinecraft->m_pTextures->loadAndBindTexture(C_TERRAIN_NAME); pLR->render(pMob, 1, f); glDepthMask(true); #ifndef ORIGINAL_CODE glShadeModel(GL_FLAT); #endif glEnable(GL_CULL_FACE); glDisable(GL_BLEND); if (field_44 == 1.0f && pMob->isPlayer() && m_pMinecraft->m_hitResult.m_hitType != HitResult::NONE && !pMob->isUnderLiquid(Material::water)) { glDisable(GL_ALPHA_TEST); if (m_pMinecraft->getOptions()->m_bBlockOutlines) pLR->renderHitOutline((Player*)pMob, m_pMinecraft->m_hitResult, 0, nullptr, f); else pLR->renderHitSelect((Player*)pMob, m_pMinecraft->m_hitResult, 0, nullptr, f); // added by iProgramInCpp - renders the cracks pLR->renderHit((Player*)pMob, m_pMinecraft->m_hitResult, 0, nullptr, f); glEnable(GL_ALPHA_TEST); } // @BUG? glDisable(GL_FOG); if (field_44 == 1.0f) { glClear(GL_DEPTH_BUFFER_BIT); renderItemInHand(f, i); } if (!bAnaglyph) break; } if (bAnaglyph) glColorMask(true, true, true, false); } void GameRenderer::render(float f) { if (m_pMinecraft->m_pLocalPlayer && m_pMinecraft->m_bGrabbedMouse) { Minecraft *pMC = m_pMinecraft; pMC->m_mouseHandler.poll(); float multPitch, diff_field_84; if (pMC->m_mouseHandler.smoothTurning()) { multPitch = -1.0f; float mult1 = 2.0f * (0.2f + pMC->getOptions()->field_8 * 0.6f); mult1 = mult1 * mult1 * mult1; float xd = 4.0f * mult1 * pMC->m_mouseHandler.m_delta.x; float yd = 4.0f * mult1 * pMC->m_mouseHandler.m_delta.y; float old_field_84 = field_84; field_84 = float(field_C) + f; diff_field_84 = field_84 - old_field_84; field_74 += xd; field_78 += yd; if (diff_field_84 > 3.0f) diff_field_84 = 3.0f; if (pMC->getOptions()->m_bInvertMouse) multPitch = 1.0f; if (!pMC->getOptions()->field_240) { // @TODO: untangle this code float v17 = xd + field_14; float v18 = field_18; float v19 = field_1C; field_14 = v17; float v20 = mult1 * 0.25f * (v17 - v18); float v21 = v19 + (v20 - v19) * 0.5f; field_1C = v21; if ((v20 <= 0.0 || v20 <= v21) && (v20 >= 0.0 || v20 >= v21)) v21 = mult1 * 0.25f * (v17 - v18); float v22 = yd + field_20; field_18 = v18 + v21; float v23 = field_24; field_20 = v22; float v24 = mult1 * 0.15f * (v22 - v23); float v25 = field_28 + (v24 - field_28) * 0.5f; field_28 = v25; if ((v24 <= 0.0 || v24 <= v25) && (v24 >= 0.0 || v24 >= v25)) v25 = v24; field_24 = v23 + v25; } } else { multPitch = -1.0f; if (pMC->getOptions()->m_bInvertMouse) multPitch = 1.0f; diff_field_84 = 1.0f; field_7C = pMC->m_mouseHandler.m_delta.x; field_80 = pMC->m_mouseHandler.m_delta.y; } pMC->m_pLocalPlayer->turn(diff_field_84 * field_7C, diff_field_84 * multPitch * field_80); } int mouseX = int(Mouse::getX() * Gui::InvGuiScale); int mouseY = int(Mouse::getY() * Gui::InvGuiScale); if (m_pMinecraft->isTouchscreen()) { int pointerId = Multitouch::getFirstActivePointerIdExThisUpdate(); if (pointerId < 0) { mouseX = -9999; mouseY = -9999; } else { mouseX = int(float(Multitouch::getX(pointerId)) * Gui::InvGuiScale); mouseY = int(float(Multitouch::getY(pointerId)) * Gui::InvGuiScale); } } if (m_pMinecraft->isLevelGenerated()) { if (t_keepPic < 0) { renderLevel(f); if (m_pMinecraft->getOptions()->m_bDontRenderGui) { if (!m_pMinecraft->m_pScreen) return; } m_pMinecraft->m_gui.render(f, m_pMinecraft->m_pScreen != nullptr, mouseX, mouseY); } } else { glViewport(0, 0, Minecraft::width, Minecraft::height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); setupGuiScreen(); } if (m_pMinecraft->m_pLocalPlayer && m_pMinecraft->m_pLocalPlayer->m_pMoveInput) m_pMinecraft->m_pLocalPlayer->m_pMoveInput->render(f); if (m_pMinecraft->m_pScreen) { glClear(GL_DEPTH_BUFFER_BIT); m_pMinecraft->m_pScreen->onRender(mouseX, mouseY, f); if (m_pMinecraft->m_pScreen && !m_pMinecraft->m_pScreen->isInGameScreen()) { #ifdef ORIGINAL_CODE // force some lag for some reason. I guess it's to make it spend more time actually generating the world? sleepMs(15); #endif } } std::stringstream debugText; debugText << "ReMinecraftPE " << m_pMinecraft->getVersionString(); debugText << "\n" << m_shownFPS << " fps, " << m_shownChunkUpdates << " chunk updates"; if (m_pMinecraft->getOptions()->m_bDebugText) { if (m_pMinecraft->m_pLocalPlayer) { char posStr[96]; Vec3 pos = m_pMinecraft->m_pLocalPlayer->getPos(f); sprintf(posStr, "%.2f, %.2f, %.2f", pos.x, pos.y, pos.z); debugText << "\npos: " << posStr; debugText << "\nentities: " << m_pMinecraft->m_pLevel->m_entities.size(); debugText << "\n" << m_pMinecraft->m_pLevelRenderer->gatherStats1(); } m_pMinecraft->m_pFont->drawShadow(debugText.str(), 2, 2, 0xFFFFFF); } int timeMs = getTimeMs(); if (timeMs - m_lastUpdatedMS >= 1000) { m_lastUpdatedMS = timeMs; m_shownFPS = m_pMinecraft->getFpsIntlCounter(); m_shownChunkUpdates = Chunk::updates; Chunk::updates = 0; } } void GameRenderer::tick() { --t_keepPic; #ifndef ORIGINAL_CODE // @BUG: If the game is left on for approximately 1,242 days, the counter will underflow, // causing the screen to appear frozen, and the level to not render. if (t_keepPic < -100) t_keepPic = -100; #endif if (!m_pMinecraft->m_pLocalPlayer) return; if (--t_keepHitResult == 0) m_pMinecraft->m_hitResult.m_hitType = HitResult::NONE; #ifndef ORIGINAL_CODE // Not harmless to let it underflow, but we won't anyway if (t_keepHitResult < -100) t_keepHitResult = -100; #endif float x1 = powf(fabsf(field_74), 1.2f); field_7C = x1 * 0.4f; if (field_74 < 0.0f) field_7C = -field_7C; float x2 = powf(fabsf(field_78), 1.2f); field_80 = x2 * 0.4f; if (field_78 < 0.0f) field_80 = -field_80; field_74 = 0.0f; field_78 = 0.0f; field_6C = field_70; field_30 = field_2C; field_38 = field_34; field_40 = field_3C; field_54 = field_50; field_5C = field_58; Mob* pMob = m_pMinecraft->m_pMobPersp; if (!pMob) { pMob = m_pMinecraft->m_pMobPersp = m_pMinecraft->m_pLocalPlayer; } float bright = m_pMinecraft->m_pLevel->getBrightness(Mth::floor(pMob->m_pos.x), Mth::floor(pMob->m_pos.y), Mth::floor(pMob->m_pos.z)); float x3 = float(3 - m_pMinecraft->getOptions()->m_iViewDistance); field_C++; float x4 = x3 / 3.0f; float x5 = (x4 + bright * (1.0f - x4) - field_70) * 0.1f; field_70 += x5; m_pItemInHandRenderer->tick(); } void GameRenderer::renderItemInHand(float f, int i) { glLoadIdentity(); if (m_pMinecraft->getOptions()->m_bAnaglyphs) glTranslatef(float(2 * i - 1) * 0.1f, 0.0f, 0.0f); glPushMatrix(); bobHurt(f); if (m_pMinecraft->getOptions()->m_bViewBobbing) bobView(f); if (!m_pMinecraft->getOptions()->m_bThirdPerson && !m_pMinecraft->getOptions()->m_bDontRenderGui) m_pItemInHandRenderer->render(f); glPopMatrix(); if (!m_pMinecraft->getOptions()->m_bThirdPerson) { m_pItemInHandRenderer->renderScreenEffect(f); bobHurt(f); } if (m_pMinecraft->getOptions()->m_bViewBobbing) bobView(f); } void GameRenderer::prepareAndRenderClouds(LevelRenderer* pLR, float f) { glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluPerspective(getFov(f), float(Minecraft::width) / float(Minecraft::height), 0.05f, field_8 * 512.0f); glMatrixMode(GL_MODELVIEW); glPushMatrix(); setupFog(0); glDepthMask(false); glEnable(GL_FOG); glFogf(GL_FOG_START, field_8 * 0.2f); glFogf(GL_FOG_END, field_8 * 0.75f); pLR->renderSky(f); glFogf(GL_FOG_START, field_8 * 4.2f * 0.6f); glFogf(GL_FOG_END, field_8 * 4.2f); pLR->renderClouds(f); glFogf(GL_FOG_START, field_8 * 0.6f); glFogf(GL_FOG_END, field_8); glDisable(GL_FOG); glDepthMask(true); setupFog(1); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); } void GameRenderer::onGraphicsReset() { } void GameRenderer::pick(float f) { if (!m_pMinecraft->m_pMobPersp || !m_pMinecraft->m_pLevel) return; Mob* pMob = m_pMinecraft->m_pMobPersp; HitResult& mchr = m_pMinecraft->m_hitResult; float dist = m_pMinecraft->m_pGameMode->getPickRange(); bool isFirstPerson = !m_pMinecraft->getOptions()->m_bThirdPerson; if (!m_pMinecraft->useSplitControls()) { Vec3 mobPos = pMob->getPos(f); Vec3 foundPosNear, foundPosFar; bool flag = true; float offset = isFirstPerson ? 6.0f : 12.0f; if (m_pMinecraft->m_pInputHolder->allowPicking()) { int viewport[4] = { 0 }; viewport[2] = Minecraft::width; viewport[3] = Minecraft::height; float obj_coord[3] = { 0 }; if (glhUnProjectf(m_pMinecraft->m_pInputHolder->m_feedbackX, Minecraft::height - m_pMinecraft->m_pInputHolder->m_feedbackY, 1.0f, m_matrix_model_view, m_matrix_projection, viewport, obj_coord)) { foundPosFar = mobPos + Vec3(obj_coord[0], obj_coord[1], obj_coord[2]); glhUnProjectf(m_pMinecraft->m_pInputHolder->m_feedbackX, Minecraft::height - m_pMinecraft->m_pInputHolder->m_feedbackY, 0.0f, m_matrix_model_view, m_matrix_projection, viewport, obj_coord); foundPosNear = mobPos + Vec3(obj_coord[0], obj_coord[1], obj_coord[2]); Vec3 diff = foundPosFar - foundPosNear; Vec3 normDiff = diff.normalize(); Vec3 normScaledDiff = normDiff.scale(offset); mobPos = foundPosNear + normScaledDiff; foundPosFar = mobPos; } // keep the hit result forever t_keepHitResult = -1; } else { t_keepHitResult = 1; // keep the tick result for exactly one frame flag = false; } if (flag) { if (isFirstPerson) { mchr = m_pMinecraft->m_pLevel->clip(foundPosNear, foundPosFar, false); } else { HitResult hr = m_pMinecraft->m_pLevel->clip(foundPosNear, foundPosFar, false); float diffX = float(hr.m_tileX) - m_pMinecraft->m_pMobPersp->m_pos.x; float diffY = float(hr.m_tileY) - m_pMinecraft->m_pMobPersp->m_pos.y; float diffZ = float(hr.m_tileZ) - m_pMinecraft->m_pMobPersp->m_pos.z; if (hr.m_hitType == HitResult::NONE || diffX * diffX + diffY * diffY + diffZ * diffZ > offset * offset) mchr.m_hitType = HitResult::NONE; else mchr = hr; } } } else { // easy case: pick from the middle of the screen HitResult hrMob = pMob->pick(dist, f); mchr = hrMob; } Vec3 mobPos = pMob->getPos(f); if (m_pMinecraft->m_hitResult.m_hitType != HitResult::NONE) dist = mchr.m_hitPos.distanceTo(mobPos); if (m_pMinecraft->m_pGameMode->isCreativeType()) dist = 32.0f; else if (dist > 3.0f) dist = 3.0f; Vec3 view = pMob->getViewVector(f); Vec3 exp = view * dist; Vec3 limit = mobPos + view * dist; field_10 = nullptr; AABB scanAABB = pMob->m_hitbox; if (exp.x < 0) scanAABB.min.x += exp.x; if (exp.x > 0) scanAABB.max.x += exp.x; if (exp.y < 0) scanAABB.min.y += exp.y; if (exp.y > 0) scanAABB.max.y += exp.y; if (exp.z < 0) scanAABB.min.z += exp.z; if (exp.z > 0) scanAABB.max.z += exp.z; scanAABB.grow(1, 1, 1); EntityVector* pEnts = m_pMinecraft->m_pLevel->getEntities(pMob, scanAABB); float fDist = 0.0f; for (int i = 0; i < int(pEnts->size()); i++) { Entity *pEnt = (*pEnts)[i]; if (!pEnt->isPickable()) continue; AABB checkAABB = pEnt->m_hitbox; checkAABB.grow(pEnt->getPickRadius()); HitResult hrMobChk = checkAABB.clip(mobPos, limit); if (checkAABB.contains(mobPos)) { if (fDist >= 0.0f) { //this is it brother field_10 = pEnt; fDist = 0.0f; } continue; } if (hrMobChk.m_hitType != HitResult::NONE) { float dX = hrMobChk.m_hitPos.x - mobPos.x; float dY = hrMobChk.m_hitPos.y - mobPos.y; float dZ = hrMobChk.m_hitPos.z - mobPos.z; float fNewDist = sqrtf(dX * dX + dY * dY + dZ * dZ); if (fDist > fNewDist || fDist == 0.0f) { field_10 = pEnt; fDist = fNewDist; } } } // picked entities take priority over tiles (?!) if (field_10) { m_pMinecraft->m_hitResult = HitResult(field_10); return; } if (m_pMinecraft->m_hitResult.m_hitType != HitResult::NONE || view.y >= -0.7f) return; mobPos = pMob->getPos(f); Vec3 checkVec = mobPos; checkVec.translate(0, -2, 0); HitResult hrLevelChk = m_pMinecraft->m_pLevel->clip(mobPos, checkVec); if (hrLevelChk.m_hitType == HitResult::NONE) return; mchr = hrLevelChk; mchr.m_bUnk24 = true; if (fabsf(view.x) <= fabsf(view.z)) { m_pMinecraft->m_hitResult.m_hitSide = view.z >= 0.0f ? HitResult::MAXZ : HitResult::MINZ; } else { m_pMinecraft->m_hitResult.m_hitSide = view.x >= 0.0f ? HitResult::MAXX : HitResult::MINX; } }