mirror of
https://github.com/4jcraft/4jcraft.git
synced 2026-04-24 16:33:36 +00:00
2109 lines
74 KiB
C++
2109 lines
74 KiB
C++
#include "../Platform/stdafx.h"
|
|
#include "GameRenderer.h"
|
|
#include "EntityRenderers/ItemInHandRenderer.h"
|
|
#include "LevelRenderer.h"
|
|
#include "Frustum.h"
|
|
#include "FrustumCuller.h"
|
|
#include "../Textures/Textures.h"
|
|
#include "Tesselator.h"
|
|
#include "Particles/ParticleEngine.h"
|
|
#include "Particles/SmokeParticle.h"
|
|
#include "Particles/WaterDropParticle.h"
|
|
#include "../GameState/GameMode.h"
|
|
#include "../GameState/CreativeMode.h"
|
|
#include "Lighting.h"
|
|
#include "../GameState/Options.h"
|
|
#include "../Player/MultiPlayerLocalPlayer.h"
|
|
#include "Particles/GuiParticles.h"
|
|
#include "../Level/MultiPlayerLevel.h"
|
|
#include "Chunk.h"
|
|
#include "../../Minecraft.World/Headers/net.minecraft.world.entity.h"
|
|
#include "../../Minecraft.World/Headers/net.minecraft.world.entity.player.h"
|
|
#include "../../Minecraft.World/Headers/net.minecraft.world.item.enchantment.h"
|
|
#include "../../Minecraft.World/Headers/net.minecraft.world.level.h"
|
|
#include "../../Minecraft.World/Headers/net.minecraft.world.level.material.h"
|
|
#include "../../Minecraft.World/Headers/net.minecraft.world.level.tile.h"
|
|
#include "../../Minecraft.World/Headers/net.minecraft.world.level.chunk.h"
|
|
#include "../../Minecraft.World/Headers/net.minecraft.world.level.biome.h"
|
|
#include "../../Minecraft.World/Headers/net.minecraft.world.level.dimension.h"
|
|
#include "../../Minecraft.World/Headers/net.minecraft.world.phys.h"
|
|
#include "../../Minecraft.World/Platform/System.h"
|
|
#include "../../Minecraft.World/IO/Streams/FloatBuffer.h"
|
|
#include "../../Minecraft.World/Util/ThreadName.h"
|
|
#include "../../Minecraft.World/Level/Storage/SparseLightStorage.h"
|
|
#include "../../Minecraft.World/Level/Storage/CompressedTileStorage.h"
|
|
#include "../../Minecraft.World/Level/Storage/SparseDataStorage.h"
|
|
#include "../../Minecraft.World/Util/JavaMath.h"
|
|
#include "../../Minecraft.World/Util/Facing.h"
|
|
#include "../../Minecraft.World/Entities/MobEffect.h"
|
|
#include "../../Minecraft.World/Util/IntCache.h"
|
|
#include "../../Minecraft.World/Util/SmoothFloat.h"
|
|
#include "../../Minecraft.World/Entities/MobEffectInstance.h"
|
|
#include "../../Minecraft.World/Items/Item.h"
|
|
#include "Camera.h"
|
|
#include "../../Minecraft.World/Util/SoundTypes.h"
|
|
#include "Models/HumanoidModel.h"
|
|
#include "../../Minecraft.World/Items/Item.h"
|
|
#include "../../Minecraft.World/IO/Streams/Compression.h"
|
|
#include "PS3/PS3Extras/ShutdownManager.h"
|
|
#include "../UI/BossMobGuiInfo.h"
|
|
|
|
#include "../Textures/Packs/TexturePackRepository.h"
|
|
#include "../Textures/Packs/TexturePack.h"
|
|
#include "../Textures/TextureAtlas.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;
|
|
std::vector<uint8_t*> GameRenderer::m_deleteStackByte;
|
|
std::vector<SparseLightStorage*> GameRenderer::m_deleteStackSparseLightStorage;
|
|
std::vector<CompressedTileStorage*>
|
|
GameRenderer::m_deleteStackCompressedTileStorage;
|
|
std::vector<SparseDataStorage*> GameRenderer::m_deleteStackSparseDataStorage;
|
|
#endif
|
|
CRITICAL_SECTION GameRenderer::m_csDeleteStack;
|
|
|
|
ResourceLocation GameRenderer::RAIN_LOCATION =
|
|
ResourceLocation(TN_ENVIRONMENT_RAIN);
|
|
ResourceLocation GameRenderer::SNOW_LOCATION =
|
|
ResourceLocation(TN_ENVIRONMENT_SNOW);
|
|
|
|
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;
|
|
|
|
darkenWorldAmount = 0.0f;
|
|
darkenWorldAmountO = 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; i < NUM_LIGHT_TEXTURES; i++)
|
|
lightPixels[i] = intArray(
|
|
(int*)RenderManager.allocIOMem(16 * 16 * sizeof(int)), 16 * 16);
|
|
#else
|
|
for (int i = 0; i < NUM_LIGHT_TEXTURES; i++)
|
|
lightPixels[i] = intArray(16 * 16);
|
|
#endif
|
|
|
|
#ifdef MULTITHREAD_ENABLE
|
|
m_updateEvents = new C4JThread::EventArray(
|
|
eUpdateEventCount, C4JThread::EventArray::e_modeAutoClear);
|
|
m_updateEvents->Set(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 (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 = std::dynamic_pointer_cast<Mob>(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();
|
|
|
|
darkenWorldAmountO = darkenWorldAmount;
|
|
if (BossMobGuiInfo::darkenWorld) {
|
|
darkenWorldAmount +=
|
|
1.0f / ((float)SharedConstants::TICKS_PER_SECOND * 1);
|
|
if (darkenWorldAmount > 1) {
|
|
darkenWorldAmount = 1;
|
|
}
|
|
BossMobGuiInfo::darkenWorld = false;
|
|
} else if (darkenWorldAmount > 0) {
|
|
darkenWorldAmount -=
|
|
1.0f / ((float)SharedConstants::TICKS_PER_SECOND * 4);
|
|
}
|
|
|
|
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;
|
|
|
|
mc->crosshairPickMob = nullptr;
|
|
|
|
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;
|
|
std::vector<std::shared_ptr<Entity> >* 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++) {
|
|
std::shared_ptr<Entity> 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 (e == mc->cameraTargetPlayer->riding != NULL) {
|
|
if (nearest == 0) {
|
|
hovered = e;
|
|
}
|
|
} else {
|
|
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);
|
|
if (hovered->instanceof(eTYPE_LIVINGENTITY)) {
|
|
mc->crosshairPickMob =
|
|
std::dynamic_pointer_cast<LivingEntity>(hovered);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void GameRenderer::SetFovVal(float fov) { m_fov = fov; }
|
|
|
|
float GameRenderer::GetFovVal() { return m_fov; }
|
|
|
|
void GameRenderer::tickFov() {
|
|
std::shared_ptr<LocalPlayer> player =
|
|
std::dynamic_pointer_cast<LocalPlayer>(mc->cameraTargetPlayer);
|
|
|
|
int playerIdx = player ? player->GetXboxPad() : 0;
|
|
tFov[playerIdx] = player->getFieldOfViewModifier();
|
|
|
|
oFov[playerIdx] = fov[playerIdx];
|
|
fov[playerIdx] += (tFov[playerIdx] - fov[playerIdx]) * 0.5f;
|
|
|
|
if (fov[playerIdx] > 1.5f) fov[playerIdx] = 1.5f;
|
|
if (fov[playerIdx] < 0.1f) fov[playerIdx] = 0.1f;
|
|
}
|
|
|
|
float GameRenderer::getFov(float a, bool applyEffects) {
|
|
if (cameraFlip > 0) return 90;
|
|
|
|
std::shared_ptr<LocalPlayer> player =
|
|
std::dynamic_pointer_cast<LocalPlayer>(mc->cameraTargetPlayer);
|
|
int playerIdx = player ? player->GetXboxPad() : 0;
|
|
float fov = m_fov; // 70;
|
|
if (applyEffects) {
|
|
fov += mc->options->fov * 40;
|
|
fov *= oFov[playerIdx] + (this->fov[playerIdx] - 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) {
|
|
std::shared_ptr<LivingEntity> 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) {
|
|
if (!mc->cameraTargetPlayer->instanceof(eTYPE_LIVINGENTITY)) return;
|
|
|
|
std::shared_ptr<Player> player =
|
|
std::dynamic_pointer_cast<Player>(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) {
|
|
std::shared_ptr<LivingEntity> player = mc->cameraTargetPlayer;
|
|
std::shared_ptr<LocalPlayer> localplayer =
|
|
std::dynamic_pointer_cast<LocalPlayer>(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 playerYRot =
|
|
player->yRotO + (player->yRot - player->yRotO) * a;
|
|
float playerXRot =
|
|
player->xRotO + (player->xRot - player->xRotO) * a;
|
|
float yRot = playerYRot;
|
|
float xRot = playerXRot;
|
|
|
|
// Thirdperson view values are now 0 for disabled, 1 for original
|
|
// mode, 2 for reversed.
|
|
if (localplayer->ThirdPersonView() == 2) {
|
|
// Reverse x 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
|
|
xRot += 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;
|
|
}
|
|
}
|
|
|
|
if (localplayer->ThirdPersonView() == 2) {
|
|
glRotatef(180, 0, 1, 0);
|
|
}
|
|
|
|
glRotatef(playerXRot - xRot, 1, 0, 0);
|
|
glRotatef(playerYRot - yRot, 0, 1, 0);
|
|
glTranslatef(0, 0, (float)-cameraDist);
|
|
glRotatef(yRot - playerYRot, 0, 1, 0);
|
|
glRotatef(xRot - playerXRot, 1, 0, 0);
|
|
}
|
|
} else {
|
|
glTranslatef(0, 0, -0.1f);
|
|
}
|
|
|
|
if (!mc->options->fixedCamera) {
|
|
glRotatef(player->xRotO + (player->xRot - player->xRotO) * a, 1, 0, 0);
|
|
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) {
|
|
zoom = zoom;
|
|
zoom_x = xa;
|
|
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 << HumanoidModel::eAnim_NoLegAnim)) != 0;
|
|
bool bNoBobbingAnim = (mc->player->getAnimOverrideBitmask() &
|
|
(1 << HumanoidModel::eAnim_NoBobbing)) != 0;
|
|
|
|
if (app.GetGameSettings(mc->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;
|
|
|
|
// 4J-JEV: I'm fairly confident this method would crash if the cameratarget
|
|
// isnt a local player anyway, but oh well.
|
|
std::shared_ptr<LocalPlayer> localplayer =
|
|
mc->cameraTargetPlayer->instanceof(eTYPE_LOCALPLAYER)
|
|
? std::dynamic_pointer_cast<LocalPlayer>(mc->cameraTargetPlayer)
|
|
: nullptr;
|
|
|
|
bool renderHand = true;
|
|
|
|
// 4J-PB - to turn off the hand for screenshots, but not when the item held
|
|
// is a map
|
|
if (localplayer != NULL) {
|
|
std::shared_ptr<ItemInstance> item =
|
|
localplayer->inventory->getSelected();
|
|
if (!(item && item->getItem()->id == Item::map_Id) &&
|
|
app.GetGameSettings(localplayer->GetXboxPad(),
|
|
eGameSetting_DisplayHand) == 0)
|
|
renderHand = false;
|
|
}
|
|
|
|
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 << HumanoidModel::eAnim_NoLegAnim) |
|
|
(1 << HumanoidModel::eAnim_NoBobbing))) != 0;
|
|
if (app.GetGameSettings(localplayer->GetXboxPad(), eGameSetting_ViewBob) &&
|
|
!localplayer->abilities.flying && !bNoLegAnim)
|
|
bobView(a);
|
|
|
|
// 4J: Skip hand rendering if render hand is off
|
|
if (renderHand) {
|
|
// 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
|
|
// update light texture
|
|
// todo: check implementation of getLightTexture.
|
|
RenderManager.TextureBindVertex(
|
|
getLightTexture(mc->player->GetXboxPad(), mc->level));
|
|
#if 0
|
|
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);
|
|
#endif
|
|
}
|
|
|
|
// 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
|
|
std::shared_ptr<MultiplayerLocalPlayer> 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->skyFlashTime > 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 (darkenWorldAmount > 0) {
|
|
float amount = darkenWorldAmountO +
|
|
(darkenWorldAmount - darkenWorldAmountO) * a;
|
|
_r = _r * (1.0f - amount) + (_r * .7f) * amount;
|
|
_g = _g * (1.0f - amount) + (_g * .6f) * amount;
|
|
_b = _b * (1.0f - amount) + (_b * .6f) * amount;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (_r > 1) _r = 1;
|
|
if (_g > 1) _g = 1;
|
|
if (_b > 1) _b = 1;
|
|
|
|
float brightness = 0.0f; // 4J - TODO - was mc->options->gamma;
|
|
|
|
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 alpha = 255;
|
|
int r = (int)(_r * 255);
|
|
int g = (int)(_g * 255);
|
|
int b = (int)(_b * 255);
|
|
|
|
#if (defined _DURANGO || defined _WIN64 || __PSVITA__ || __linux__)
|
|
lightPixels[j][i] = alpha << 24 | b << 16 | g << 8 | r;
|
|
#elif (defined _XBOX || defined __ORBIS__)
|
|
lightPixels[j][i] = alpha << 24 | r << 16 | g << 8 | b;
|
|
#else
|
|
lightPixels[j][i] = r << 24 | g << 16 | b << 8 | alpha;
|
|
#endif
|
|
}
|
|
|
|
mc->textures->replaceTextureDirect(lightPixels[j], 16, 16,
|
|
getLightTexture(j, level));
|
|
// lightTexture->upload(); // 4J: not relevant
|
|
|
|
//_updateLightTexture = false;
|
|
}
|
|
}
|
|
|
|
float GameRenderer::getNightVisionScale(std::shared_ptr<Player> player,
|
|
float a) {
|
|
int duration = player->getEffect(MobEffect::nightVision)->getDuration();
|
|
if (duration > (SharedConstants::TICKS_PER_SECOND * 10)) {
|
|
return 1.0f;
|
|
} else {
|
|
float flash = std::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 && focused) {
|
|
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 (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(uint8_t* 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(void* 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_t 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);
|
|
|
|
std::shared_ptr<LivingEntity> 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);
|
|
}
|
|
// 4jcraft: needs to be enabled for proper transparent texturing on low
|
|
// render dists this was done in renderSky() for the far and normal
|
|
// dists but was missing here, UPDATE: Also needed for the nether, so
|
|
// just enable it unconditionally
|
|
glEnable(GL_ALPHA_TEST);
|
|
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_t diff = until - System::nanoTime();
|
|
if (diff < 0) break;
|
|
if (diff > 1000000000) break;
|
|
} while (true);
|
|
PIXEndNamedEvent();
|
|
}
|
|
#endif
|
|
|
|
if (cameraEntity->y < Level::genDepth) {
|
|
prepareAndRenderClouds(levelRenderer, a);
|
|
}
|
|
Frustum::getFrustum(); // 4J added - re-calculate frustum as rendering
|
|
// the clouds does a scale & recalculates one
|
|
// that isn't any good for the rest of the level
|
|
// rendering
|
|
|
|
setupFog(0, a);
|
|
glEnable(GL_FOG);
|
|
MemSect(31);
|
|
mc->textures->bindTexture(
|
|
&TextureAtlas::LOCATION_BLOCKS); // 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);
|
|
#ifdef __PSVITA__
|
|
// AP - make sure we're using the Alpha cut out effect for particles
|
|
glEnable(GL_ALPHA_TEST);
|
|
#endif
|
|
PIXEndNamedEvent();
|
|
PIXBeginNamedEvent(0, "Particle render");
|
|
turnOnLightLayer(a); // 4J - brought forward from 1.8.2
|
|
particleEngine->renderLit(cameraEntity, a,
|
|
ParticleEngine::OPAQUE_LIST);
|
|
Lighting::turnOff();
|
|
setupFog(0, a);
|
|
particleEngine->render(cameraEntity, a,
|
|
ParticleEngine::OPAQUE_LIST);
|
|
PIXEndNamedEvent();
|
|
turnOffLightLayer(a); // 4J - brought forward from 1.8.2
|
|
|
|
if ((mc->hitResult != NULL) &&
|
|
cameraEntity->isUnderLiquid(Material::water) &&
|
|
cameraEntity->instanceof(
|
|
eTYPE_PLAYER)) //&& !mc->options.hideGui)
|
|
{
|
|
std::shared_ptr<Player> player =
|
|
std::dynamic_pointer_cast<Player>(cameraEntity);
|
|
glDisable(GL_ALPHA_TEST);
|
|
levelRenderer->renderHit(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(
|
|
&TextureAtlas::LOCATION_BLOCKS); // 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();
|
|
}
|
|
|
|
// 4J - added - have split out translucent particle rendering so that it
|
|
// happens after the water is rendered, primarily for fireworks
|
|
PIXBeginNamedEvent(0, "Particle render (translucent)");
|
|
Lighting::turnOn();
|
|
turnOnLightLayer(a); // 4J - brought forward from 1.8.2
|
|
particleEngine->renderLit(cameraEntity, a,
|
|
ParticleEngine::TRANSLUCENT_LIST);
|
|
Lighting::turnOff();
|
|
setupFog(0, a);
|
|
particleEngine->render(cameraEntity, a,
|
|
ParticleEngine::TRANSLUCENT_LIST);
|
|
PIXEndNamedEvent();
|
|
turnOffLightLayer(a); // 4J - brought forward from 1.8.2
|
|
////////////////////////// End of 4J added section
|
|
|
|
glDepthMask(true);
|
|
glEnable(GL_CULL_FACE);
|
|
glDisable(GL_BLEND);
|
|
|
|
if ((zoom == 1) &&
|
|
cameraEntity->instanceof(eTYPE_PLAYER)) //&& !mc->options.hideGui)
|
|
{
|
|
if (mc->hitResult != NULL &&
|
|
!cameraEntity->isUnderLiquid(Material::water)) {
|
|
std::shared_ptr<Player> player =
|
|
std::dynamic_pointer_cast<Player>(cameraEntity);
|
|
glDisable(GL_ALPHA_TEST);
|
|
levelRenderer->renderHitOutline(player, mc->hitResult, 0, 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(),
|
|
std::dynamic_pointer_cast<Player>(cameraEntity), a);
|
|
glDisable(GL_BLEND);
|
|
|
|
if (cameraEntity->y >= Level::genDepth) {
|
|
prepareAndRenderClouds(levelRenderer, a);
|
|
}
|
|
|
|
// 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::prepareAndRenderClouds(LevelRenderer* levelRenderer,
|
|
float a) {
|
|
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();
|
|
}
|
|
}
|
|
|
|
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);
|
|
std::shared_ptr<LivingEntity> 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(
|
|
std::shared_ptr<SmokeParticle>(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(std::shared_ptr<WaterDropParticle>(
|
|
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);
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<LivingEntity> 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(
|
|
&SNOW_LOCATION); // 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(&RAIN_LOCATION);
|
|
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);
|
|
#ifdef __PSVITA__
|
|
// AP - this will set up the 4 vertices in half the time
|
|
float Alpha = ((1 - dd * dd) * 0.5f + 0.5f) * rainLevel;
|
|
int tex2 =
|
|
(level->getLightColor(x, yl, z, 0) * 3 + 0xf000f0) / 4;
|
|
t->tileRainQuad(
|
|
x - xa + 0.5, yy0, z - za + 0.5, 0 * s,
|
|
yy0 * s / 4.0f + ra * s, x + xa + 0.5, yy0,
|
|
z + za + 0.5, 1 * s, yy0 * s / 4.0f + ra * s,
|
|
x + xa + 0.5, yy1, z + za + 0.5, 1 * s,
|
|
yy1 * s / 4.0f + ra * s, x - xa + 0.5, yy1,
|
|
z - za + 0.5, 0 * s, yy1 * s / 4.0f + ra * s, br, br,
|
|
br, Alpha, br, br, br, 0, tex2);
|
|
#else
|
|
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);
|
|
// 4jcraft: this color call made rain invisible
|
|
// 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);
|
|
#endif
|
|
t->offset(0, 0, 0);
|
|
t->end();
|
|
} else {
|
|
if (mode != 1) {
|
|
if (mode >= 0) t->end();
|
|
mode = 1;
|
|
mc->textures->bindTexture(&SNOW_LOCATION);
|
|
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);
|
|
#ifdef __PSVITA__
|
|
// AP - this will set up the 4 vertices in half the time
|
|
float Alpha = ((1 - dd * dd) * 0.3f + 0.5f) * rainLevel;
|
|
int tex2 =
|
|
(level->getLightColor(x, yl, z, 0) * 3 + 0xf000f0) / 4;
|
|
t->tileRainQuad(
|
|
x - xa + 0.5, yy0, z - za + 0.5, 0 * s + uo,
|
|
yy0 * s / 4.0f + ra * s + vo, x + xa + 0.5, yy0,
|
|
z + za + 0.5, 1 * s + uo, yy0 * s / 4.0f + ra * s + vo,
|
|
x + xa + 0.5, yy1, z + za + 0.5, 1 * s + uo,
|
|
yy1 * s / 4.0f + ra * s + vo, x - xa + 0.5, yy1,
|
|
z - za + 0.5, 0 * s + uo, yy1 * s / 4.0f + ra * s + vo,
|
|
br, br, br, Alpha, br, br, br, Alpha, tex2);
|
|
#else
|
|
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);
|
|
#endif
|
|
t->offset(0, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mode >= 0) t->end();
|
|
glEnable(GL_CULL_FACE);
|
|
glDisable(GL_BLEND);
|
|
glAlphaFunc(GL_GREATER, 0.1f);
|
|
turnOffLightLayer(a);
|
|
|
|
RenderManager.StateSetEnableViewportClipPlanes(false);
|
|
}
|
|
|
|
// 4J - added forceScale parameter
|
|
void GameRenderer::setupGuiScreen(int forceScale /*=-1*/) {
|
|
int fbw, fbh;
|
|
RenderManager.GetFramebufferSize(fbw, fbh);
|
|
|
|
#ifdef _ENABLEIGGY
|
|
// 4jcraft: use actual framebuffer dimensions instead of mc->width/height
|
|
// to ensure GUI scales correctly after a window resize.
|
|
ScreenSizeCalculator ssc(mc->options, fbw, fbh, forceScale);
|
|
#else
|
|
ScreenSizeCalculator ssc(mc->options, mc->width, mc->height, forceScale);
|
|
#endif
|
|
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;
|
|
std::shared_ptr<LivingEntity> 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) {
|
|
float clearness = EnchantmentHelper::getOxygenBonus(player) * 0.2f;
|
|
|
|
unsigned int colour =
|
|
Minecraft::GetInstance()->getColourTable()->getColor(
|
|
eMinecraftColour_Under_Water_Clear_Colour);
|
|
uint8_t redComponent = ((colour >> 16) & 0xFF);
|
|
uint8_t greenComponent = ((colour >> 8) & 0xFF);
|
|
uint8_t blueComponent = ((colour) & 0xFF);
|
|
|
|
fr = (float)redComponent / 256 + clearness; // 0.02f;
|
|
fg = (float)greenComponent / 256 + clearness; // 0.02f;
|
|
fb = (float)blueComponent / 256 + clearness; // 0.2f;
|
|
} else if (t != 0 && Tile::tiles[t]->material == Material::lava) {
|
|
unsigned int colour =
|
|
Minecraft::GetInstance()->getColourTable()->getColor(
|
|
eMinecraftColour_Under_Lava_Clear_Colour);
|
|
uint8_t redComponent = ((colour >> 16) & 0xFF);
|
|
uint8_t greenComponent = ((colour >> 8) & 0xFF);
|
|
uint8_t 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 (darkenWorldAmount > 0) {
|
|
float amount =
|
|
darkenWorldAmountO + (darkenWorldAmount - darkenWorldAmountO) * a;
|
|
fr = fr * (1.0f - amount) + (fr * .7f) * amount;
|
|
fg = fg * (1.0f - amount) + (fg * .6f) * amount;
|
|
fb = fb * (1.0f - amount) + (fb * .6f) * amount;
|
|
}
|
|
|
|
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) {
|
|
std::shared_ptr<LivingEntity> player = mc->cameraTargetPlayer;
|
|
|
|
// 4J - check for creative mode brought forward from 1.2.3
|
|
bool creative = false;
|
|
if (player->instanceof(eTYPE_PLAYER)) {
|
|
creative =
|
|
(std::dynamic_pointer_cast<Player>(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
|
|
} 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 - (EnchantmentHelper::getOxygenBonus(player) *
|
|
0.03f)); // was 0.06
|
|
}
|
|
} 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
|
|
} 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, std::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);
|
|
}
|