void DrawLandscapeForShadows(StageOneShader *shader) { if (!Model::gPlayer.KnownPosition()) return; ChunkCoord player_cc; Model::gPlayer.GetChunkCoord(&player_cc); // Draw all visible chunks. Chunks that are not loaded will trigger a reload from the server. The top chunks // should be loaded first, as they affect the lighting on the chunks below. for (auto it=sShadowChunks.begin() ; it != sShadowChunks.end(); it++ ) { Chunk *cp = ChunkFind(&(*it), true); if (!cp->IsDirty() && cp->fChunkObject && cp->fChunkObject->Empty()) { // This chunk exists, is updated, but contains nothing. continue; } int dx = cp->cc.x - player_cc.x; int dy = cp->cc.y - player_cc.y; int dz = cp->cc.z - player_cc.z; glm::mat4 modelMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(dx*CHUNK_SIZE, dz*CHUNK_SIZE, -dy*CHUNK_SIZE)); shader->Model(modelMatrix); cp->Draw(shader, 0, DL_NoTransparent); cp->DrawObjects(shader, dx, dy, dz, true); } }
void DrawLandscapeTopDown(StageOneShader *shader, int width, int height, bool forceload, DL_Type dlType) { if (!Model::gPlayer.KnownPosition()) return; ChunkCoord player_cc; Model::gPlayer.GetChunkCoord(&player_cc); int verticallimit = (height+CHUNK_SIZE-1)/CHUNK_SIZE/2; int horisontallimit = (width+CHUNK_SIZE-1)/CHUNK_SIZE/2; // Draw all visible chunks. Chunks that are not loaded will trigger a reload from the server. The top chunks // should be loaded first, as they affect the lighting on the chunks below. for (int dz = verticallimit; dz >= -verticallimit; dz--) for (int dx = -horisontallimit; dx <= horisontallimit; dx++) for (int dy = -horisontallimit; dy <= horisontallimit; dy++) { glm::mat4 modelMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(dx*CHUNK_SIZE, dz*CHUNK_SIZE, -dy*CHUNK_SIZE)); ChunkCoord cc; cc.x = player_cc.x + dx; cc.y = player_cc.y + dy; cc.z = player_cc.z + dz; Chunk *cp = ChunkFind(&cc, forceload); if (cp == 0) continue; shader->Model(modelMatrix); // Draw the chunk if (!cp->IsDirty() && cp->fChunkObject && cp->fChunkObject->Empty()) { // This chunk exists, is updated, but contains nothing. continue; } cp->Draw(shader, 0, dlType); cp->DrawObjects(shader, dx, dy, dz, true); } }
void Chunks::Update() { int updated = 0; for (int i = 0; i < VIEW_DISTANCE / CHUNK_SIZE_X * 2; i++) { for (int j = 0; j < VIEW_DISTANCE / CHUNK_SIZE_Z * 2; ++j) { Chunk* c = Get(i,j); if (c->IsReady()) { if (c->IsDirty() && updated < 2) { c->Update(); updated++; } } } } }
// TODO: This function should be split into a separate function for picking mode. void DrawLandscape(StageOneShader *shader, DL_Type dlType) { if (!Model::gPlayer.KnownPosition()) return; if (dlType == DL_NoTransparent) { // The lits of chunks used for shadowing is computed below, when in non-transparent mode. // Start the list empty. sShadowChunks.clear(); } FindAllNearChunks(sChunkDistances); ChunkCoord player_cc; Model::gPlayer.GetChunkCoord(&player_cc); // Draw all visible chunks. Chunks that are not loaded will trigger a reload from the server. The top chunks // should be loaded first, as they affect the lighting on the chunks below. int chunkHor = (int)(maxRenderDistance / CHUNK_SIZE + 1); // At 80m viewing distance, there are 895 chunks. Using various filters, the actual chunks that can be seen are // much fewer. Save the filtered list, to speed up the drawing. int listOfVisibleChunks[sMaxVolume]; int src, dst; // Create list of visible chunks. This check is faster than using queries, so it is a good filter to // start with. for (src=0, dst=0; sChunkDistances[src].distance < chunkHor; src++) { if (src > sMaxVolume) break; // Just a safety precaution, should never happen Chunk *cp = sListOfNearChunks[src]; if (cp) { if (cp->fScheduledForLoading) continue; // Chunk doesn't exist yet // Do not ignore the chunk just because it doesn't have any data yet, as the data update is triggered // by the drawing function. if (!cp->IsDirty() && cp->fChunkObject && cp->fChunkObject->Empty()) { // This chunk exists, is updated, but contains nothing. continue; } } int dx = sChunkDistances[src].dx; int dy = sChunkDistances[src].dy; int dz = sChunkDistances[src].dz; glm::mat4 modelMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(dx*CHUNK_SIZE, dz*CHUNK_SIZE, -dy*CHUNK_SIZE)); if (Outside(dx, dy , dz, modelMatrix)) { continue; // Go to next chunk } listOfVisibleChunks[dst++] = src; } int visibleChunklistLength = dst; // printf("DrawLandscape: listOfVisibleChunks %d\n", visibleChunklistLength); bool insideAnyTeleport = false; // We need to know which TP is the nearest. float distanceToNearTP2 = 1000.0f*1000.0f; // Distance^2 to the nearest TP, initialized to something big. glm::vec3 TPPosition; for (int i=0; i<visibleChunklistLength; i++) { if (i%NUMQUERIES == 0 && dlType == DL_NoTransparent) { // Request NUMQUERIES queries, every NUMQUERIES chunk. // The chunks that are tested is not the same ones that will be drawn, // to allow the reading of the query result to be with a delay. // The first NUMQUERIES chunks are not tested. The occlusion test using queries is most effective // for long viewing distances, where there is a high chance of chunks not being visible. // TODO: The test could also be used for transparent objects, if it wasn't that QuerySetup() enables depth update again. int from = i+NUMQUERIES; int to = i+NUMQUERIES*2; if (to > visibleChunklistLength) to = visibleChunklistLength; if (to > from) QuerySetup(shader, from, to, listOfVisibleChunks); // Initiate the query for a group of chunks } int ind = listOfVisibleChunks[i]; if (dlType == DL_OnlyTransparent) ind = listOfVisibleChunks[visibleChunklistLength-i-1]; // Read chunks in reverse order for transparent objects if (i >= NUMQUERIES && dlType == DL_NoTransparent) { // There is no query initiated for the first NUMQUERIES chunks. GLuint numSamples = 1; glGetQueryObjectuiv(sQueryId[i%(NUMQUERIES*2)], GL_QUERY_RESULT, &numSamples); if (numSamples == 0) continue; // No pixels changed by this chunk, skip it! } int dx = sChunkDistances[ind].dx; int dy = sChunkDistances[ind].dy; int dz = sChunkDistances[ind].dz; glm::mat4 modelMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(dx*CHUNK_SIZE, dz*CHUNK_SIZE, -dy*CHUNK_SIZE)); // Don't allow picking outside of near chunks. if (dlType == DL_Picking && (dx < -1 || dx > 1 || dy < -1 || dy > 1 || dz < -1 || dz > 1)) break; ChunkCoord cc; cc.x = player_cc.x + dx; cc.y = player_cc.y + dy; cc.z = player_cc.z + dz; // If we have come this far, a null pointer is no longer accepted. The chunk is needed. Either we get // the real chunk, or an empty one. Chunk *cp = ChunkFind(&cc, true); if (dlType == DL_Picking) { // Picking mode. Throw away the previous triangles, and replace it with special triangles used // for the picking mode. These contain colour information to allow identification of cubes. cp->fChunkBlocks->TestJellyBlockTimeout(true); cp->PushTriangles(ChunkObject::Make(cp, true, dx, dy, dz)); // Stash away the old triangles for quick restore gChunkShaderPicking.Model(modelMatrix); } else { cp->fChunkBlocks->TestJellyBlockTimeout(false); shader->Model(modelMatrix); } if (dlType == DL_NoTransparent) AddChunkToShadowList(cp); // Draw the chunk cp->Draw(shader, &gChunkShaderPicking, dlType); if (dlType == DL_Picking) { // Picking mode. Restore the normal triangles again. cp->PopTriangles(); } if (dlType == DL_NoTransparent) { cp->DrawObjects(shader, dx, dy, dz, false); } if (dlType == DL_OnlyTransparent) { unsigned char tx, ty, tz; if (gMode.CommunicationAllowed() && Model::gSuperChunkManager.GetTeleport(&tx, &ty, &tz, &cp->cc)) { double diffX = double(Model::gPlayer.x)/BLOCK_COORD_RES - cc.x*CHUNK_SIZE - tx - 0.5; double diffY = double(Model::gPlayer.y)/BLOCK_COORD_RES - cc.y*CHUNK_SIZE - ty - 0.5; double diffZ = double(Model::gPlayer.z)/BLOCK_COORD_RES - cc.z*CHUNK_SIZE - tz - PLAYER_HEIGHT*2; double d2 = diffX*diffX + diffY*diffY + diffZ*diffZ; if (diffX < 1.5 && diffX > -1.5 && diffY < 1.5 && diffY > -1.5 && diffZ < 3.0 && diffZ > 0.0) insideAnyTeleport = true; // printf("%.2f, %.2f, %.2f\n", diffX, diffY, diffZ); if (gMode.Get() == GameMode::GAME) { // When in teleport mode, the teleports are drawn elsewhere. float x = (float)tx + dx*CHUNK_SIZE + 0.5f; float y = (float)tz + dz*CHUNK_SIZE + 0.5f; float z = -(float)ty - dy*CHUNK_SIZE - 0.5f; gDrawObjectList.emplace_back(glm::vec3(x, y+2, z), BT_Teleport); // gMsgWindow.Add("chunk::DrawObjects Teleport at %f,%f,%f\n", x, y, z); glm::mat4 modelMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(x, y, z)); glBindTexture(GL_TEXTURE_2D, GameTexture::Teleport); modelMatrix = glm::scale(modelMatrix, glm::vec3(4.0f, 4.0f, 4.0f)); modelMatrix = glm::rotate(modelMatrix, 45.0f, glm::vec3(0,1,0)); shader->Model(modelMatrix); gLantern.Draw(); } if (d2 < distanceToNearTP2) { TPPosition.x = diffX; TPPosition.y = diffY; TPPosition.z = diffZ; distanceToNearTP2 = d2; } } } } if (dlType == DL_OnlyTransparent) { static bool wasNearTP = false; if (distanceToNearTP2 < 100) { // Update sound effect only if inside a limited distance. gSoundControl.SetEnvironmentSound(SoundControl::STeleport, 0, -800*TPPosition.x, -800*TPPosition.y, -800*TPPosition.z); wasNearTP = true; } else if (wasNearTP) { wasNearTP = false; gSoundControl.RemoveEnvironmentSound(SoundControl::STeleport, 0); } // Test for being inside teleports is only done in transparent mode switch(gMode.Get()) { case GameMode::GAME: if (insideAnyTeleport) { gMode.Set(GameMode::TELEPORT); } break; case GameMode::TELEPORT: if (!insideAnyTeleport && !gAdminTP) { gMode.Set(GameMode::GAME); } break; case GameMode::CONSTRUCT: // Draw the semi transparent boundaries between chunks. DrawChunkBorders(shader); break; case GameMode::INIT: case GameMode::ESC: case GameMode::EXIT: case GameMode::LOGIN: case GameMode::LOGIN_FAILED: case GameMode::PASSWORD: case GameMode::REQ_PASSWD: case GameMode::WAIT_ACK: break; // Ignore } } }