#include "stdafx.h" #include "GameRenderer.h" #include "ItemInHandRenderer.h" #include "LevelRenderer.h" #include "Frustum.h" #include "FrustumCuller.h" #include "Textures.h" #include "Tesselator.h" #include "ParticleEngine.h" #include "SmokeParticle.h" #include "WaterDropParticle.h" #include "GameMode.h" #include "CreativeMode.h" #include "Lighting.h" #include "Options.h" #include "MultiplayerLocalPlayer.h" #include "GuiParticles.h" #include "MultiPlayerLevel.h" #include "Chunk.h" #include "..\Minecraft.World\net.minecraft.world.entity.h" #include "..\Minecraft.World\net.minecraft.world.level.h" #include "..\Minecraft.World\net.minecraft.world.entity.player.h" #include "..\Minecraft.World\net.minecraft.world.phys.h" #include "..\Minecraft.World\net.minecraft.world.level.material.h" #include "..\Minecraft.World\net.minecraft.world.level.tile.h" #include "..\Minecraft.World\net.minecraft.world.level.chunk.h" #include "..\Minecraft.World\net.minecraft.world.level.biome.h" #include "..\Minecraft.World\net.minecraft.world.level.dimension.h" #include "..\Minecraft.World\System.h" #include "..\Minecraft.World\FloatBuffer.h" #include "..\Minecraft.World\ThreadName.h" #include "..\Minecraft.World\SparseLightStorage.h" #include "..\Minecraft.World\CompressedTileStorage.h" #include "..\Minecraft.World\SparseDataStorage.h" #include "..\Minecraft.World\JavaMath.h" #include "..\Minecraft.World\Facing.h" #include "..\Minecraft.World\MobEffect.h" #include "..\Minecraft.World\IntCache.h" #include "..\Minecraft.World\SmoothFloat.h" #include "..\Minecraft.World\MobEffectInstance.h" #include "..\Minecraft.World\Item.h" #include "Camera.h" #include "..\Minecraft.World\SoundTypes.h" #include "HumanoidModel.h" #include "..\Minecraft.World\Item.h" #include "..\Minecraft.World\compression.h" #include "PS3\PS3Extras\ShutdownManager.h" #include "TexturePackRepository.h" #include "TexturePack.h" bool GameRenderer::anaglyph3d = false; int GameRenderer::anaglyphPass = 0; #ifdef MULTITHREAD_ENABLE C4JThread* GameRenderer::m_updateThread; C4JThread::EventArray* GameRenderer::m_updateEvents; bool GameRenderer::nearThingsToDo = false; bool GameRenderer::updateRunning = false; vector GameRenderer::m_deleteStackByte; vector GameRenderer::m_deleteStackSparseLightStorage; vector GameRenderer::m_deleteStackCompressedTileStorage; vector GameRenderer::m_deleteStackSparseDataStorage; #endif CRITICAL_SECTION GameRenderer::m_csDeleteStack; GameRenderer::GameRenderer(Minecraft *mc) { // 4J - added this block of initialisers renderDistance = 0; _tick = 0; hovered = nullptr; thirdDistance = 4; thirdDistanceO = 4; thirdRotation = 0; thirdRotationO = 0; thirdTilt = 0; thirdTiltO = 0; accumulatedSmoothXO = 0; accumulatedSmoothYO = 0; tickSmoothXO = 0; tickSmoothYO = 0; lastTickA = 0; cameraPos = Vec3::newPermanent(0.0f,0.0f,0.0f); fovOffset = 0; fovOffsetO = 0; cameraRoll = 0; cameraRollO = 0; for( int i = 0; i < 4; i++ ) { fov[i] = 0.0f; oFov[i] = 0.0f; tFov[i] = 0.0f; } isInClouds = false; zoom = 1; zoom_x = 0; zoom_y = 0; rainXa = NULL; rainZa = NULL; lastActiveTime = Minecraft::currentTimeMillis(); lastNsTime = 0; random = new Random(); rainSoundTime = 0; xMod = 0; yMod = 0; lb = MemoryTracker::createFloatBuffer(16); fr = 0.0f; fg = 0.0f; fb = 0.0f; fogBrO = 0.0f; fogBr = 0.0f; cameraFlip = 0; _updateLightTexture = false; blr = 0.0f; blrt = 0.0f; blg = 0.0f; blgt = 0.0f; m_fov=70.0f; // 4J Stu - Init these so they are setup before the tick for( int i = 0; i < 4; i++ ) { fov[i] = oFov[i] = 1.0f; } this->mc = mc; itemInHandRenderer = NULL; // 4J-PB - set up the local players iteminhand renderers here - needs to be done with lighting enabled so that the render geometry gets compiled correctly glEnable(GL_LIGHTING); mc->localitemInHandRenderers[0] = new ItemInHandRenderer(mc);//itemInHandRenderer; mc->localitemInHandRenderers[1] = new ItemInHandRenderer(mc); mc->localitemInHandRenderers[2] = new ItemInHandRenderer(mc); mc->localitemInHandRenderers[3] = new ItemInHandRenderer(mc); glDisable(GL_LIGHTING); // 4J - changes brought forward from 1.8.2 BufferedImage *img = new BufferedImage(16, 16, BufferedImage::TYPE_INT_RGB); for( int i = 0; i < NUM_LIGHT_TEXTURES; i++ ) { lightTexture[i] = mc->textures->getTexture(img); // 4J - changed to one light texture per level to support split screen } delete img; #ifdef __PS3__ // we're using the RSX now to upload textures to vram, so we need the main ram textures allocated from io space for(int i=0;iSet(eUpdateEventIsFinished); InitializeCriticalSection(&m_csDeleteStack); m_updateThread = new C4JThread(runUpdate, NULL, "Chunk update"); #ifdef __PS3__ m_updateThread->SetPriority(THREAD_PRIORITY_ABOVE_NORMAL); #endif// __PS3__ m_updateThread->SetProcessor(CPU_CORE_CHUNK_UPDATE); m_updateThread->Run(); #endif } // 4J Stu Added to go with 1.8.2 change GameRenderer::~GameRenderer() { if(rainXa != NULL) delete [] rainXa; if(rainZa != NULL) delete [] rainZa; } void GameRenderer::tick(bool first) // 4J - add bFirst { tickFov(); tickLightTexture(); // 4J - change brought forward from 1.8.2 fogBrO = fogBr; thirdDistanceO = thirdDistance; thirdRotationO = thirdRotation; thirdTiltO = thirdTilt; fovOffsetO = fovOffset; cameraRollO = cameraRoll; if (ClientConstants::DEADMAU5_CAMERA_CHEATS) { if (mc->screen == NULL) { float distanceDelta = 0; float rotationDelta = 0; float tiltDelta = 0; float rollDelta = 0; if (Keyboard::isKeyDown(Keyboard::KEY_U)) { distanceDelta -= .3f * mc->options->cameraSpeed; } else if (Keyboard::isKeyDown(Keyboard::KEY_O)) { distanceDelta += .3f * mc->options->cameraSpeed; } if (Keyboard::isKeyDown(Keyboard::KEY_J)) { rotationDelta += 8.0f * mc->options->cameraSpeed; } else if (Keyboard::isKeyDown(Keyboard::KEY_L)) { rotationDelta -= 8.0f * mc->options->cameraSpeed; } if (Keyboard::isKeyDown(Keyboard::KEY_I)) { tiltDelta += 6.0f * mc->options->cameraSpeed; } else if (Keyboard::isKeyDown(Keyboard::KEY_K)) { tiltDelta -= 6.0f * mc->options->cameraSpeed; } if (Keyboard::isKeyDown(Keyboard::KEY_Y) && Keyboard::isKeyDown(Keyboard::KEY_H)) { fovOffset = 0; } else if (Keyboard::isKeyDown(Keyboard::KEY_Y)) { fovOffset -= 3.0f; } else if (Keyboard::isKeyDown(Keyboard::KEY_H)) { fovOffset += 3.0f; } if (Keyboard::isKeyDown(Keyboard::KEY_N) && Keyboard::isKeyDown(Keyboard::KEY_M)) { cameraRoll = 0; } else if (Keyboard::isKeyDown(Keyboard::KEY_N)) { rollDelta -= 8.0f * mc->options->cameraSpeed; } else if (Keyboard::isKeyDown(Keyboard::KEY_M)) { rollDelta += 8.0f * mc->options->cameraSpeed; } if (mc->options->smoothCamera) { distanceDelta = smoothDistance.getNewDeltaValue(distanceDelta, .5f * mc->options->sensitivity); rotationDelta = smoothRotation.getNewDeltaValue(rotationDelta, .5f * mc->options->sensitivity); tiltDelta = smoothTilt.getNewDeltaValue(tiltDelta, .5f * mc->options->sensitivity); rollDelta = smoothRoll.getNewDeltaValue(rollDelta, .5f * mc->options->sensitivity); } thirdDistance += distanceDelta; thirdRotation += rotationDelta; thirdTilt += tiltDelta; cameraRoll += rollDelta; } } if (mc->options->smoothCamera) { // update player view in tick() instead of render() to maintain // camera movement regardless of FPS float ss = mc->options->sensitivity * 0.6f + 0.2f; float sens = (ss * ss * ss) * 8; tickSmoothXO = smoothTurnX.getNewDeltaValue(accumulatedSmoothXO, 0.05f * sens); tickSmoothYO = smoothTurnY.getNewDeltaValue(accumulatedSmoothYO, 0.05f * sens); lastTickA = 0; accumulatedSmoothXO = 0; accumulatedSmoothYO = 0; } if (mc->cameraTargetPlayer == NULL) { mc->cameraTargetPlayer = dynamic_pointer_cast(mc->player); } float brr = mc->level->getBrightness(Mth::floor(mc->cameraTargetPlayer->x), Mth::floor(mc->cameraTargetPlayer->y), Mth::floor(mc->cameraTargetPlayer->z)); float whiteness = (3 - mc->options->viewDistance) / 3.0f; float fogBrT = brr * (1 - whiteness) + whiteness; fogBr += (fogBrT - fogBr) * 0.1f; itemInHandRenderer->tick(); PIXBeginNamedEvent(0,"Rain tick"); tickRain(); PIXEndNamedEvent(); if( mc->player != mc->localplayers[ProfileManager.GetPrimaryPad()] ) return; // 4J added for split screen - only do rest of processing for once per frame _tick++; } void GameRenderer::pick(float a) { if (mc->cameraTargetPlayer == NULL) return; if (mc->level == NULL) return; double range = mc->gameMode->getPickRange(); delete mc->hitResult; MemSect(31); mc->hitResult = mc->cameraTargetPlayer->pick(range, a); MemSect(0); // 4J - added - stop blocks right at the edge of the world from being pickable so we shouldn't be able to directly destroy or create anything there if( mc->hitResult ) { int maxxz = ( ( mc->level->chunkSource->m_XZSize / 2 ) * 16 ) - 2; int minxz = ( -( mc->level->chunkSource->m_XZSize / 2 ) * 16 ) + 1; // Don't select the tops of the very edge blocks, or the sides of the next blocks in // 4J Stu - Only block the sides that are facing an outside block int hitx = mc->hitResult->x; int hitz = mc->hitResult->z; int face = mc->hitResult->f; if( face == Facing::WEST && hitx < 0 ) hitx -= 1; if( face == Facing::EAST && hitx > 0 ) hitx += 1; if( face == Facing::NORTH && hitz < 0 ) hitz -= 1; if( face == Facing::SOUTH && hitz > 0 ) hitz += 1; if( ( hitx < minxz ) || ( hitx > maxxz) || ( hitz < minxz ) || ( hitz > maxxz) ) { delete mc->hitResult; mc->hitResult = NULL; } } double dist = range; Vec3 *from = mc->cameraTargetPlayer->getPos(a); if (mc->gameMode->hasFarPickRange()) { dist = range = 6; } else { if (dist > 3) dist = 3; range = dist; } if (mc->hitResult != NULL) { dist = mc->hitResult->pos->distanceTo(from); } Vec3 *b = mc->cameraTargetPlayer->getViewVector(a); Vec3 *to = from->add(b->x * range, b->y * range, b->z * range); hovered = nullptr; float overlap = 1; vector > *objects = mc->level->getEntities(mc->cameraTargetPlayer, mc->cameraTargetPlayer->bb->expand(b->x * (range), b->y * (range), b->z * (range))->grow(overlap, overlap, overlap)); double nearest = dist; AUTO_VAR(itEnd, objects->end()); for (AUTO_VAR(it, objects->begin()); it != itEnd; it++) { shared_ptr e = *it; //objects->at(i); if (!e->isPickable()) continue; float rr = e->getPickRadius(); AABB *bb = e->bb->grow(rr, rr, rr); HitResult *p = bb->clip(from, to); if (bb->contains(from)) { if (0 < nearest || nearest == 0) { hovered = e; nearest = 0; } } else if (p != NULL) { double dd = from->distanceTo(p->pos); if (dd < nearest || nearest == 0) { hovered = e; nearest = dd; } } delete p; } if (hovered != NULL) { if (nearest < dist || (mc->hitResult == NULL)) { if( mc->hitResult != NULL ) delete mc->hitResult; mc->hitResult = new HitResult(hovered); } } } void GameRenderer::SetFovVal(float fov) { m_fov=fov; } float GameRenderer::GetFovVal() { return m_fov; } void GameRenderer::tickFov() { shared_ptrplayer = dynamic_pointer_cast(mc->cameraTargetPlayer); int playerIdx = player ? player->GetXboxPad() : 0; tFov[playerIdx] = player->getFieldOfViewModifier(); oFov[playerIdx] = fov[playerIdx]; fov[playerIdx] += (tFov[playerIdx] - fov[playerIdx]) * 0.5f; } float GameRenderer::getFov(float a, bool applyEffects) { if (cameraFlip > 0 ) return 90; shared_ptr player = dynamic_pointer_cast(mc->cameraTargetPlayer); int playerIdx = player ? player->GetXboxPad() : 0; float fov = m_fov;//70; if (applyEffects) { fov += mc->options->fov * 40; fov *= this->oFov[playerIdx] + (this->fov[playerIdx] - this->oFov[playerIdx]) * a; } if (player->getHealth() <= 0) { float duration = player->deathTime + a; fov /= ((1 - 500 / (duration + 500)) * 2.0f + 1); } int t = Camera::getBlockAt(mc->level, player, a); if (t != 0 && Tile::tiles[t]->material == Material::water) fov = fov * 60 / 70; return fov + fovOffsetO + (fovOffset - fovOffsetO) * a; } void GameRenderer::bobHurt(float a) { shared_ptr player = mc->cameraTargetPlayer; float hurt = player->hurtTime - a; if (player->getHealth() <= 0) { float duration = player->deathTime + a; glRotatef(40 - (40 * 200) / (duration + 200), 0, 0, 1); } if (hurt < 0) return; hurt /= player->hurtDuration; hurt = (float) Mth::sin(hurt * hurt * hurt * hurt * PI); float rr = player->hurtDir; glRotatef(-rr, 0, 1, 0); glRotatef(-hurt * 14, 0, 0, 1); glRotatef(+rr, 0, 1, 0); } void GameRenderer::bobView(float a) { shared_ptr player = dynamic_pointer_cast(mc->cameraTargetPlayer); if(player==NULL) { return; } //shared_ptr player = dynamic_pointer_cast(mc->cameraTargetPlayer); float wda = player->walkDist - player->walkDistO; float b = -(player->walkDist + wda * a); float bob = player->oBob + (player->bob - player->oBob) * a; float tilt = player->oTilt + (player->tilt - player->oTilt) * a; glTranslatef((float) Mth::sin(b * PI) * bob * 0.5f, -(float) abs(Mth::cos(b * PI) * bob), 0); glRotatef((float) Mth::sin(b * PI) * bob * 3, 0, 0, 1); glRotatef((float) abs(Mth::cos(b * PI - 0.2f) * bob) * 5, 1, 0, 0); glRotatef((float) tilt, 1, 0, 0); } void GameRenderer::moveCameraToPlayer(float a) { shared_ptr player = mc->cameraTargetPlayer; shared_ptr localplayer = dynamic_pointer_cast(mc->cameraTargetPlayer); float heightOffset = player->heightOffset - 1.62f; double x = player->xo + (player->x - player->xo) * a; double y = player->yo + (player->y - player->yo) * a - heightOffset; double z = player->zo + (player->z - player->zo) * a; glRotatef(cameraRollO + (cameraRoll - cameraRollO) * a, 0, 0, 1); if (player->isSleeping()) { heightOffset += 1.0; glTranslatef(0.0f, 0.3f, 0); if (!mc->options->fixedCamera) { int t = mc->level->getTile(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); if (t == Tile::bed_Id) { int data = mc->level->getData(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); int direction = data & 3; glRotatef((float)direction * 90,0.0f, 1.0f, 0.0f); } glRotatef(player->yRotO + (player->yRot - player->yRotO) * a + 180, 0, -1, 0); glRotatef(player->xRotO + (player->xRot - player->xRotO) * a, -1, 0, 0); } } // 4J-PB - changing this to be per player //else if (mc->options->thirdPersonView) else if (localplayer->ThirdPersonView()) { double cameraDist = thirdDistanceO + (thirdDistance - thirdDistanceO) * a; if (mc->options->fixedCamera) { float rotationY = thirdRotationO + (thirdRotation - thirdRotationO) * a; float xRot = thirdTiltO + (thirdTilt - thirdTiltO) * a; glTranslatef(0, 0, (float) -cameraDist); glRotatef(xRot, 1, 0, 0); glRotatef(rotationY, 0, 1, 0); } else { // 4J - corrected bug where this used to just take player->xRot & yRot directly and so wasn't taking into account interpolation, allowing camera to go through walls float yRot = player->yRotO + (player->yRot - player->yRotO) * a; float xRot = player->xRotO + (player->xRot - player->xRotO) * a; // Thirdperson view values are now 0 for disabled, 1 for original mode, 2 for reversed. if( localplayer->ThirdPersonView() == 2 ) { // Reverse y rotation - note that this is only used in doing collision to calculate our view // distance, the actual rotation itself is just below this else {} block yRot += 180.0f; } double xd = -Mth::sin(yRot / 180 * PI) * Mth::cos(xRot / 180 * PI) * cameraDist; double zd = Mth::cos(yRot / 180 * PI) * Mth::cos(xRot / 180 * PI) * cameraDist; double yd = -Mth::sin(xRot / 180 * PI) * cameraDist; for (int i = 0; i < 8; i++) { float xo = (float)((i & 1) * 2 - 1); float yo = (float)(((i >> 1) & 1) * 2 - 1); float zo = (float)(((i >> 2) & 1) * 2 - 1); xo *= 0.1f; yo *= 0.1f; zo *= 0.1f; // 4J - corrected bug here where zo was also added to x component HitResult *hr = mc->level->clip(Vec3::newTemp(x + xo, y + yo, z + zo), Vec3::newTemp(x - xd + xo, y - yd + yo, z - zd + zo)); if (hr != NULL) { double dist = hr->pos->distanceTo(Vec3::newTemp(x, y, z)); if (dist < cameraDist) cameraDist = dist; delete hr; } } // 4J - removed extra rotations here that aren't needed because our xRot/yRot don't ever // deviate from the player's view direction // glRotatef(player->xRot - xRot, 1, 0, 0); // glRotatef(player->yRot - yRot, 0, 1, 0); glTranslatef(0, 0, (float) -cameraDist); // glRotatef(yRot - player->yRot, 0, 1, 0); // glRotatef(xRot - player->xRot, 1, 0, 0); } } else { glTranslatef(0, 0, -0.1f); } if (!mc->options->fixedCamera) { glRotatef(player->xRotO + (player->xRot - player->xRotO) * a, 1, 0, 0); if( localplayer->ThirdPersonView() == 2 ) { // Third person view is now 0 for disabled, 1 for original, 2 for flipped glRotatef(player->yRotO + (player->yRot - player->yRotO) * a, 0, 1, 0); } else { glRotatef(player->yRotO + (player->yRot - player->yRotO) * a + 180, 0, 1, 0); } } glTranslatef(0, heightOffset, 0); x = player->xo + (player->x - player->xo) * a; y = player->yo + (player->y - player->yo) * a - heightOffset; z = player->zo + (player->z - player->zo) * a; isInClouds = mc->levelRenderer->isInCloud(x, y, z, a); } void GameRenderer::zoomRegion(double zoom, double xa, double ya) { this->zoom = zoom; this->zoom_x = xa; this->zoom_y = ya; } void GameRenderer::unZoomRegion() { zoom = 1; } // 4J added as we have more complex adjustments to make for fov & aspect on account of viewports void GameRenderer::getFovAndAspect(float& fov, float& aspect, float a, bool applyEffects) { // 4J - split out aspect ratio and fov here so we can adjust for viewports - we might need to revisit these as // they are maybe be too generous for performance. aspect = mc->width / (float) mc->height; fov = getFov(a, applyEffects); if( ( mc->player->m_iScreenSection == C4JRender::VIEWPORT_TYPE_SPLIT_TOP ) || ( mc->player->m_iScreenSection == C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM ) ) { aspect *= 2.0f; fov *= 0.7f; // Reduce FOV to make things less fish-eye, at the expense of reducing vertical FOV from single player mode } else if( ( mc->player->m_iScreenSection == C4JRender::VIEWPORT_TYPE_SPLIT_LEFT ) || ( mc->player->m_iScreenSection == C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT) ) { // Ideally I'd like to make the fov bigger here, but if I do then you an see that the arm isn't very long... aspect *= 0.5f; } } void GameRenderer::setupCamera(float a, int eye) { renderDistance = (float)(16 * 16 >> (mc->options->viewDistance)); glMatrixMode(GL_PROJECTION); glLoadIdentity(); float stereoScale = 0.07f; if (mc->options->anaglyph3d) glTranslatef(-(eye * 2 - 1) * stereoScale, 0, 0); // 4J - have split out fov & aspect calculation so we can take into account viewports float aspect, fov; getFovAndAspect(fov, aspect, a, true); if (zoom != 1) { glTranslatef((float) zoom_x, (float) -zoom_y, 0); glScaled(zoom, zoom, 1); } gluPerspective(fov, aspect, 0.05f, renderDistance * 2); if (mc->gameMode->isCutScene()) { float s = 1 / 1.5f; glScalef(1, s, 1); } glMatrixMode(GL_MODELVIEW); glLoadIdentity(); if (mc->options->anaglyph3d) glTranslatef((eye * 2 - 1) * 0.10f, 0, 0); bobHurt(a); // 4J-PB - this is a per-player option //if (mc->options->bobView) bobView(a); bool bNoLegAnim =(mc->player->getAnimOverrideBitmask()&(1<player->getAnimOverrideBitmask()&(1<player->GetXboxPad(),eGameSetting_ViewBob) && !mc->player->abilities.flying && !bNoLegAnim && !bNoBobbingAnim) bobView(a); float pt = mc->player->oPortalTime + (mc->player->portalTime - mc->player->oPortalTime) * a; if (pt > 0) { int multiplier = 20; if (mc->player->hasEffect(MobEffect::confusion)) { multiplier = 7; } float skew = 5 / (pt * pt + 5) - pt * 0.04f; skew *= skew; glRotatef((_tick + a) * multiplier, 0, 1, 1); glScalef(1 / skew, 1, 1); glRotatef(-(_tick + a) * multiplier, 0, 1, 1); } moveCameraToPlayer(a); if (cameraFlip > 0) { int i = cameraFlip - 1; if (i == 1) glRotatef(90, 0, 1, 0); if (i == 2) glRotatef(180, 0, 1, 0); if (i == 3) glRotatef(-90, 0, 1, 0); if (i == 4) glRotatef(90, 1, 0, 0); if (i == 5) glRotatef(-90, 1, 0, 0); } } void GameRenderer::renderItemInHand(float a, int eye) { if (cameraFlip > 0) return; shared_ptr localplayer = dynamic_pointer_cast(mc->cameraTargetPlayer); // 4J-PB - to turn off the hand for screenshots, but not when the item held is a map if ( localplayer!=NULL) { shared_ptr item = localplayer->inventory->getSelected(); if(!(item && item->getItem()->id==Item::map_Id) && app.GetGameSettings(localplayer->GetXboxPad(),eGameSetting_DisplayHand)==0 ) return; } glMatrixMode(GL_PROJECTION); glLoadIdentity(); float stereoScale = 0.07f; if (mc->options->anaglyph3d) glTranslatef(-(eye * 2 - 1) * stereoScale, 0, 0); // 4J - have split out fov & aspect calculation so we can take into account viewports float fov, aspect; getFovAndAspect(fov, aspect, a, false); if (zoom != 1) { glTranslatef((float) zoom_x, (float) -zoom_y, 0); glScaled(zoom, zoom, 1); } gluPerspective(fov, aspect, 0.05f, renderDistance * 2); if (mc->gameMode->isCutScene()) { float s = 1 / 1.5f; glScalef(1, s, 1); } glMatrixMode(GL_MODELVIEW); glLoadIdentity(); if (mc->options->anaglyph3d) glTranslatef((eye * 2 - 1) * 0.10f, 0, 0); glPushMatrix(); bobHurt(a); // 4J-PB - changing this to be per player //if (mc->options->bobView) bobView(a); bool bNoLegAnim =(localplayer->getAnimOverrideBitmask()&( (1<GetXboxPad(),eGameSetting_ViewBob) && !localplayer->abilities.flying && !bNoLegAnim) bobView(a); // 4J-PB - changing this to be per player //if (!mc->options->thirdPersonView && !mc->cameraTargetPlayer->isSleeping()) if (!localplayer->ThirdPersonView() && !mc->cameraTargetPlayer->isSleeping()) { if (!mc->options->hideGui && !mc->gameMode->isCutScene()) { turnOnLightLayer(a); PIXBeginNamedEvent(0,"Item in hand render"); itemInHandRenderer->render(a); PIXEndNamedEvent(); turnOffLightLayer(a); } } glPopMatrix(); // 4J-PB - changing this to be per player //if (!mc->options->thirdPersonView && !mc->cameraTargetPlayer->isSleeping()) if (!localplayer->ThirdPersonView() && !mc->cameraTargetPlayer->isSleeping()) { itemInHandRenderer->renderScreenEffect(a); bobHurt(a); } // 4J-PB - changing this to be per player //if (mc->options->bobView) bobView(a); if(app.GetGameSettings(localplayer->GetXboxPad(),eGameSetting_ViewBob) && !localplayer->abilities.flying && !bNoLegAnim) bobView(a); } // 4J - change brought forward from 1.8.2 void GameRenderer::turnOffLightLayer(double alpha) { // 4J - TODO #if 0 if (SharedConstants::TEXTURE_LIGHTING) { glClientActiveTexture(GL_TEXTURE1); glActiveTexture(GL_TEXTURE1); glDisable(GL_TEXTURE_2D); glClientActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0); } #endif RenderManager.TextureBindVertex(-1); } // 4J - change brought forward from 1.8.2 void GameRenderer::turnOnLightLayer(double alpha) { // 4J - TODO #if 0 if (SharedConstants::TEXTURE_LIGHTING) { glClientActiveTexture(GL_TEXTURE1); glActiveTexture(GL_TEXTURE1); glMatrixMode(GL_TEXTURE); glLoadIdentity(); // float s = 1 / 16f / 15.0f*16/14.0f; float s = 1 / 16.0f / 15.0f * 15 / 16; glScalef(s, s, s); glTranslatef(8f, 8f, 8f); glMatrixMode(GL_MODELVIEW); mc->textures->bind(lightTexture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glColor4f(1, 1, 1, 1); glEnable(GL_TEXTURE_2D); glClientActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0); } #endif RenderManager.TextureBindVertex(getLightTexture(mc->player->GetXboxPad(), mc->level)); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); } // 4J - change brought forward from 1.8.2 void GameRenderer::tickLightTexture() { blrt += (float)((Math::random() - Math::random()) * Math::random() * Math::random()); blgt += (float)((Math::random() - Math::random()) * Math::random() * Math::random()); blrt *= 0.9; blgt *= 0.9; blr += (blrt - blr) * 1; blg += (blgt - blg) * 1; _updateLightTexture = true; } void GameRenderer::updateLightTexture(float a) { // 4J-JEV: Now doing light textures on PER PLAYER basis. // 4J - we *had* added separate light textures for all dimensions, and this loop to update them all here for(int j = 0; j < XUSER_MAX_COUNT; j++ ) { // Loop over all the players shared_ptr player = Minecraft::GetInstance()->localplayers[j]; if (player == NULL) continue; Level *level = player->level; // 4J - was mc->level when it was just to update the one light texture float skyDarken1 = level->getSkyDarken((float) 1); for (int i = 0; i < 256; i++) { float darken = skyDarken1 * 0.95f + 0.05f; float sky = level->dimension->brightnessRamp[i / 16] * darken; float block = level->dimension->brightnessRamp[i % 16] * (blr * 0.1f + 1.5f); if (level->lightningBoltTime > 0) { sky = level->dimension->brightnessRamp[i / 16]; } float rs = sky * (skyDarken1 * 0.65f + 0.35f); float gs = sky * (skyDarken1 * 0.65f + 0.35f); float bs = sky; float rb = block; float gb = block * ((block * 0.6f + 0.4f) * 0.6f + 0.4f); float bb = block * ((block * block) * 0.6f + 0.4f); float _r = (rs + rb); float _g = (gs + gb); float _b = (bs + bb); _r = _r * 0.96f + 0.03f; _g = _g * 0.96f + 0.03f; _b = _b * 0.96f + 0.03f; if (level->dimension->id == 1) { _r = (0.22f + rb * 0.75f); _g = (0.28f + gb * 0.75f); _b = (0.25f + bb * 0.75f); } if (player->hasEffect(MobEffect::nightVision)) { float scale = getNightVisionScale(player, a); { float dist = 1.0f / _r; if (dist > (1.0f / _g)) { dist = (1.0f / _g); } if (dist > (1.0f / _b)) { dist = (1.0f / _b); } _r = _r * (1.0f - scale) + (_r * dist) * scale; _g = _g * (1.0f - scale) + (_g * dist) * scale; _b = _b * (1.0f - scale) + (_b * dist) * scale; } } float brightness = 0.0f; // 4J - TODO - was mc->options->gamma; if (_r > 1) _r = 1; if (_g > 1) _g = 1; if (_b > 1) _b = 1; float ir = 1 - _r; float ig = 1 - _g; float ib = 1 - _b; ir = 1 - (ir * ir * ir * ir); ig = 1 - (ig * ig * ig * ig); ib = 1 - (ib * ib * ib * ib); _r = _r * (1 - brightness) + ir * brightness; _g = _g * (1 - brightness) + ig * brightness; _b = _b * (1 - brightness) + ib * brightness; _r = _r * 0.96f + 0.03f; _g = _g * 0.96f + 0.03f; _b = _b * 0.96f + 0.03f; if (_r > 1) _r = 1; if (_g > 1) _g = 1; if (_b > 1) _b = 1; if (_r < 0) _r = 0; if (_g < 0) _g = 0; if (_b < 0) _b = 0; int a = 255; int r = (int) (_r * 255); int g = (int) (_g * 255); int b = (int) (_b * 255); #if defined _WIN64 lightPixels[j][i] = a << 24 | b << 16 | g << 8 | r; #else lightPixels[j][i] = r << 24 | g << 16 | b << 8 | a; #endif } mc->textures->replaceTextureDirect( lightPixels[j], 16, 16, getLightTexture(j,level) ); } } float GameRenderer::getNightVisionScale(shared_ptr player, float a) { int duration = player->getEffect(MobEffect::nightVision)->getDuration(); if (duration > (SharedConstants::TICKS_PER_SECOND * 10)) { return 1.0f; } else { float flash = max(0.0f, (float)duration - a); return .7f + Mth::sin(flash * PI * .05f) * .3f; // was: .7 + sin(flash*pi*0.2) * .3 } } // 4J added, so we can have a light texture for each player to support split screen int GameRenderer::getLightTexture(int iPad, Level *level) { // Turn the current dimenions id into an index from 0 to 2 // int idx = level->dimension->id; // if( idx == -1 ) idx = 2; return lightTexture[iPad]; // 4J-JEV: Changing to Per Player lighting textures. } void GameRenderer::render(float a, bool bFirst) { if( _updateLightTexture && bFirst) updateLightTexture(a); if (Display::isActive()) { lastActiveTime = System::currentTimeMillis(); } else { if (System::currentTimeMillis() - lastActiveTime > 500) { mc->pauseGame(); } } #if 0 // 4J - TODO if (mc->mouseGrabbed) { mc->mouseHandler.poll(); float ss = mc->options->sensitivity * 0.6f + 0.2f; float sens = (ss * ss * ss) * 8; float xo = mc->mouseHandler.xd * sens; float yo = mc->mouseHandler.yd * sens; int yAxis = 1; if (mc->options->invertYMouse) yAxis = -1; if (Minecraft.DEADMAU5_CAMERA_CHEATS) { if (!mc->options->fixedCamera) { if (Keyboard.isKeyDown(Keyboard.KEY_J)) { xo = -12f * mc->options->sensitivity; } else if (Keyboard.isKeyDown(Keyboard.KEY_L)) { xo = 12f * mc->options->sensitivity; } if (Keyboard.isKeyDown(Keyboard.KEY_I)) { yo = 12f * mc->options->sensitivity; } else if (Keyboard.isKeyDown(Keyboard.KEY_K)) { yo = -12f * mc->options->sensitivity; } } } if (mc->options->smoothCamera) { xo = smoothTurnX.getNewDeltaValue(xo, .05f * sens); yo = smoothTurnY.getNewDeltaValue(yo, .05f * sens); } mc->player.turn(xo, yo * yAxis); } #endif if (mc->noRender) return; GameRenderer::anaglyph3d = mc->options->anaglyph3d; glViewport(0, 0, mc->width, mc->height); // 4J - added ScreenSizeCalculator ssc(mc->options, mc->width, mc->height); int screenWidth = ssc.getWidth(); int screenHeight = ssc.getHeight(); int xMouse = Mouse::getX() * screenWidth / mc->width; int yMouse = screenHeight - Mouse::getY() * screenHeight / mc->height - 1; int maxFps = getFpsCap(mc->options->framerateLimit); if (mc->level != NULL) { if (mc->options->framerateLimit == 0) { renderLevel(a, 0); } else { renderLevel(a, lastNsTime + 1000000000 / maxFps); } lastNsTime = System::nanoTime(); if (!mc->options->hideGui || mc->screen != NULL) { mc->gui->render(a, mc->screen != NULL, xMouse, yMouse); } } else { glViewport(0, 0, mc->width, mc->height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); setupGuiScreen(); lastNsTime = System::nanoTime(); } if (mc->screen != NULL) { glClear(GL_DEPTH_BUFFER_BIT); mc->screen->render(xMouse, yMouse, a); if (mc->screen != NULL && mc->screen->particles != NULL) mc->screen->particles->render(a); } } void GameRenderer::renderLevel(float a) { renderLevel(a, 0); } #ifdef MULTITHREAD_ENABLE // Request that an item be deleted, when it is safe to do so void GameRenderer::AddForDelete(byte *deleteThis) { EnterCriticalSection(&m_csDeleteStack); m_deleteStackByte.push_back(deleteThis); } void GameRenderer::AddForDelete(SparseLightStorage *deleteThis) { EnterCriticalSection(&m_csDeleteStack); m_deleteStackSparseLightStorage.push_back(deleteThis); } void GameRenderer::AddForDelete(CompressedTileStorage *deleteThis) { EnterCriticalSection(&m_csDeleteStack); m_deleteStackCompressedTileStorage.push_back(deleteThis); } void GameRenderer::AddForDelete(SparseDataStorage *deleteThis) { EnterCriticalSection(&m_csDeleteStack); m_deleteStackSparseDataStorage.push_back(deleteThis); } void GameRenderer::FinishedReassigning() { LeaveCriticalSection(&m_csDeleteStack); } int GameRenderer::runUpdate(LPVOID lpParam) { Minecraft *minecraft = Minecraft::GetInstance(); Vec3::CreateNewThreadStorage(); AABB::CreateNewThreadStorage(); IntCache::CreateNewThreadStorage(); Tesselator::CreateNewThreadStorage(1024*1024); Compression::UseDefaultThreadStorage(); RenderManager.InitialiseContext(); #ifdef _LARGE_WORLDS Chunk::CreateNewThreadStorage(); #endif Tile::CreateNewThreadStorage(); ShutdownManager::HasStarted(ShutdownManager::eRenderChunkUpdateThread,m_updateEvents); while(ShutdownManager::ShouldRun(ShutdownManager::eRenderChunkUpdateThread)) { //m_updateEvents->Clear(eUpdateEventIsFinished); //m_updateEvents->WaitForSingle(eUpdateCanRun,INFINITE); // 4J Stu - We Need to have this happen atomically to avoid deadlocks m_updateEvents->WaitForAll(INFINITE); if( !ShutdownManager::ShouldRun(ShutdownManager::eRenderChunkUpdateThread) ) { break; } m_updateEvents->Set(eUpdateCanRun); // PIXBeginNamedEvent(0,"Updating dirty chunks %d",(count++)&7); // Update chunks atomically until there aren't any very near ones left - they will be deferred for rendering // until the call to CBuffDeferredModeEnd if we have anything near to render here // Now limiting maximum number of updates that can be deferred as have noticed that with redstone clock circuits, it is possible to create // things that need constant updating, so if you stand near them, the render data Never gets updated and the game just keeps going until it runs out of render memory... int count = 0; static const int MAX_DEFERRED_UPDATES = 10; bool shouldContinue = false; do { shouldContinue = minecraft->levelRenderer->updateDirtyChunks(); count++; } while ( shouldContinue && count < MAX_DEFERRED_UPDATES ); // while( minecraft->levelRenderer->updateDirtyChunks() ) // ; RenderManager.CBuffDeferredModeEnd(); // If any renderable tile entities were flagged in this last block of chunk(s) that were udpated, then change their // flags to say that this deferred chunk is over and they are actually safe to be removed now minecraft->levelRenderer->fullyFlagRenderableTileEntitiesToBeRemoved(); // We've got stacks for things that can only safely be deleted whilst this thread isn't updating things - delete those things now EnterCriticalSection(&m_csDeleteStack); for(unsigned int i = 0; i < m_deleteStackByte.size(); i++ ) { delete m_deleteStackByte[i]; } m_deleteStackByte.clear(); for(unsigned int i = 0; i < m_deleteStackSparseLightStorage.size(); i++ ) { delete m_deleteStackSparseLightStorage[i]; } m_deleteStackSparseLightStorage.clear(); for(unsigned int i = 0; i < m_deleteStackCompressedTileStorage.size(); i++ ) { delete m_deleteStackCompressedTileStorage[i]; } m_deleteStackCompressedTileStorage.clear(); for(unsigned int i = 0; i < m_deleteStackSparseDataStorage.size(); i++ ) { delete m_deleteStackSparseDataStorage[i]; } m_deleteStackSparseDataStorage.clear(); LeaveCriticalSection(&m_csDeleteStack); // PIXEndNamedEvent(); AABB::resetPool(); Vec3::resetPool(); IntCache::Reset(); m_updateEvents->Set(eUpdateEventIsFinished); } ShutdownManager::HasFinished(ShutdownManager::eRenderChunkUpdateThread); return 0; } #endif void GameRenderer::EnableUpdateThread() { // #ifdef __PS3__ // MGH - disable the update on PS3 for now // return; // #endif #ifdef MULTITHREAD_ENABLE if( updateRunning) return; app.DebugPrintf("------------------EnableUpdateThread--------------------\n"); updateRunning = true; m_updateEvents->Set(eUpdateCanRun); m_updateEvents->Set(eUpdateEventIsFinished); #endif } void GameRenderer::DisableUpdateThread() { // #ifdef __PS3__ // MGH - disable the update on PS3 for now // return; // #endif #ifdef MULTITHREAD_ENABLE if( !updateRunning) return; app.DebugPrintf("------------------DisableUpdateThread--------------------\n"); updateRunning = false; m_updateEvents->Clear(eUpdateCanRun); m_updateEvents->WaitForSingle(eUpdateEventIsFinished,INFINITE); #endif } void GameRenderer::renderLevel(float a, __int64 until) { // if (updateLightTexture) updateLightTexture(); // 4J - TODO - Java 1.0.1 has this line enabled, should check why - don't want to put it in now in case it breaks split-screen glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); // Is this the primary player? Only do the updating of chunks if it is. This controls the creation of render data for each chunk - all of this we are only // going to do for the primary player, and the other players can just view whatever they have loaded in - we're sharing render data between players. bool updateChunks = ( mc->player == mc->localplayers[ProfileManager.GetPrimaryPad()] ); // if (mc->cameraTargetPlayer == NULL) // 4J - removed condition as we want to update this is mc->player changes for different local players { mc->cameraTargetPlayer = mc->player; } pick(a); shared_ptr cameraEntity = mc->cameraTargetPlayer; LevelRenderer *levelRenderer = mc->levelRenderer; ParticleEngine *particleEngine = mc->particleEngine; double xOff = cameraEntity->xOld + (cameraEntity->x - cameraEntity->xOld) * a; double yOff = cameraEntity->yOld + (cameraEntity->y - cameraEntity->yOld) * a; double zOff = cameraEntity->zOld + (cameraEntity->z - cameraEntity->zOld) * a; for (int i = 0; i < 2; i++) { if (mc->options->anaglyph3d) { GameRenderer::anaglyphPass = i; if (GameRenderer::anaglyphPass == 0) glColorMask(false, true, true, false); else glColorMask(true, false, false, false); } glViewport(0, 0, mc->width, mc->height); setupClearColor(a); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_CULL_FACE); setupCamera(a, i); Camera::prepare(mc->player, mc->player->ThirdPersonView() == 2); Frustum::getFrustum(); if (mc->options->viewDistance < 2) { setupFog(-1, a); levelRenderer->renderSky(a); if(mc->skins->getSelected()->getId() == 1026 ) levelRenderer->renderHaloRing(a); } glEnable(GL_FOG); setupFog(1, a); if (mc->options->ambientOcclusion) { GL11::glShadeModel(GL11::GL_SMOOTH); } PIXBeginNamedEvent(0,"Culling"); MemSect(31); // Culler *frustum = new FrustumCuller(); FrustumCuller frustObj; Culler *frustum = &frustObj; MemSect(0); frustum->prepare(xOff, yOff, zOff); mc->levelRenderer->cull(frustum, a); PIXEndNamedEvent(); #ifndef MULTITHREAD_ENABLE if ( (i == 0) && updateChunks ) // 4J - added updateChunks condition { int PIXPass = 0; PIXBeginNamedEvent(0,"Updating dirty chunks"); do { PIXBeginNamedEvent(0,"Updating dirty chunks pass %d",PIXPass++); bool retval = mc->levelRenderer->updateDirtyChunks(cameraEntity, false); PIXEndNamedEvent(); if( retval ) break; if (until == 0) break; __int64 diff = until - System::nanoTime(); if (diff < 0) break; if (diff > 1000000000) break; } while (true); PIXEndNamedEvent(); } #endif setupFog(0, a); glEnable(GL_FOG); MemSect(31); mc->textures->bindTexture(TN_TERRAIN); // 4J was L"/terrain.png" MemSect(0); Lighting::turnOff(); PIXBeginNamedEvent(0,"Level render"); levelRenderer->render(cameraEntity, 0, a, updateChunks); PIXEndNamedEvent(); GL11::glShadeModel(GL11::GL_FLAT); if (cameraFlip == 0 ) { Lighting::turnOn(); PIXBeginNamedEvent(0,"Entity render"); // 4J - for entities, don't include the "a" factor that interpolates from the old to new position, as the AABBs for the entities are already fully at the new position // This fixes flickering minecarts, and pigs that you are riding on frustum->prepare(cameraEntity->x,cameraEntity->y,cameraEntity->z); // 4J Stu - When rendering entities, in the end if the dragon is hurt or we have a lot of entities we can end up wrapping // our index into the temp Vec3 cache and overwrite the one that was storing the camera position // Fix for #77745 - TU9: Content: Gameplay: Items and mobs not belonging to end world are disappearing when Enderdragon is damaged. Vec3 *cameraPosTemp = cameraEntity->getPos(a); cameraPos->x = cameraPosTemp->x; cameraPos->y = cameraPosTemp->y; cameraPos->z = cameraPosTemp->z; levelRenderer->renderEntities(cameraPos, frustum, a); PIXEndNamedEvent(); PIXBeginNamedEvent(0,"Particle render"); turnOnLightLayer(a); // 4J - brought forward from 1.8.2 particleEngine->renderLit(cameraEntity, a); Lighting::turnOff(); setupFog(0, a); particleEngine->render(cameraEntity, a); PIXEndNamedEvent(); turnOffLightLayer(a); // 4J - brought forward from 1.8.2 shared_ptr player = dynamic_pointer_cast(cameraEntity); if (mc->hitResult != NULL && cameraEntity->isUnderLiquid(Material::water) && player!=NULL) //&& !mc->options.hideGui) { //shared_ptr player = dynamic_pointer_cast(cameraEntity); glDisable(GL_ALPHA_TEST); levelRenderer->renderHit(player, mc->hitResult, 0, player->inventory->getSelected(), a); levelRenderer->renderHitOutline(player, mc->hitResult, 0, player->inventory->getSelected(), a); glEnable(GL_ALPHA_TEST); } } glDisable(GL_BLEND); glEnable(GL_CULL_FACE); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthMask(true); setupFog(0, a); glEnable(GL_BLEND); glDisable(GL_CULL_FACE); MemSect(31); mc->textures->bindTexture(TN_TERRAIN); // 4J was L"/terrain.png" MemSect(0); // 4J - have changed this fancy rendering option to work with our command buffers. The original used to use frame buffer flags to disable // writing to colour when doing the z-only pass, but that value gets obliterated by our command buffers. Using alpha blend function instead // to achieve the same effect. if (true) // (mc->options->fancyGraphics) { if (mc->options->ambientOcclusion) { GL11::glShadeModel(GL11::GL_SMOOTH); } glBlendFunc(GL_ZERO, GL_ONE); PIXBeginNamedEvent(0,"Fancy second pass - writing z"); int visibleWaterChunks = levelRenderer->render(cameraEntity, 1, a, updateChunks); PIXEndNamedEvent(); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); if (visibleWaterChunks > 0) { PIXBeginNamedEvent(0,"Fancy second pass - actual rendering"); levelRenderer->render(cameraEntity, 1, a, updateChunks); // 4J - chanaged, used to be renderSameAsLast but we don't support that anymore PIXEndNamedEvent(); } GL11::glShadeModel(GL11::GL_FLAT); } else { PIXBeginNamedEvent(0,"Second pass level render"); levelRenderer->render(cameraEntity, 1, a, updateChunks); PIXEndNamedEvent(); } glDepthMask(true); glEnable(GL_CULL_FACE); glDisable(GL_BLEND); if (zoom == 1 && (dynamic_pointer_cast(cameraEntity)!=NULL)) //&& !mc->options.hideGui) { if (mc->hitResult != NULL && !cameraEntity->isUnderLiquid(Material::water)) { shared_ptr player = dynamic_pointer_cast(cameraEntity); glDisable(GL_ALPHA_TEST); levelRenderer->renderHit(player, mc->hitResult, 0, player->inventory->getSelected(), a); levelRenderer->renderHitOutline(player, mc->hitResult, 0, player->inventory->getSelected(), a); glEnable(GL_ALPHA_TEST); } } /* 4J - moved rain rendering to after clouds so that it alpha blends onto them properly PIXBeginNamedEvent(0,"Rendering snow and rain"); renderSnowAndRain(a); PIXEndNamedEvent(); glDisable(GL_FOG); */ glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE); levelRenderer->renderDestroyAnimation(Tesselator::getInstance(), dynamic_pointer_cast(cameraEntity), a); glDisable(GL_BLEND); if (mc->options->isCloudsOn()) { glPushMatrix(); setupFog(0, a); glEnable(GL_FOG); PIXBeginNamedEvent(0,"Rendering clouds"); levelRenderer->renderClouds(a); PIXEndNamedEvent(); glDisable(GL_FOG); setupFog(1, a); glPopMatrix(); } // 4J - rain rendering moved here so that it renders after clouds & can blend properly onto them setupFog(0, a); glEnable(GL_FOG); PIXBeginNamedEvent(0,"Rendering snow and rain"); renderSnowAndRain(a); PIXEndNamedEvent(); glDisable(GL_FOG); if (zoom == 1) { glClear(GL_DEPTH_BUFFER_BIT); renderItemInHand(a, i); } if (!mc->options->anaglyph3d) { return; } } glColorMask(true, true, true, false); } void GameRenderer::tickRain() { float rainLevel = mc->level->getRainLevel(1); if (!mc->options->fancyGraphics) rainLevel /= 2; if (rainLevel == 0) return; rainLevel /= ( mc->levelRenderer->activePlayers() + 1 ); random->setSeed(_tick * 312987231l); shared_ptr player = mc->cameraTargetPlayer; Level *level = mc->level; int x0 = Mth::floor(player->x); int y0 = Mth::floor(player->y); int z0 = Mth::floor(player->z); int r = 10; double rainPosX = 0; double rainPosY = 0; double rainPosZ = 0; int rainPosSamples = 0; int rainCount = (int) (100 * rainLevel * rainLevel); if (mc->options->particles == 1) { rainCount >>= 1; } else if (mc->options->particles == 2) { rainCount = 0; } for (int i = 0; i < rainCount; i++) { int x = x0 + random->nextInt(r) - random->nextInt(r); int z = z0 + random->nextInt(r) - random->nextInt(r); int y = level->getTopRainBlock(x, z); int t = level->getTile(x, y - 1, z); Biome *biome = level->getBiome(x,z); if (y <= y0 + r && y >= y0 - r && biome->hasRain() && biome->getTemperature() >= 0.2f) { float xa = random->nextFloat(); float za = random->nextFloat(); if (t > 0) { if (Tile::tiles[t]->material == Material::lava) { mc->particleEngine->add( shared_ptr( new SmokeParticle(level, x + xa, y + 0.1f - Tile::tiles[t]->getShapeY0(), z + za, 0, 0, 0) ) ); } else { if (random->nextInt(++rainPosSamples) == 0) { rainPosX = x + xa; rainPosY = y + 0.1f - Tile::tiles[t]->getShapeY0(); rainPosZ = z + za; } mc->particleEngine->add( shared_ptr( new WaterDropParticle(level, x + xa, y + 0.1f - Tile::tiles[t]->getShapeY0(), z + za) ) ); } } } } if (rainPosSamples > 0 && random->nextInt(3) < rainSoundTime++) { rainSoundTime = 0; MemSect(24); if (rainPosY > player->y + 1 && level->getTopRainBlock(Mth::floor(player->x), Mth::floor(player->z)) > Mth::floor(player->y)) { mc->level->playLocalSound(rainPosX, rainPosY, rainPosZ, eSoundType_AMBIENT_WEATHER_RAIN, 0.1f, 0.5f); } else { mc->level->playLocalSound(rainPosX, rainPosY, rainPosZ, eSoundType_AMBIENT_WEATHER_RAIN, 0.2f, 1.0f); } MemSect(0); } } // 4J - this whole function updated from 1.8.2 void GameRenderer::renderSnowAndRain(float a) { float rainLevel = mc->level->getRainLevel(a); if (rainLevel <= 0) return; // 4J - rain is relatively low poly, but high fill-rate - better to clip it RenderManager.StateSetEnableViewportClipPlanes(true); this->turnOnLightLayer(a); if (rainXa == NULL) { rainXa = new float[32 * 32]; rainZa = new float[32 * 32]; for (int z = 0; z < 32; z++) { for (int x = 0; x < 32; x++) { float xa = x - 16; float za = z - 16; float d = Mth::sqrt(xa * xa + za * za); rainXa[z << 5 | x] = -za / d; rainZa[z << 5 | x] = xa / d; } } } shared_ptr player = mc->cameraTargetPlayer; Level *level = mc->level; int x0 = Mth::floor(player->x); int y0 = Mth::floor(player->y); int z0 = Mth::floor(player->z); Tesselator *t = Tesselator::getInstance(); glDisable(GL_CULL_FACE); glNormal3f(0, 1, 0); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glAlphaFunc(GL_GREATER, 0.01f); MemSect(31); mc->textures->bindTexture(TN_ENVIRONMENT_SNOW); // 4J was L"/environment/snow.png" MemSect(0); double xo = player->xOld + (player->x - player->xOld) * a; double yo = player->yOld + (player->y - player->yOld) * a; double zo = player->zOld + (player->z - player->zOld) * a; int yMin = Mth::floor(yo); int r = 5; // 4J - was if(mc.options.fancyGraphics) r = 10; switch( mc->levelRenderer->activePlayers() ) { case 1: default: r = 9; break; case 2: r = 7; break; case 3: r = 5; break; case 4: r = 5; break; } // 4J - some changes made here to access biome through new interface that caches results in levelchunk flags, as an optimisation int mode = -1; float time = _tick + a; glColor4f(1, 1, 1, 1); for (int x = x0 - r; x <= x0 + r; x++) for (int z = z0 - r; z <= z0 + r; z++) { int rainSlot = (z - z0 + 16) * 32 + (x - x0 + 16); float xa = rainXa[rainSlot] * 0.5f; float za = rainZa[rainSlot] * 0.5f; // 4J - changes here brought forward from 1.8.2 Biome *b = level->getBiome(x, z); if (!b->hasRain() && !b->hasSnow()) continue; int floor = level->getTopRainBlock(x, z); int yy0 = y0 - r; int yy1 = y0 + r; if (yy0 < floor) yy0 = floor; if (yy1 < floor) yy1 = floor; float s = 1; int yl = floor; if (yl < yMin) yl = yMin; if (yy0 != yy1) { random->setSeed((x * x * 3121 + x * 45238971) ^ (z * z * 418711 + z * 13761)); // 4J - changes here brought forward from 1.8.2 float temp = b->getTemperature(); if (level->getBiomeSource()->scaleTemp(temp, floor) >= 0.15f) { if (mode != 0) { if (mode >= 0) t->end(); mode = 0; mc->textures->bindTexture(TN_ENVIRONMENT_RAIN); t->begin(); } float ra = (((_tick + x * x * 3121 + x * 45238971 + z * z * 418711 + z * 13761) & 31) + a) / 32.0f * (3 + random->nextFloat()); double xd = (x + 0.5f) - player->x; double zd = (z + 0.5f) - player->z; float dd = (float) Mth::sqrt(xd * xd + zd * zd) / r; float br = 1; t->offset(-xo * 1, -yo * 1, -zo * 1); t->tex2(level->getLightColor(x, yl, z, 0)); t->color(br, br, br, ((1 - dd * dd) * 0.5f + 0.5f) * rainLevel); t->vertexUV(x - xa + 0.5, yy0, z - za + 0.5, 0 * s, yy0 * s / 4.0f + ra * s); t->vertexUV(x + xa + 0.5, yy0, z + za + 0.5, 1 * s, yy0 * s / 4.0f + ra * s); t->color(br, br, br, 0.0f); // 4J - added to soften the top visible edge of the rain t->vertexUV(x + xa + 0.5, yy1, z + za + 0.5, 1 * s, yy1 * s / 4.0f + ra * s); t->vertexUV(x - xa + 0.5, yy1, z - za + 0.5, 0 * s, yy1 * s / 4.0f + ra * s); t->offset(0, 0, 0); t->end(); } else { if (mode != 1) { if (mode >= 0) t->end(); mode = 1; mc->textures->bindTexture(TN_ENVIRONMENT_SNOW); t->begin(); } float ra = (((_tick) & 511) + a) / 512.0f; float uo = random->nextFloat() + time * 0.01f * (float) random->nextGaussian(); float vo = random->nextFloat() + time * (float) random->nextGaussian() * 0.001f; double xd = (x + 0.5f) - player->x; double zd = (z + 0.5f) - player->z; float dd = (float) sqrt(xd * xd + zd * zd) / r; float br = 1; t->offset(-xo * 1, -yo * 1, -zo * 1); t->tex2((level->getLightColor(x, yl, z, 0) * 3 + 0xf000f0) / 4); t->color(br, br, br, ((1 - dd * dd) * 0.3f + 0.5f) * rainLevel); t->vertexUV(x - xa + 0.5, yy0, z - za + 0.5, 0 * s + uo, yy0 * s / 4.0f + ra * s + vo); t->vertexUV(x + xa + 0.5, yy0, z + za + 0.5, 1 * s + uo, yy0 * s / 4.0f + ra * s + vo); t->vertexUV(x + xa + 0.5, yy1, z + za + 0.5, 1 * s + uo, yy1 * s / 4.0f + ra * s + vo); t->vertexUV(x - xa + 0.5, yy1, z - za + 0.5, 0 * s + uo, yy1 * s / 4.0f + ra * s + vo); t->offset(0, 0, 0); } } } if( mode >= 0 ) t->end(); glEnable(GL_CULL_FACE); glDisable(GL_BLEND); glAlphaFunc(GL_GREATER, 0.1f); this->turnOffLightLayer(a); RenderManager.StateSetEnableViewportClipPlanes(false); } // 4J - added forceScale parameter void GameRenderer::setupGuiScreen(int forceScale /*=-1*/) { ScreenSizeCalculator ssc(mc->options, mc->width, mc->height, forceScale); glClear(GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, (float)ssc.rawWidth, (float)ssc.rawHeight, 0, 1000, 3000); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0, 0, -2000); } void GameRenderer::setupClearColor(float a) { Level *level = mc->level; shared_ptr player = mc->cameraTargetPlayer; float whiteness = 1.0f / (4 - mc->options->viewDistance); whiteness = 1 - (float) pow((double)whiteness, 0.25); Vec3 *skyColor = level->getSkyColor(mc->cameraTargetPlayer, a); float sr = (float) skyColor->x; float sg = (float) skyColor->y; float sb = (float) skyColor->z; Vec3 *fogColor = level->getFogColor(a); fr = (float) fogColor->x; fg = (float) fogColor->y; fb = (float) fogColor->z; if (mc->options->viewDistance < 2) { Vec3 *sunAngle = Mth::sin(level->getSunAngle(a)) > 0 ? Vec3::newTemp(-1, 0, 0) : Vec3::newTemp(1, 0, 0); float d = (float) player->getViewVector(a)->dot(sunAngle); if (d < 0) d = 0; if (d > 0) { float *c = level->dimension->getSunriseColor(level->getTimeOfDay(a), a); if (c != NULL) { d *= c[3]; fr = fr * (1 - d) + c[0] * d; fg = fg * (1 - d) + c[1] * d; fb = fb * (1 - d) + c[2] * d; } } } fr += (sr - fr) * whiteness; fg += (sg - fg) * whiteness; fb += (sb - fb) * whiteness; float rainLevel = level->getRainLevel(a); if (rainLevel > 0) { float ba = 1 - rainLevel * 0.5f; float bb = 1 - rainLevel * 0.4f; fr *= ba; fg *= ba; fb *= bb; } float thunderLevel = level->getThunderLevel(a); if (thunderLevel > 0) { float ba = 1 - thunderLevel * 0.5f; fr *= ba; fg *= ba; fb *= ba; } int t = Camera::getBlockAt(mc->level, player, a); if (isInClouds) { Vec3 *cc = level->getCloudColor(a); fr = (float) cc->x; fg = (float) cc->y; fb = (float) cc->z; } else if (t != 0 && Tile::tiles[t]->material == Material::water) { unsigned int colour = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Under_Water_Clear_Colour ); byte redComponent = ((colour>>16)&0xFF); byte greenComponent = ((colour>>8)&0xFF); byte blueComponent = ((colour)&0xFF); fr = (float)redComponent/256;//0.02f; fg = (float)greenComponent/256;//0.02f; fb = (float)blueComponent/256;//0.2f; } else if (t != 0 && Tile::tiles[t]->material == Material::lava) { unsigned int colour = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Under_Lava_Clear_Colour ); byte redComponent = ((colour>>16)&0xFF); byte greenComponent = ((colour>>8)&0xFF); byte blueComponent = ((colour)&0xFF); fr = (float)redComponent/256;//0.6f; fg = (float)greenComponent/256;//0.1f; fb = (float)blueComponent/256;//0.00f; } float brr = fogBrO + (fogBr - fogBrO) * a; fr *= brr; fg *= brr; fb *= brr; double yy = (player->yOld + (player->y - player->yOld) * a) * level->dimension->getClearColorScale(); // 4J - getClearColorScale brought forward from 1.2.3 if (player->hasEffect(MobEffect::blindness)) { int duration = player->getEffect(MobEffect::blindness)->getDuration(); if (duration < 20) { yy = yy * (1.0f - (float) duration / 20.0f); } else { yy = 0; } } if (yy < 1) { if (yy < 0) yy = 0; yy = yy * yy; fr *= yy; fg *= yy; fb *= yy; } if (player->hasEffect(MobEffect::nightVision)) { float scale = getNightVisionScale(mc->player, a); { float dist = FLT_MAX; // MGH - changed this to avoid divide by zero if ( (fr > 0) && (dist > (1.0f / fr)) ) { dist = (1.0f / fr); } if ( (fg > 0) && (dist > (1.0f / fg)) ) { dist = (1.0f / fg); } if ( (fb > 0) && (dist > (1.0f / fb)) ) { dist = (1.0f / fb); } fr = fr * (1.0f - scale) + (fr * dist) * scale; fg = fg * (1.0f - scale) + (fg * dist) * scale; fb = fb * (1.0f - scale) + (fb * dist) * scale; } } if (mc->options->anaglyph3d) { float frr = (fr * 30 + fg * 59 + fb * 11) / 100; float fgg = (fr * 30 + fg * 70) / (100); float fbb = (fr * 30 + fb * 70) / (100); fr = frr; fg = fgg; fb = fbb; } glClearColor(fr, fg, fb, 0.0f); } void GameRenderer::setupFog(int i, float alpha) { shared_ptr player = mc->cameraTargetPlayer; // 4J - check for creative mode brought forward from 1.2.3 bool creative = false; if (dynamic_pointer_cast(player) ) { creative = (dynamic_pointer_cast(player))->abilities.instabuild; } if (i == 999) { __debugbreak(); // 4J TODO /* glFog(GL_FOG_COLOR, getBuffer(0, 0, 0, 1)); glFogi(GL_FOG_MODE, GL_LINEAR); glFogf(GL_FOG_START, 0); glFogf(GL_FOG_END, 8); if (GLContext.getCapabilities().GL_NV_fog_distance) { glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, NVFogDistance.GL_EYE_RADIAL_NV); } glFogf(GL_FOG_START, 0); */ return; } glFog(GL_FOG_COLOR, getBuffer(fr, fg, fb, 1)); glNormal3f(0, -1, 0); glColor4f(1, 1, 1, 1); int t = Camera::getBlockAt(mc->level, player, alpha); if (player->hasEffect(MobEffect::blindness)) { float distance = 5.0f; int duration = player->getEffect(MobEffect::blindness)->getDuration(); if (duration < 20) { distance = 5.0f + (renderDistance - 5.0f) * (1.0f - (float) duration / 20.0f); } glFogi(GL_FOG_MODE, GL_LINEAR); if (i < 0) { glFogf(GL_FOG_START, 0); glFogf(GL_FOG_END, distance * 0.8f); } else { glFogf(GL_FOG_START, distance * 0.25f); glFogf(GL_FOG_END, distance); } // 4J - TODO investigate implementing this // if (GLContext.getCapabilities().GL_NV_fog_distance) // { // glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, NVFogDistance.GL_EYE_RADIAL_NV); // } } else if (isInClouds) { glFogi(GL_FOG_MODE, GL_EXP); glFogf(GL_FOG_DENSITY, 0.1f); // was 0.06 unsigned int colour = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_In_Cloud_Fog_Colour ); byte redComponent = ((colour>>16)&0xFF); byte greenComponent = ((colour>>8)&0xFF); byte blueComponent = ((colour)&0xFF); float rr = (float)redComponent/256;//1.0f; float gg = (float)greenComponent/256;//1.0f; float bb = (float)blueComponent/256;//1.0f; if (mc->options->anaglyph3d) { float rrr = (rr * 30 + gg * 59 + bb * 11) / 100; float ggg = (rr * 30 + gg * 70) / (100); float bbb = (rr * 30 + bb * 70) / (100); rr = rrr; gg = ggg; bb = bbb; } } else if (t > 0 && Tile::tiles[t]->material == Material::water) { glFogi(GL_FOG_MODE, GL_EXP); if (player->hasEffect(MobEffect::waterBreathing)) { glFogf(GL_FOG_DENSITY, 0.05f); // was 0.06 } else { glFogf(GL_FOG_DENSITY, 0.1f); // was 0.06 } unsigned int colour = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Under_Water_Fog_Colour ); byte redComponent = ((colour>>16)&0xFF); byte greenComponent = ((colour>>8)&0xFF); byte blueComponent = ((colour)&0xFF); float rr = (float)redComponent/256;//0.4f; float gg = (float)greenComponent/256;//0.4f; float bb = (float)blueComponent/256;//0.9f; if (mc->options->anaglyph3d) { float rrr = (rr * 30 + gg * 59 + bb * 11) / 100; float ggg = (rr * 30 + gg * 70) / (100); float bbb = (rr * 30 + bb * 70) / (100); rr = rrr; gg = ggg; bb = bbb; } } else if (t > 0 && Tile::tiles[t]->material == Material::lava) { glFogi(GL_FOG_MODE, GL_EXP); glFogf(GL_FOG_DENSITY, 2.0f); // was 0.06 unsigned int colour = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Under_Lava_Fog_Colour ); byte redComponent = ((colour>>16)&0xFF); byte greenComponent = ((colour>>8)&0xFF); byte blueComponent = ((colour)&0xFF); float rr = (float)redComponent/256;//0.4f; float gg = (float)greenComponent/256;//0.3f; float bb = (float)blueComponent/256;//0.3f; if (mc->options->anaglyph3d) { float rrr = (rr * 30 + gg * 59 + bb * 11) / 100; float ggg = (rr * 30 + gg * 70) / (100); float bbb = (rr * 30 + bb * 70) / (100); rr = rrr; gg = ggg; bb = bbb; } } else { float distance = renderDistance; if (!mc->level->dimension->hasCeiling) { // 4J - test for doing bedrockfog brought forward from 1.2.3 if (mc->level->dimension->hasBedrockFog() && !creative) { double yy = ((player->getLightColor(alpha) & 0xf00000) >> 20) / 16.0 + (player->yOld + (player->y - player->yOld) * alpha + 4) / 32; if (yy < 1) { if (yy < 0) yy = 0; yy = yy * yy; float dist = 100 * (float) yy; if (dist < 5) dist = 5; if (distance > dist) distance = dist; } } } glFogi(GL_FOG_MODE, GL_LINEAR); glFogf(GL_FOG_START, distance * 0.25f); glFogf(GL_FOG_END, distance); if (i < 0) { glFogf(GL_FOG_START, 0); glFogf(GL_FOG_END, distance * 0.8f); } else { glFogf(GL_FOG_START, distance * 0.25f); glFogf(GL_FOG_END, distance); } /* 4J - removed - TODO investigate if (GLContext.getCapabilities().GL_NV_fog_distance) { glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, NVFogDistance.GL_EYE_RADIAL_NV); } */ if (mc->level->dimension->isFoggyAt((int) player->x, (int) player->z)) { glFogf(GL_FOG_START, distance * 0.05f); glFogf(GL_FOG_END, min(distance, 16 * 16 * .75f) * .5f); } } glEnable(GL_COLOR_MATERIAL); glColorMaterial(GL_FRONT, GL_AMBIENT); } FloatBuffer *GameRenderer::getBuffer(float a, float b, float c, float d) { lb->clear(); lb->put(a)->put(b)->put(c)->put(d); lb->flip(); return lb; } int GameRenderer::getFpsCap(int option) { int maxFps = 200; if (option == 1) maxFps = 120; if (option == 2) maxFps = 35; return maxFps; } void GameRenderer::updateAllChunks() { // mc->levelRenderer->updateDirtyChunks(mc->cameraTargetPlayer, true); }