Files
minecraft-pe-0.6.1/src/client/renderer/LevelRenderer.cpp
2026-03-02 22:04:18 +03:00

1296 lines
38 KiB
C++
Executable File

#include "LevelRenderer.h"
#include "DirtyChunkSorter.h"
#include "DistanceChunkSorter.h"
#include "Chunk.h"
#include "TileRenderer.h"
#include "../Minecraft.h"
#include "../../util/Mth.h"
#include "../../world/entity/player/Player.h"
#include "../../world/level/tile/LevelEvent.h"
#include "../../world/level/tile/LeafTile.h"
#include "../../client/particle/ParticleEngine.h"
#include "../../client/particle/ParticleInclude.h"
#include "../sound/SoundEngine.h"
#include "culling/Culler.h"
#include "entity/EntityRenderDispatcher.h"
#include "../model/HumanoidModel.h"
#include "GameRenderer.h"
#include "../../AppPlatform.h"
#include "../../util/PerfTimer.h"
#include "Textures.h"
#include "tileentity/TileEntityRenderDispatcher.h"
#include "../particle/BreakingItemParticle.h"
#include "../../client/player/LocalPlayer.h"
#ifdef GFX_SMALLER_CHUNKS
/* static */ const int LevelRenderer::CHUNK_SIZE = 8;
#else
/* static */ const int LevelRenderer::CHUNK_SIZE = 16;
#endif
LevelRenderer::LevelRenderer( Minecraft* mc)
: mc(mc),
textures(mc->textures),
level(NULL),
cullStep(0),
chunkLists(0),
xChunks(0), yChunks(0), zChunks(0),
chunks(NULL),
sortedChunks(NULL),
xMinChunk(0), yMinChunk(0), zMinChunk(0),
xMaxChunk(0), yMaxChunk(0), zMaxChunk(0),
lastViewDistance(-1),
noEntityRenderFrames(2),
totalEntities(0),
renderedEntities(0),
culledEntities(0),
occlusionCheck(false),
totalChunks(0), offscreenChunks(0), renderedChunks(0), occludedChunks(0), emptyChunks(0),
chunkFixOffs(0),
xOld(-9999), yOld(-9999), zOld(-9999),
ticks(0),
skyList(0), starList(0), darkList(0),
tileRenderer(NULL),
destroyProgress(0)
{
#ifdef OPENGL_ES
int maxChunksWidth = 2 * LEVEL_WIDTH / CHUNK_SIZE + 1;
numListsOrBuffers = maxChunksWidth * maxChunksWidth * (128/CHUNK_SIZE) * 3;
chunkBuffers = new GLuint[numListsOrBuffers];
glGenBuffers2(numListsOrBuffers, chunkBuffers);
LOGI("numBuffers: %d\n", numListsOrBuffers);
//for (int i = 0; i < numListsOrBuffers; ++i) printf("bufId %d: %d\t", i, chunkBuffers[i]);
glGenBuffers2(1, &skyBuffer);
generateSky();
#else
int maxChunksWidth = 1024 / CHUNK_SIZE;
numListsOrBuffers = maxChunksWidth * maxChunksWidth * maxChunksWidth * 3;
chunkLists = glGenLists(numListsOrBuffers);
#endif
}
LevelRenderer::~LevelRenderer()
{
delete tileRenderer;
tileRenderer = NULL;
deleteChunks();
#ifdef OPENGL_ES
glDeleteBuffers(numListsOrBuffers, chunkBuffers);
glDeleteBuffers(1, &skyBuffer);
delete[] chunkBuffers;
#else
glDeleteLists(numListsOrBuffers, chunkLists);
#endif
}
void LevelRenderer::generateSky() {
Tesselator& t = Tesselator::instance;
float yy;
int s = 128;
int d = (256 / s) + 2;
yy = (float) (16);
t.begin();
skyVertexCount = 0;
for (int xx = -s * d; xx <= s * d; xx += s) {
for (int zz = -s * d; zz <= s * d; zz += s) {
t.vertex((float) xx + 0, yy, (float) zz + 0);
t.vertex((float)(xx + s), yy, (float) zz + 0);
t.vertex((float)(xx + s), yy, (float)(zz + s));
t.vertex((float) xx + 0, yy, (float)(zz + s));
//LOGI("x, z: %d, %d\n", xx, zz);
skyVertexCount += 4;
}
}
t.end(true, skyBuffer);
//LOGI("skyvertexcount: %d\n", skyVertexCount);
//glEndList();
}
void LevelRenderer::setLevel( Level* level )
{
if (this->level != NULL) {
this->level->removeListener(this);
}
xOld = -9999;
yOld = -9999;
zOld = -9999;
EntityRenderDispatcher::getInstance()->setLevel(level);
EntityRenderDispatcher::getInstance()->setMinecraft(mc);
this->level = level;
delete tileRenderer;
tileRenderer = new TileRenderer(level);
if (level != NULL) {
level->addListener(this);
allChanged();
}
}
void LevelRenderer::allChanged()
{
deleteChunks();
Tile::leaves->setFancy(mc->options.fancyGraphics);
Tile::leaves_carried->setFancy(mc->options.fancyGraphics);
lastViewDistance = mc->options.viewDistance;
int dist = (512 >> 3) << (3 - lastViewDistance);
if (lastViewDistance <= 2 && mc->isPowerVR())
dist = (int)((float)dist * 0.8f);
LOGI("last: %d, power: %d\n", lastViewDistance, mc->isPowerVR());
#if defined(RPI)
dist *= 0.6f;
#endif
if (dist > 400) dist = 400;
/*
* if (Minecraft.FLYBY_MODE) { dist = 512 - CHUNK_SIZE * 2; }
*/
xChunks = (dist / LevelRenderer::CHUNK_SIZE) + 1;
yChunks = (128 / LevelRenderer::CHUNK_SIZE);
zChunks = (dist / LevelRenderer::CHUNK_SIZE) + 1;
chunksLength = xChunks * yChunks * zChunks;
LOGI("chunksLength: %d. Distance: %d\n", chunksLength, dist);
chunks = new Chunk*[chunksLength];
sortedChunks = new Chunk*[chunksLength];
int id = 0;
int count = 0;
xMinChunk = 0;
yMinChunk = 0;
zMinChunk = 0;
xMaxChunk = xChunks;
yMaxChunk = yChunks;
zMaxChunk = zChunks;
dirtyChunks.clear();
//renderableTileEntities.clear();
for (int x = 0; x < xChunks; x++) {
for (int y = 0; y < yChunks; y++) {
for (int z = 0; z < zChunks; z++) {
const int c = getLinearCoord(x, y, z);
Chunk* chunk = new Chunk(level, x * CHUNK_SIZE, y * CHUNK_SIZE, z * CHUNK_SIZE, CHUNK_SIZE, chunkLists + id, &chunkBuffers[id]);
if (occlusionCheck) {
chunk->occlusion_id = 0;//occlusionCheckIds.get(count);
}
chunk->occlusion_querying = false;
chunk->occlusion_visible = true;
chunk->visible = true;
chunk->id = count++;
chunk->setDirty();
chunks[c] = chunk;
sortedChunks[c] = chunk;
dirtyChunks.push_back(chunk);
id += 3;
}
}
}
if (level != NULL) {
Entity* player = mc->cameraTargetPlayer;
if (player != NULL) {
this->resortChunks(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z));
DistanceChunkSorter distanceSorter(player);
std::sort(sortedChunks, sortedChunks + chunksLength, distanceSorter);
}
}
noEntityRenderFrames = 2;
}
void LevelRenderer::deleteChunks()
{
for (int z = 0; z < zChunks; ++z)
for (int y = 0; y < yChunks; ++y)
for (int x = 0; x < xChunks; ++x) {
int c = getLinearCoord(x, y, z);
delete chunks[c];
}
delete[] chunks;
chunks = NULL;
delete[] sortedChunks;
sortedChunks = NULL;
}
void LevelRenderer::resortChunks( int xc, int yc, int zc )
{
xc -= CHUNK_SIZE / 2;
//yc -= CHUNK_SIZE / 2;
zc -= CHUNK_SIZE / 2;
xMinChunk = INT_MAX;
yMinChunk = INT_MAX;
zMinChunk = INT_MAX;
xMaxChunk = INT_MIN;
yMaxChunk = INT_MIN;
zMaxChunk = INT_MIN;
int dirty = 0;
int s2 = xChunks * CHUNK_SIZE;
int s1 = s2 / 2;
for (int x = 0; x < xChunks; x++) {
int xx = x * CHUNK_SIZE;
int xOff = (xx + s1 - xc);
if (xOff < 0) xOff -= (s2 - 1);
xOff /= s2;
xx -= xOff * s2;
if (xx < xMinChunk) xMinChunk = xx;
if (xx > xMaxChunk) xMaxChunk = xx;
for (int z = 0; z < zChunks; z++) {
int zz = z * CHUNK_SIZE;
int zOff = (zz + s1 - zc);
if (zOff < 0) zOff -= (s2 - 1);
zOff /= s2;
zz -= zOff * s2;
if (zz < zMinChunk) zMinChunk = zz;
if (zz > zMaxChunk) zMaxChunk = zz;
for (int y = 0; y < yChunks; y++) {
int yy = y * CHUNK_SIZE;
if (yy < yMinChunk) yMinChunk = yy;
if (yy > yMaxChunk) yMaxChunk = yy;
Chunk* chunk = chunks[(z * yChunks + y) * xChunks + x];
bool wasDirty = chunk->isDirty();
chunk->setPos(xx, yy, zz);
if (!wasDirty && chunk->isDirty()) {
dirtyChunks.push_back(chunk);
++dirty;
}
}
}
}
}
int LevelRenderer::render( Mob* player, int layer, float alpha )
{
if (mc->options.viewDistance != lastViewDistance) {
allChanged();
}
TIMER_PUSH("sortchunks");
for (int i = 0; i < 10; i++) {
chunkFixOffs = (chunkFixOffs + 1) % chunksLength;
Chunk* c = chunks[chunkFixOffs];
if (c->isDirty() && std::find(dirtyChunks.begin(), dirtyChunks.end(), c) == dirtyChunks.end()) {
dirtyChunks.push_back(c);
}
}
if (layer == 0) {
totalChunks = 0;
offscreenChunks = 0;
occludedChunks = 0;
renderedChunks = 0;
emptyChunks = 0;
}
float xOff = player->xOld + (player->x - player->xOld) * alpha;
float yOff = player->yOld + (player->y - player->yOld) * alpha;
float zOff = player->zOld + (player->z - player->zOld) * alpha;
float xd = player->x - xOld;
float yd = player->y - yOld;
float zd = player->z - zOld;
if (xd * xd + yd * yd + zd * zd > 4 * 4) {
xOld = player->x;
yOld = player->y;
zOld = player->z;
resortChunks(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z));
DistanceChunkSorter distanceSorter(player);
std::sort(sortedChunks, sortedChunks + chunksLength, distanceSorter);
}
int count = 0;
if (occlusionCheck && !mc->options.anaglyph3d && layer == 0) {
int from = 0;
int to = 16;
//checkQueryResults(from, to);
for (int i = from; i < to; i++) {
sortedChunks[i]->occlusion_visible = true;
}
count += renderChunks(from, to, layer, alpha);
do {
from = to;
to = to * 2;
if (to > chunksLength) to = chunksLength;
glDisable2(GL_TEXTURE_2D);
glDisable2(GL_LIGHTING);
glDisable2(GL_ALPHA_TEST);
glDisable2(GL_FOG);
glColorMask(false, false, false, false);
glDepthMask(false);
//checkQueryResults(from, to);
glPushMatrix2();
float xo = 0;
float yo = 0;
float zo = 0;
for (int i = from; i < to; i++) {
if (sortedChunks[i]->isEmpty()) {
sortedChunks[i]->visible = false;
continue;
}
if (!sortedChunks[i]->visible) {
sortedChunks[i]->occlusion_visible = true;
}
if (sortedChunks[i]->visible && !sortedChunks[i]->occlusion_querying) {
float dist = Mth::sqrt(sortedChunks[i]->distanceToSqr(player));
int frequency = (int) (1 + dist / 128);
if (ticks % frequency == i % frequency) {
Chunk* chunk = sortedChunks[i];
float xt = (float) (chunk->x - xOff);
float yt = (float) (chunk->y - yOff);
float zt = (float) (chunk->z - zOff);
float xdd = xt - xo;
float ydd = yt - yo;
float zdd = zt - zo;
if (xdd != 0 || ydd != 0 || zdd != 0) {
glTranslatef2(xdd, ydd, zdd);
xo += xdd;
yo += ydd;
zo += zdd;
}
sortedChunks[i]->renderBB();
sortedChunks[i]->occlusion_querying = true;
}
}
}
glPopMatrix2();
glColorMask(true, true, true, true);
glDepthMask(true);
glEnable2(GL_TEXTURE_2D);
glEnable2(GL_ALPHA_TEST);
glEnable2(GL_FOG);
count += renderChunks(from, to, layer, alpha);
} while (to < chunksLength);
} else {
TIMER_POP_PUSH("render");
count += renderChunks(0, chunksLength, layer, alpha);
}
TIMER_POP();
return count;
}
void LevelRenderer::renderDebug(const AABB& b, float a) const {
float x0 = b.x0;
float x1 = b.x1;
float y0 = b.y0;
float y1 = b.y1;
float z0 = b.z0;
float z1 = b.z1;
float u0 = 0, v0 = 0;
float u1 = 1, v1 = 1;
glEnable2(GL_BLEND);
glBlendFunc2(GL_DST_COLOR, GL_SRC_COLOR);
glDisable2(GL_TEXTURE_2D);
glColor4f2(1, 1, 1, 1);
textures->loadAndBindTexture("terrain.png");
Tesselator& t = Tesselator::instance;
t.begin();
t.color(255, 255, 255, 255);
t.offset(((Mob*)mc->player)->getPos(a).negated());
// up
t.vertexUV(x0, y0, z1, u0, v1);
t.vertexUV(x0, y0, z0, u0, v0);
t.vertexUV(x1, y0, z0, u1, v0);
t.vertexUV(x1, y0, z1, u1, v1);
// down
t.vertexUV(x1, y1, z1, u1, v1);
t.vertexUV(x1, y1, z0, u1, v0);
t.vertexUV(x0, y1, z0, u0, v0);
t.vertexUV(x0, y1, z1, u0, v1);
// north
t.vertexUV(x0, y1, z0, u1, v0);
t.vertexUV(x1, y1, z0, u0, v0);
t.vertexUV(x1, y0, z0, u0, v1);
t.vertexUV(x0, y0, z0, u1, v1);
// south
t.vertexUV(x0, y1, z1, u0, v0);
t.vertexUV(x0, y0, z1, u0, v1);
t.vertexUV(x1, y0, z1, u1, v1);
t.vertexUV(x1, y1, z1, u1, v0);
// west
t.vertexUV(x0, y1, z1, u1, v0);
t.vertexUV(x0, y1, z0, u0, v0);
t.vertexUV(x0, y0, z0, u0, v1);
t.vertexUV(x0, y0, z1, u1, v1);
// east
t.vertexUV(x1, y0, z1, u0, v1);
t.vertexUV(x1, y0, z0, u1, v1);
t.vertexUV(x1, y1, z0, u1, v0);
t.vertexUV(x1, y1, z1, u0, v0);
t.offset(0, 0, 0);
t.draw();
glEnable2(GL_TEXTURE_2D);
glDisable2(GL_BLEND);
}
void LevelRenderer::render(const AABB& b) const
{
Tesselator& t = Tesselator::instance;
glColor4f2(1, 1, 1, 1);
textures->loadAndBindTexture("terrain.png");
//t.begin();
t.color(255, 255, 255, 255);
t.offset(((Mob*)mc->player)->getPos(0).negated());
t.begin(GL_LINE_STRIP);
t.vertex(b.x0, b.y0, b.z0);
t.vertex(b.x1, b.y0, b.z0);
t.vertex(b.x1, b.y0, b.z1);
t.vertex(b.x0, b.y0, b.z1);
t.vertex(b.x0, b.y0, b.z0);
t.draw();
t.begin(GL_LINE_STRIP);
t.vertex(b.x0, b.y1, b.z0);
t.vertex(b.x1, b.y1, b.z0);
t.vertex(b.x1, b.y1, b.z1);
t.vertex(b.x0, b.y1, b.z1);
t.vertex(b.x0, b.y1, b.z0);
t.draw();
t.begin(GL_LINES);
t.vertex(b.x0, b.y0, b.z0);
t.vertex(b.x0, b.y1, b.z0);
t.vertex(b.x1, b.y0, b.z0);
t.vertex(b.x1, b.y1, b.z0);
t.vertex(b.x1, b.y0, b.z1);
t.vertex(b.x1, b.y1, b.z1);
t.vertex(b.x0, b.y0, b.z1);
t.vertex(b.x0, b.y1, b.z1);
t.offset(0, 0, 0);
t.draw();
}
//void LevelRenderer::checkQueryResults( int from, int to )
//{
// for (int i = from; i < to; i++) {
// if (sortedChunks[i]->occlusion_querying) {
// // I wanna do a fast occusion culler here.
// }
// }
//}
int LevelRenderer::renderChunks( int from, int to, int layer, float alpha )
{
_renderChunks.clear();
int count = 0;
for (int i = from; i < to; i++) {
if (layer == 0) {
totalChunks++;
if (sortedChunks[i]->empty[layer]) emptyChunks++;
else if (!sortedChunks[i]->visible) offscreenChunks++;
else if (occlusionCheck && !sortedChunks[i]->occlusion_visible) occludedChunks++;
else renderedChunks++;
}
if (!sortedChunks[i]->empty[layer] && sortedChunks[i]->visible && sortedChunks[i]->occlusion_visible) {
int list = sortedChunks[i]->getList(layer);
if (list >= 0) {
_renderChunks.push_back(sortedChunks[i]);
count++;
}
}
}
Mob* player = mc->cameraTargetPlayer;
float xOff = player->xOld + (player->x - player->xOld) * alpha;
float yOff = player->yOld + (player->y - player->yOld) * alpha;
float zOff = player->zOld + (player->z - player->zOld) * alpha;
//int lists = 0;
renderList.clear();
renderList.init(xOff, yOff, zOff);
for (unsigned int i = 0; i < _renderChunks.size(); ++i) {
Chunk* chunk = _renderChunks[i];
#ifdef USE_VBO
renderList.addR(chunk->getRenderChunk(layer));
#else
renderList.add(chunk->getList(layer));
#endif
renderList.next();
}
renderSameAsLast(layer, alpha);
return count;
}
void LevelRenderer::renderSameAsLast( int layer, float alpha )
{
renderList.render();
}
void LevelRenderer::tick()
{
ticks++;
}
bool LevelRenderer::updateDirtyChunks( Mob* player, bool force )
{
bool slow = false;
if (slow) {
DirtyChunkSorter dirtySorter(player);
std::sort(dirtyChunks.begin(), dirtyChunks.end(), dirtySorter);
int s = dirtyChunks.size() - 1;
int amount = dirtyChunks.size();
for (int i = 0; i < amount; i++) {
Chunk* chunk = dirtyChunks[s-i];
if (!force) {
if (chunk->distanceToSqr(player) > 16 * 16) {
if (chunk->visible) {
if (i >= MAX_VISIBLE_REBUILDS_PER_FRAME) return false;
} else {
if (i >= MAX_INVISIBLE_REBUILDS_PER_FRAME) return false;
}
}
} else {
if (!chunk->visible) continue;
}
chunk->rebuild();
dirtyChunks.erase( std::find(dirtyChunks.begin(), dirtyChunks.end(), chunk) ); // @q: s-i?
chunk->setClean();
}
return dirtyChunks.size() == 0;
} else {
const int count = 3;
DirtyChunkSorter dirtyChunkSorter(player);
Chunk* toAdd[count] = {NULL};
std::vector<Chunk*>* nearChunks = NULL;
int pendingChunkSize = dirtyChunks.size();
int pendingChunkRemoved = 0;
for (int i = 0; i < pendingChunkSize; i++) {
Chunk* chunk = dirtyChunks[i];
if (!force) {
if (chunk->distanceToSqr(player) > 1024.0f) {
int index;
// is this chunk in the closest <count>?
for (index = 0; index < count; index++) {
if (toAdd[index] != NULL && dirtyChunkSorter(toAdd[index], chunk) == false) {
break;
}
}
index--;
if (index > 0) {
int x = index;
while (--x != 0) {
toAdd[x - 1] = toAdd[x];
}
toAdd[index] = chunk;
}
continue;
}
} else if (!chunk->visible) {
continue;
}
// chunk is very close -- always render
if (nearChunks == NULL) {
nearChunks = new std::vector<Chunk*>();
}
pendingChunkRemoved++;
nearChunks->push_back(chunk);
dirtyChunks[i] = NULL;
}
// if there are nearby chunks that need to be prepared for
// rendering, sort them and then process them
static const float MaxFrameTime = 1.0f / 100.0f;
Stopwatch chunkWatch;
chunkWatch.start();
if (nearChunks != NULL) {
if (nearChunks->size() > 1) {
std::sort(nearChunks->begin(), nearChunks->end(), dirtyChunkSorter);
}
for (int i = nearChunks->size() - 1; i >= 0; i--) {
Chunk* chunk = (*nearChunks)[i];
chunk->rebuild();
chunk->setClean();
}
delete nearChunks;
}
// render the nearest <count> chunks (farther than 1024 units away)
int secondaryRemoved = 0;
for (int i = count - 1; i >= 0; i--) {
Chunk* chunk = toAdd[i];
if (chunk != NULL) {
float ttt = chunkWatch.stopContinue();
if (ttt >= MaxFrameTime) {
//LOGI("Too much work, I quit2!\n");
break;
}
if (!chunk->visible && i != count - 1) {
// escape early if chunks aren't ready
toAdd[i] = NULL;
toAdd[0] = NULL;
break;
}
toAdd[i]->rebuild();
toAdd[i]->setClean();
secondaryRemoved++;
}
}
// compact by removing nulls
int cursor = 0;
int target = 0;
int arraySize = dirtyChunks.size();
while (cursor != arraySize) {
Chunk* chunk = dirtyChunks[cursor];
if (chunk != NULL) {
bool remove = false;
for (int i = 0; i < count && !remove; i++)
if (chunk == toAdd[i]) {
remove = true;
}
if (!remove) {
//if (chunk == toAdd[0] || chunk == toAdd[1] || chunk == toAdd[2]) {
// ; // this chunk was rendered and should be removed
//} else {
if (target != cursor) {
dirtyChunks[target] = chunk;
}
target++;
}
}
cursor++;
}
// trim
if (cursor > target)
dirtyChunks.erase(dirtyChunks.begin() + target, dirtyChunks.end());
return pendingChunkSize == (pendingChunkRemoved + secondaryRemoved);
}
}
void LevelRenderer::renderHit( Player* player, const HitResult& h, int mode, /*ItemInstance*/void* inventoryItem, float a )
{
if (mode == 0) {
if (destroyProgress > 0) {
Tesselator& t = Tesselator::instance;
glEnable2(GL_BLEND);
glBlendFunc2(GL_DST_COLOR, GL_SRC_COLOR);
textures->loadAndBindTexture("terrain.png");
glPushMatrix2();
int tileId = level->getTile(h.x, h.y, h.z);
Tile* tile = tileId > 0 ? Tile::tiles[tileId] : NULL;
//glDisable2(GL_ALPHA_TEST);
glPolygonOffset(-3.0f, -3.0f);
glEnable2(GL_POLYGON_OFFSET_FILL);
t.begin();
t.color(1.0f, 1.0f, 1.0f, 0.5f);
t.noColor();
float xo = player->xOld + (player->x - player->xOld) * a;
float yo = player->yOld + (player->y - player->yOld) * a;
float zo = player->zOld + (player->z - player->zOld) * a;
t.offset(-xo, -yo, -zo);
//t.noColor();
if (tile == NULL) tile = Tile::rock;
const int progress = (int) (destroyProgress * 10);
tileRenderer->tesselateInWorld(tile, h.x, h.y, h.z, 15 * 16 + progress);
t.draw();
t.offset(0, 0, 0);
glPolygonOffset(0.0f, 0.0f);
glDisable2(GL_POLYGON_OFFSET_FILL);
//glDisable2(GL_ALPHA_TEST);
glDisable2(GL_BLEND);
glDepthMask(true);
glPopMatrix2();
}
}
//else if (inventoryItem != NULL) {
// glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// float br = ((float) (util.Mth::sin(System.currentTimeMillis() / 100.0f)) * 0.2f + 0.8f);
// glColor4f2(br, br, br, ((float) (util.Mth::sin(System.currentTimeMillis() / 200.0f)) * 0.2f + 0.5f));
// int id = textures.loadTexture("terrain.png");
// glBindTexture2(GL_TEXTURE_2D, id);
// int x = h.x;
// int y = h.y;
// int z = h.z;
// if (h.f == 0) y--;
// if (h.f == 1) y++;
// if (h.f == 2) z--;
// if (h.f == 3) z++;
// if (h.f == 4) x--;
// if (h.f == 5) x++;
// /*
// * t.begin(); t.noColor(); Tile.tiles[tileType].tesselate(level, x,
// * y, z, t); t.end();
// */
// }
}
void LevelRenderer::renderHitOutline( Player* player, const HitResult& h, int mode, /*ItemInstance*/void* inventoryItem, float a )
{
if (mode == 0 && h.type == TILE) {
glEnable2(GL_BLEND);
glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor4f2(0, 0, 0, 0.4f);
glLineWidth(1.0f);
glDisable2(GL_TEXTURE_2D);
glDepthMask(false);
float ss = 0.002f;
int tileId = level->getTile(h.x, h.y, h.z);
if (tileId > 0) {
Tile::tiles[tileId]->updateShape(level, h.x, h.y, h.z);
float xo = player->xOld + (player->x - player->xOld) * a;
float yo = player->yOld + (player->y - player->yOld) * a;
float zo = player->zOld + (player->z - player->zOld) * a;
render(Tile::tiles[tileId]->getTileAABB(level, h.x, h.y, h.z).grow(ss, ss, ss).cloneMove(-xo, -yo, -zo));
}
glDepthMask(true);
glEnable2(GL_TEXTURE_2D);
glDisable2(GL_BLEND);
}
}
void LevelRenderer::setDirty( int x0, int y0, int z0, int x1, int y1, int z1 )
{
int _x0 = Mth::intFloorDiv(x0, CHUNK_SIZE);
int _y0 = Mth::intFloorDiv(y0, CHUNK_SIZE);
int _z0 = Mth::intFloorDiv(z0, CHUNK_SIZE);
int _x1 = Mth::intFloorDiv(x1, CHUNK_SIZE);
int _y1 = Mth::intFloorDiv(y1, CHUNK_SIZE);
int _z1 = Mth::intFloorDiv(z1, CHUNK_SIZE);
for (int x = _x0; x <= _x1; x++) {
int xx = x % xChunks;
if (xx < 0) xx += xChunks;
for (int y = _y0; y <= _y1; y++) {
int yy = y % yChunks;
if (yy < 0) yy += yChunks;
for (int z = _z0; z <= _z1; z++) {
int zz = z % zChunks;
if (zz < 0) zz += zChunks;
int p = ((zz) * yChunks + (yy)) * xChunks + (xx);
Chunk* chunk = chunks[p];
if (!chunk->isDirty()) {
dirtyChunks.push_back(chunk);
chunk->setDirty();
}
}
}
}
}
void LevelRenderer::tileChanged( int x, int y, int z)
{
setDirty(x - 1, y - 1, z - 1, x + 1, y + 1, z + 1);
}
void LevelRenderer::setTilesDirty( int x0, int y0, int z0, int x1, int y1, int z1 )
{
setDirty(x0 - 1, y0 - 1, z0 - 1, x1 + 1, y1 + 1, z1 + 1);
}
void LevelRenderer::cull( Culler* culler, float a )
{
for (int i = 0; i < chunksLength; i++) {
if (!chunks[i]->isEmpty()) {
if (!chunks[i]->visible || ((i + cullStep) & 15) == 0) {
chunks[i]->cull(culler);
}
}
}
cullStep++;
}
void LevelRenderer::skyColorChanged()
{
for (int i = 0; i < chunksLength; i++) {
if (chunks[i]->skyLit) {
if (!chunks[i]->isDirty()) {
dirtyChunks.push_back(chunks[i]);
chunks[i]->setDirty();
}
}
}
}
bool entityRenderPredicate(const Entity* a, const Entity* b) {
return a->entityRendererId < b->entityRendererId;
}
void LevelRenderer::renderEntities(Vec3 cam, Culler* culler, float a) {
if (noEntityRenderFrames > 0) {
noEntityRenderFrames--;
return;
}
TIMER_PUSH("prepare");
TileEntityRenderDispatcher::getInstance()->prepare(level, textures, mc->font, mc->cameraTargetPlayer, a);
EntityRenderDispatcher::getInstance()->prepare(level, mc->font, mc->cameraTargetPlayer, &mc->options, a);
totalEntities = 0;
renderedEntities = 0;
culledEntities = 0;
Entity* player = mc->cameraTargetPlayer;
EntityRenderDispatcher::xOff = TileEntityRenderDispatcher::xOff = (player->xOld + (player->x - player->xOld) * a);
EntityRenderDispatcher::yOff = TileEntityRenderDispatcher::yOff = (player->yOld + (player->y - player->yOld) * a);
EntityRenderDispatcher::zOff = TileEntityRenderDispatcher::zOff = (player->zOld + (player->z - player->zOld) * a);
glEnableClientState2(GL_VERTEX_ARRAY);
glEnableClientState2(GL_TEXTURE_COORD_ARRAY);
TIMER_POP_PUSH("entities");
const EntityList& entities = level->getAllEntities();
totalEntities = entities.size();
if (totalEntities > 0) {
Entity** toRender = new Entity*[totalEntities];
for (int i = 0; i < totalEntities; i++) {
Entity* entity = entities[i];
if (entity->shouldRender(cam) && culler->isVisible(entity->bb))
{
if (entity == mc->cameraTargetPlayer && mc->options.thirdPersonView == 0 && mc->cameraTargetPlayer->isPlayer() && !((Player*)mc->cameraTargetPlayer)->isSleeping()) continue;
if (entity == mc->cameraTargetPlayer && !mc->options.thirdPersonView)
continue;
if (!level->hasChunkAt(Mth::floor(entity->x), Mth::floor(entity->y), Mth::floor(entity->z)))
continue;
toRender[renderedEntities++] = entity;
//EntityRenderDispatcher::getInstance()->render(entity, a);
}
}
if (renderedEntities > 0) {
std::sort(&toRender[0], &toRender[renderedEntities], entityRenderPredicate);
for (int i = 0; i < renderedEntities; ++i) {
EntityRenderDispatcher* disp = EntityRenderDispatcher::getInstance();
disp->render(toRender[i], a);
}
}
delete[] toRender;
}
TIMER_POP_PUSH("tileentities");
for (unsigned int i = 0; i < level->tileEntities.size(); i++) {
TileEntityRenderDispatcher::getInstance()->render(level->tileEntities[i], a);
}
glDisableClientState2(GL_VERTEX_ARRAY);
glDisableClientState2(GL_TEXTURE_COORD_ARRAY);
TIMER_POP();
}
std::string LevelRenderer::gatherStats1() {
std::stringstream ss;
ss << "C: " << renderedChunks << "/" << totalChunks << ". F: " << offscreenChunks << ", O: " << occludedChunks << ", E: " << emptyChunks << "\n";
return ss.str();
}
//
// /*public*/ std::string gatherStats2() {
// return "E: " + renderedEntities + "/" + totalEntities + ". B: " + culledEntities + ", I: " + ((totalEntities - culledEntities) - renderedEntities);
// }
//
// int[] toRender = new int[50000];
// IntBuffer resultBuffer = MemoryTracker.createIntBuffer(64);
void LevelRenderer::renderSky(float alpha) {
if (mc->level->dimension->foggy) return;
glDisable2(GL_TEXTURE_2D);
Vec3 sc = level->getSkyColor(mc->cameraTargetPlayer, alpha);
float sr = (float) sc.x;
float sg = (float) sc.y;
float sb = (float) sc.z;// + 0.5f;
if (mc->options.anaglyph3d) {
float srr = (sr * 30.0f + sg * 59.0f + sb * 11.0f) / 100.0f;
float sgg = (sr * 30.0f + sg * 70.0f) / (100.0f);
float sbb = (sr * 30.0f + sb * 70.0f) / (100.0f);
sr = srr;
sg = sgg;
sb = sbb;
}
glColor4f2(sr, sg, Mth::Min(1.0f, sb), 1);
//Tesselator& t = Tesselator::instance;
glEnable2(GL_FOG);
glColor4f2(sr, sg, sb, 1.0f);
#ifdef OPENGL_ES
drawArrayVT(skyBuffer, skyVertexCount);
#endif
glEnable2(GL_TEXTURE_2D);
}
void LevelRenderer::renderClouds( float alpha ) {
//if (!mc->level->dimension->isNaturalDimension()) return;
glEnable2(GL_TEXTURE_2D);
glDisable(GL_CULL_FACE);
float yOffs = (float) (mc->player->yOld + (mc->player->y - mc->player->yOld) * alpha);
int s = 32;
int d = 256 / s;
Tesselator& t = Tesselator::instance;
//glBindTexture(GL_TEXTURE_2D, texturesloadTexture("/environment/clouds.png"));
textures->loadAndBindTexture("environment/clouds.png");
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
Vec3 cc = level->getCloudColor(alpha);
float cr = (float) cc.x;
float cg = (float) cc.y;
float cb = (float) cc.z;
float scale = 1 / 2048.0f;
float time = (ticks + alpha);
float xo = mc->player->xo + (mc->player->x - mc->player->xo) * alpha + time * 0.03f;
float zo = mc->player->zo + (mc->player->z - mc->player->zo) * alpha;
int xOffs = Mth::floor(xo / 2048);
int zOffs = Mth::floor(zo / 2048);
xo -= xOffs * 2048;
zo -= zOffs * 2048;
float yy = /*level.dimension.getCloudHeight()*/ 128 - yOffs + 0.33f;//mc->player->y + 1;
float uo = (float) (xo * scale);
float vo = (float) (zo * scale);
t.begin();
t.color(cr, cg, cb, 0.8f);
for (int xx = -s * d; xx < +s * d; xx += s) {
for (int zz = -s * d; zz < +s * d; zz += s) {
t.vertexUV((float)xx, yy, (float)zz + s, xx * scale + uo, (zz + s) * scale + vo);
t.vertexUV((float)xx + s, yy, (float)zz + s, (xx + s) * scale + uo, (zz + s) * scale + vo);
t.vertexUV((float)xx + s, yy, (float)zz, (xx + s) * scale + uo, zz * scale + vo);
t.vertexUV((float)xx, yy, (float)zz, xx * scale + uo, zz * scale + vo);
}
}
t.endOverrideAndDraw();
glColor4f(1, 1, 1, 1.0f);
glDisable(GL_BLEND);
glEnable(GL_CULL_FACE);
}
void LevelRenderer::playSound(const std::string& name, float x, float y, float z, float volume, float pitch) {
// @todo: deny sounds here if sound is off (rather than waiting 'til SoundEngine)
float dd = 16;
if (volume > 1) dd *= volume;
if (mc->cameraTargetPlayer->distanceToSqr(x, y, z) < dd * dd) {
mc->soundEngine->play(name, x, y, z, volume, pitch);
}
}
void LevelRenderer::addParticle(const std::string& name, float x, float y, float z, float xa, float ya, float za, int data) {
float xd = mc->cameraTargetPlayer->x - x;
float yd = mc->cameraTargetPlayer->y - y;
float zd = mc->cameraTargetPlayer->z - z;
float distanceSquared = xd * xd + yd * yd + zd * zd;
//Particle* p = NULL;
//if (name == "hugeexplosion") p = new HugeExplosionSeedParticle(level, x, y, z, xa, ya, za);
//else if (name == "largeexplode") p = new HugeExplosionParticle(textures, level, x, y, z, xa, ya, za);
//if (p) {
// if (distanceSquared < 32 * 32) {
// mc->particleEngine->add(p);
// } else { delete p; }
// return;
//}
const float particleDistance = 16;
if (distanceSquared > particleDistance * particleDistance) return;
//static Stopwatch sw;
//sw.start();
if (name == "bubble") mc->particleEngine->add(new BubbleParticle(level, x, y, z, xa, ya, za));
else if (name == "crit") mc->particleEngine->add(new CritParticle2(level, x, y, z, xa, ya, za));
else if (name == "smoke") mc->particleEngine->add(new SmokeParticle(level, x, y, z, xa, ya, za));
//else if (name == "note") mc->particleEngine->add(new NoteParticle(level, x, y, z, xa, ya, za));
else if (name == "explode") mc->particleEngine->add(new ExplodeParticle(level, x, y, z, xa, ya, za));
else if (name == "flame") mc->particleEngine->add(new FlameParticle(level, x, y, z, xa, ya, za));
else if (name == "lava") mc->particleEngine->add(new LavaParticle(level, x, y, z));
//else if (name == "splash") mc->particleEngine->add(new SplashParticle(level, x, y, z, xa, ya, za));
else if (name == "largesmoke") mc->particleEngine->add(new SmokeParticle(level, x, y, z, xa, ya, za, 2.5f));
else if (name == "reddust") mc->particleEngine->add(new RedDustParticle(level, x, y, z, xa, ya, za));
else if (name == "iconcrack") mc->particleEngine->add(new BreakingItemParticle(level, x, y, z, xa, ya, za, Item::items[data]));
else if (name == "snowballpoof") mc->particleEngine->add(new BreakingItemParticle(level, x, y, z, Item::snowBall));
//else if (name == "snowballpoof") mc->particleEngine->add(new BreakingItemParticle(level, x, y, z, Item::snowBall));
//else if (name == "slime") mc->particleEngine->add(new BreakingItemParticle(level, x, y, z, Item::slimeBall));
//else if (name == "heart") mc->particleEngine->add(new HeartParticle(level, x, y, z, xa, ya, za));
//sw.stop();
//sw.printEvery(50, "add-particle-string");
}
/*
void LevelRenderer::addParticle(ParticleType::Id name, float x, float y, float z, float xa, float ya, float za, int data) {
float xd = mc->cameraTargetPlayer->x - x;
float yd = mc->cameraTargetPlayer->y - y;
float zd = mc->cameraTargetPlayer->z - z;
const float particleDistance = 16;
if (xd * xd + yd * yd + zd * zd > particleDistance * particleDistance) return;
//static Stopwatch sw;
//sw.start();
//Particle* p = NULL;
if (name == ParticleType::bubble) mc->particleEngine->add( new BubbleParticle(level, x, y, z, xa, ya, za) );
else if (name == ParticleType::crit) mc->particleEngine->add(new CritParticle2(level, x, y, z, xa, ya, za) );
else if (name == ParticleType::smoke) mc->particleEngine->add(new SmokeParticle(level, x, y, z, xa, ya, za) );
else if (name == ParticleType::explode) mc->particleEngine->add( new ExplodeParticle(level, x, y, z, xa, ya, za) );
else if (name == ParticleType::flame) mc->particleEngine->add( new FlameParticle(level, x, y, z, xa, ya, za) );
else if (name == ParticleType::lava) mc->particleEngine->add( new LavaParticle(level, x, y, z) );
else if (name == ParticleType::largesmoke) mc->particleEngine->add( new SmokeParticle(level, x, y, z, xa, ya, za, 2.5f) );
else if (name == ParticleType::reddust) mc->particleEngine->add( new RedDustParticle(level, x, y, z, xa, ya, za) );
else if (name == ParticleType::iconcrack) mc->particleEngine->add( new BreakingItemParticle(level, x, y, z, xa, ya, za, Item::items[data]) );
//switch (name) {
// case ParticleType::bubble: p = new BubbleParticle(level, x, y, z, xa, ya, za); break;
// case ParticleType::crit: p = new CritParticle2(level, x, y, z, xa, ya, za); break;
// case ParticleType::smoke: p = new SmokeParticle(level, x, y, z, xa, ya, za); break;
// //case ParticleType::note: p = new NoteParticle(level, x, y, z, xa, ya, za); break;
// case ParticleType::explode: p = new ExplodeParticle(level, x, y, z, xa, ya, za); break;
// case ParticleType::flame: p = new FlameParticle(level, x, y, z, xa, ya, za); break;
// case ParticleType::lava: p = new LavaParticle(level, x, y, z); break;
// //case ParticleType::splash: p = new SplashParticle(level, x, y, z, xa, ya, za); break;
// case ParticleType::largesmoke: p = new SmokeParticle(level, x, y, z, xa, ya, za, 2.5f); break;
// case ParticleType::reddust: p = new RedDustParticle(level, x, y, z, xa, ya, za); break;
// case ParticleType::iconcrack: p = new BreakingItemParticle(level, x, y, z, xa, ya, za, Item::items[data]); break;
// //case ParticleType::snowballpoof: p = new BreakingItemParticle(level, x, y, z, Item::snowBall); break;
// //case ParticleType::slime: p = new BreakingItemParticle(level, x, y, z, Item::slimeBall); break;
// //case ParticleType::heart: p = new HeartParticle(level, x, y, z, xa, ya, za); break;
// default:
// LOGW("Couldn't find particle of type: %d\n", name);
// break;
//}
//if (p) {
// mc->particleEngine->add(p);
//}
//sw.stop();
//sw.printEvery(50, "add-particle-enum");
}
*/
void LevelRenderer::renderHitSelect( Player* player, const HitResult& h, int mode, /*ItemInstance*/void* inventoryItem, float a )
{
//if (h.type == TILE) LOGI("type: %s @ (%d, %d, %d)\n", Tile::tiles[level->getTile(h.x, h.y, h.z)]->getDescriptionId().c_str(), h.x, h.y, h.z);
if (mode == 0) {
Tesselator& t = Tesselator::instance;
glEnable2(GL_BLEND);
glDisable2(GL_TEXTURE_2D);
glBlendFunc2(GL_SRC_ALPHA, GL_ONE);
glBlendFunc2(GL_DST_COLOR, GL_SRC_COLOR);
glEnable2(GL_DEPTH_TEST);
textures->loadAndBindTexture("terrain.png");
int tileId = level->getTile(h.x, h.y, h.z);
Tile* tile = tileId > 0 ? Tile::tiles[tileId] : NULL;
glDisable2(GL_ALPHA_TEST);
//LOGI("block: %d - %d (%s)\n", tileId, level->getData(h.x, h.y, h.z), tile==NULL?"null" : tile->getDescriptionId().c_str() );
const float br = 0.65f;
glColor4f2(br * 1.0f, br * 1.0f, br * 1.0f, br * 1.0f);
glPushMatrix2();
//glPolygonOffset(-.3f, -.3f);
glPolygonOffset(-1.f, -1.f); //Implementation dependent units
glEnable2(GL_POLYGON_OFFSET_FILL);
float xo = player->xOld + (player->x - player->xOld) * a;
float yo = player->yOld + (player->y - player->yOld) * a;
float zo = player->zOld + (player->z - player->zOld) * a;
t.begin();
t.offset(-xo, -yo, -zo);
t.noColor();
if (tile == NULL) tile = Tile::rock;
tileRenderer->tesselateInWorld(tile, h.x, h.y, h.z);
t.draw();
t.offset(0, 0, 0);
glPolygonOffset(0.0f, 0.0f);
glDisable2(GL_POLYGON_OFFSET_FILL);
glEnable2(GL_TEXTURE_2D);
glDepthMask(true);
glPopMatrix2();
glEnable2(GL_ALPHA_TEST);
glDisable2(GL_BLEND);
}
}
void LevelRenderer::onGraphicsReset()
{
generateSky();
// Get new buffers
#ifdef OPENGL_ES
glGenBuffers2(numListsOrBuffers, chunkBuffers);
#else
chunkLists = glGenLists(numListsOrBuffers);
#endif
// Rebuild
allChanged();
}
void LevelRenderer::entityAdded( Entity* entity )
{
if (!entity->isPlayer())
return;
// Hack to (hopefully) get the players to show
EntityRenderDispatcher::getInstance()->onGraphicsReset();
}
int _t_keepPic = -1;
void LevelRenderer::takePicture( TripodCamera* cam, Entity* entity )
{
// Push old values
Mob* oldCameraEntity = mc->cameraTargetPlayer;
bool hideGui = mc->options.hideGui;
bool thirdPerson = mc->options.thirdPersonView;
// @huge @attn: This is highly illegal, super temp!
mc->cameraTargetPlayer = (Mob*)cam;
mc->options.hideGui = true;
mc->options.thirdPersonView = false;
mc->gameRenderer->renderLevel(0);
// Pop values back
mc->cameraTargetPlayer = oldCameraEntity;
mc->options.hideGui = hideGui;
mc->options.thirdPersonView = thirdPerson;
_t_keepPic = -1;
// Save image
static char filename[256];
sprintf(filename, "%s/games/com.mojang/img_%.4d.jpg", mc->externalStoragePath.c_str(), getTimeMs());
mc->platform()->saveScreenshot(filename, mc->width, mc->height);
}
void LevelRenderer::levelEvent(Player* player, int type, int x, int y, int z, int data) {
switch (type) {
case LevelEvent::SOUND_OPEN_DOOR:
if (Mth::random() < 0.5f) {
level->playSound(x + 0.5f, y + 0.5f, z + 0.5f, "random.door_open", 1, level->random.nextFloat() * 0.1f + 0.9f);
} else {
level->playSound(x + 0.5f, y + 0.5f, z + 0.5f, "random.door_close", 1, level->random.nextFloat() * 0.1f + 0.9f);
}
break;
}
}