453 lines
13 KiB
C++
453 lines
13 KiB
C++
/*---------------------------------------------------------------------------------
|
|
|
|
KubKraft
|
|
Copyright (C) 2012 Quent42340 <quent42340@gmail.com>
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
---------------------------------------------------------------------------------*/
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <unordered_map>
|
|
#include <map>
|
|
#include <cmath>
|
|
|
|
#include <SDL/SDL.h>
|
|
#include <GL/gl.h>
|
|
#include <GL/glu.h>
|
|
|
|
#include "types.h"
|
|
#include "config.h"
|
|
#include "cube.h"
|
|
#include "chunk.h"
|
|
#include "map.h"
|
|
#include "player.h"
|
|
#include "game.h"
|
|
|
|
using namespace std;
|
|
|
|
Chunk *Map::currentChunk = NULL;
|
|
Chunk *Map::selectedChunk = NULL;
|
|
Cube *Map::selectedCube = NULL;
|
|
|
|
Map::Map(u16 width, u16 depth, u16 height, Textures textures) {
|
|
m_width = width;
|
|
m_depth = depth;
|
|
m_height = height;
|
|
|
|
m_textures = textures;
|
|
|
|
m_map = (u16*)malloc(m_width * m_depth * m_height * sizeof(u16));
|
|
|
|
for(s32 z = 0 ; z < m_height ; z++) {
|
|
for(s32 y = 0 ; y < m_depth ; y++) {
|
|
for(s32 x = 0 ; x < m_width ; x++) {
|
|
m_map[_MAP_POS(x, y, z)] = 0;
|
|
if(z == 0) {
|
|
m_map[_MAP_POS(x, y, z)] = 3;
|
|
}
|
|
else if(z < m_height - 2) {
|
|
if(rand()%3 >= 2) m_map[_MAP_POS(x, y, z)] = 2;
|
|
} else {
|
|
if(rand()%3 >= 2) m_map[_MAP_POS(x, y, z)] = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
m_chunks = new Chunk*[(m_width >> 3) * (m_height >> 3) * (m_depth >> 3)];
|
|
|
|
for(u16 z = 0 ; z < m_height >> 3 ; z++) {
|
|
for(u16 y = 0 ; y < m_depth >> 3 ; y++) {
|
|
for(u16 x = 0 ; x < m_width >> 3 ; x++) {
|
|
m_chunks[CHUNK_POS(x, y, z)] = new Chunk(x << 3, y << 3, z << 3, m_textures);
|
|
}
|
|
}
|
|
}
|
|
|
|
for(u16 z = 0 ; z < m_height >> 3 ; z++) {
|
|
for(u16 y = 0 ; y < m_depth >> 3 ; y++) {
|
|
for(u16 x = 0 ; x < m_width >> 3 ; x++) {
|
|
int chunkIndex = x + (y * (m_width >> 3)) + (z * (m_width >> 3) * (m_depth >> 3));
|
|
if(x - 1 >= 0) m_chunks[chunkIndex]->setSurroundingChunk(0, m_chunks[chunkIndex - 1]);
|
|
if(x + 1 < m_width >> 3) m_chunks[chunkIndex]->setSurroundingChunk(1, m_chunks[chunkIndex + 1]);
|
|
if(y - 1 >= 0) m_chunks[chunkIndex]->setSurroundingChunk(2, m_chunks[chunkIndex - (m_width >> 3)]);
|
|
if(y + 1 < m_depth >> 3) m_chunks[chunkIndex]->setSurroundingChunk(3, m_chunks[chunkIndex + (m_width >> 3)]);
|
|
if(z + 1 < m_height >> 3) m_chunks[chunkIndex]->setSurroundingChunk(4, m_chunks[chunkIndex + ((m_width >> 3) * (m_depth >> 3))]);
|
|
if(z - 1 >= 0) m_chunks[chunkIndex]->setSurroundingChunk(5, m_chunks[chunkIndex - ((m_width >> 3) * (m_depth >> 3))]);
|
|
}
|
|
}
|
|
}
|
|
|
|
int cubeCount = 0;
|
|
|
|
for(int i = 0 ; i < ((m_width >> 3) * (m_depth >> 3) * (m_height >> 3)); i++) {
|
|
for(s32 z = m_chunks[i]->z() ; z < m_chunks[i]->z() + CHUNK_HEIGHT ; z++) {
|
|
for(s32 y = m_chunks[i]->y() ; y < m_chunks[i]->y() + CHUNK_DEPTH ; y++) {
|
|
for(s32 x = m_chunks[i]->x() ; x < m_chunks[i]->x() + CHUNK_WIDTH ; x++) {
|
|
switch(m_map[_MAP_POS(x, y, z)]) {
|
|
case 1:
|
|
m_chunks[i]->addCube(new Cube(x, y, z, m_textures["grass"], 1));
|
|
cubeCount++;
|
|
break;
|
|
case 2:
|
|
m_chunks[i]->addCube(new Cube(x, y, z, m_textures["stone"], 2));
|
|
cubeCount++;
|
|
break;
|
|
case 3:
|
|
m_chunks[i]->addCube(new Cube(x, y, z, m_textures["bedrock"], 3));
|
|
cubeCount++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t time = SDL_GetTicks();
|
|
|
|
for(int i = 0 ; i < ((m_width >> 3) * (m_depth >> 3) * (m_height >> 3)); i++) {
|
|
m_chunks[i]->refreshVBO();
|
|
cout << "Chunks loaded: " << i+1 << "/" << ((m_width >> 3) * (m_depth >> 3) * (m_height >> 3)) << endl;
|
|
}
|
|
|
|
cout << "Chunks loading time: " << (SDL_GetTicks() - time) << " ms for " << cubeCount << " cubes" << endl;
|
|
|
|
currentChunk = findNearestChunk(Game::player->x(), Game::player->y(), Game::player->z());
|
|
|
|
selectedCube = new Cube(-1, -1, -1, m_textures["dirt"], 0);
|
|
}
|
|
|
|
Map::~Map() {
|
|
free(m_map);
|
|
|
|
for(int i = 0 ; i < ((m_width >> 3) * (m_depth >> 3) * (m_height >> 3)); i++) {
|
|
if(m_chunks[i] != NULL) delete m_chunks[i];
|
|
}
|
|
|
|
delete[] m_chunks;
|
|
|
|
delete selectedCube;
|
|
}
|
|
|
|
void Map::draw() {
|
|
glColor3ub(0, 0, 0);
|
|
glBegin(GL_QUADS);
|
|
glVertex3f(0, 0, -0.01);
|
|
glVertex3f(m_width, 0, -0.01);
|
|
glVertex3f(m_width, m_depth, -0.01);
|
|
glVertex3f(0, m_depth, -0.01);
|
|
glEnd();
|
|
glColor3ub(255, 255, 255);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, m_textures["stone"]);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
glEnableClientState(GL_COLOR_ARRAY);
|
|
|
|
currentChunk = findNearestChunk(Game::player->x(), Game::player->y(), Game::player->z());
|
|
|
|
uint32_t time = SDL_GetTicks();
|
|
|
|
for(int i = 0 ; i < ((m_width >> 3) * (m_depth >> 3) * (m_height >> 3)); i++) {
|
|
glPushMatrix();
|
|
glTranslatef(float(m_chunks[i]->x()), float(m_chunks[i]->y()), float(m_chunks[i]->z()));
|
|
m_chunks[i]->render();
|
|
glPopMatrix();
|
|
}
|
|
|
|
time = SDL_GetTicks() - time;
|
|
//cout << "Render time: " << time << " ms" << endl;
|
|
|
|
testCubes();
|
|
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
if ((currentChunk != NULL) && (selectedCube->x() != -1) && (selectedCube->selectedFace() >= 0) && (selectedCube->selectedFace() < 6)) {
|
|
float cubeCoords[6 * 12] = {
|
|
1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, // FR | 0
|
|
1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, // FL | 1
|
|
0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, // BR | 2
|
|
0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, // BL | 3
|
|
0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, // T | 4
|
|
1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0 // B | 5
|
|
};
|
|
|
|
int xx = selectedCube->x();
|
|
int yy = selectedCube->y();
|
|
int zz = selectedCube->z();
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
glColor4ub(255, 255, 255, 64);
|
|
glDisable(GL_ALPHA_TEST);
|
|
glEnable(GL_POLYGON_OFFSET_FILL);
|
|
glPolygonOffset(-1.0, -1.0);
|
|
|
|
glBegin(GL_QUADS);
|
|
glVertex3f(xx + cubeCoords[(selectedCube->selectedFace() * 12) + 0], yy + cubeCoords[(selectedCube->selectedFace() * 12) + 1], zz + cubeCoords[(selectedCube->selectedFace() * 12) + 2]);
|
|
glVertex3f(xx + cubeCoords[(selectedCube->selectedFace() * 12) + 3], yy + cubeCoords[(selectedCube->selectedFace() * 12) + 4], zz + cubeCoords[(selectedCube->selectedFace() * 12) + 5]);
|
|
glVertex3f(xx + cubeCoords[(selectedCube->selectedFace() * 12) + 6], yy + cubeCoords[(selectedCube->selectedFace() * 12) + 7], zz + cubeCoords[(selectedCube->selectedFace() * 12) + 8]);
|
|
glVertex3f(xx + cubeCoords[(selectedCube->selectedFace() * 12) + 9], yy + cubeCoords[(selectedCube->selectedFace() * 12) + 10], zz + cubeCoords[(selectedCube->selectedFace() * 12) + 11]);
|
|
glEnd();
|
|
|
|
glDisable(GL_POLYGON_OFFSET_FILL);
|
|
glEnable(GL_ALPHA_TEST);
|
|
glColor4ub(255, 255, 255, 255);
|
|
}
|
|
}
|
|
|
|
Chunk *Map::findNearestChunk(float x, float y, float z) {
|
|
float distance = FAR;
|
|
Chunk *chunk = NULL;
|
|
|
|
for(int i = 0 ; i < ((m_width >> 3) * (m_depth >> 3) * (m_height >> 3)); i++) {
|
|
vect3D center;
|
|
|
|
center.x = m_chunks[i]->x() + CHUNK_WIDTH / 2;
|
|
center.y = m_chunks[i]->y() + CHUNK_DEPTH / 2;
|
|
center.z = m_chunks[i]->z() + CHUNK_HEIGHT / 2;
|
|
|
|
float d = sqrt(pow(center.x - x, 2) + pow(center.y - y, 2) + pow(center.z - z, 2));
|
|
|
|
if(d < distance) {
|
|
distance = d;
|
|
chunk = m_chunks[i];
|
|
}
|
|
}
|
|
|
|
return chunk;
|
|
}
|
|
|
|
bool Map::intersectionLinePlane(vect3D normal, vect3D planePoint, vect3D lineOrigPoint, vect3D directionVector, float *distance) {
|
|
float p1 = directionVector.x * normal.x + directionVector.y * normal.y + directionVector.z * normal.z; // First point to be tested
|
|
|
|
if(p1 == 0) return false; // Degenerate case
|
|
|
|
vect3D u; // planePoint - lineOrigPoint
|
|
|
|
u.x = planePoint.x - lineOrigPoint.x;
|
|
u.y = planePoint.y - lineOrigPoint.y;
|
|
u.z = planePoint.z - lineOrigPoint.z;
|
|
|
|
float p2 = u.x * normal.x + u.y * normal.y + u.z * normal.z; // Second point to be tested
|
|
|
|
float k = p2 / p1;
|
|
|
|
if((k < 0) || (k > 5)) return false;
|
|
|
|
vect3D i; // Intersection point
|
|
|
|
i.x = lineOrigPoint.x + k * directionVector.x;
|
|
i.y = lineOrigPoint.y + k * directionVector.y;
|
|
i.z = lineOrigPoint.z + k * directionVector.z;
|
|
|
|
vect3D v;
|
|
|
|
v.x = i.x - planePoint.x;
|
|
v.y = i.y - planePoint.y;
|
|
v.z = i.z - planePoint.z;
|
|
|
|
float size = 0.5;
|
|
|
|
if(v.x >= -size && v.x <= size && v.y >= -size && v.y <= size && v.z >= -size && v.z <= size) {
|
|
if(distance != NULL) *distance = k;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Front right = 0 | Front left = 1
|
|
// Back right = 2 | Back left = 3
|
|
// Top = 4 | Bottom = 5
|
|
bool Map::intersectionLineCube(int cubeX, int cubeY, int cubeZ, vect3D lineOrigPoint, vect3D directionVector, float *distance, s8 *face) {
|
|
vect3D planePoint;
|
|
vect3D normal;
|
|
|
|
float k = 0;
|
|
float smallestK = FAR;
|
|
s8 nearestFace = -1;
|
|
bool result = false;
|
|
|
|
// Front right
|
|
planePoint.x = cubeX + 0.5;
|
|
planePoint.y = cubeY + 1;
|
|
planePoint.z = cubeZ + 0.5;
|
|
|
|
normal.x = 0;
|
|
normal.y = 1;
|
|
normal.z = 0;
|
|
|
|
result = intersectionLinePlane(normal, planePoint, lineOrigPoint, directionVector, &k);
|
|
if(result && (smallestK > k)) {
|
|
smallestK = k;
|
|
nearestFace = 0;
|
|
}
|
|
|
|
// Front left
|
|
planePoint.x = cubeX + 1;
|
|
planePoint.y = cubeY + 0.5;
|
|
planePoint.z = cubeZ + 0.5;
|
|
|
|
normal.x = 1;
|
|
normal.y = 0;
|
|
normal.z = 0;
|
|
|
|
result = intersectionLinePlane(normal, planePoint, lineOrigPoint, directionVector, &k);
|
|
if(result && (smallestK > k)) {
|
|
smallestK = k;
|
|
nearestFace = 1;
|
|
}
|
|
|
|
// Back left
|
|
planePoint.x = cubeX + 0.5;
|
|
planePoint.y = cubeY;
|
|
planePoint.z = cubeZ + 0.5;
|
|
|
|
normal.x = 0;
|
|
normal.y = -1;
|
|
normal.z = 0;
|
|
|
|
result = intersectionLinePlane(normal, planePoint, lineOrigPoint, directionVector, &k);
|
|
if(result && (smallestK > k)) {
|
|
smallestK = k;
|
|
nearestFace = 3;
|
|
}
|
|
|
|
// Back right
|
|
planePoint.x = cubeX;
|
|
planePoint.y = cubeY + 0.5;
|
|
planePoint.z = cubeZ + 0.5;
|
|
|
|
normal.x = -1;
|
|
normal.y = 0;
|
|
normal.z = 0;
|
|
|
|
result = intersectionLinePlane(normal, planePoint, lineOrigPoint, directionVector, &k);
|
|
if(result && (smallestK > k)) {
|
|
smallestK = k;
|
|
nearestFace = 2;
|
|
}
|
|
|
|
// Bottom
|
|
planePoint.x = cubeX + 0.5;
|
|
planePoint.y = cubeY + 0.5;
|
|
planePoint.z = cubeZ;
|
|
|
|
normal.x = 0;
|
|
normal.y = 0;
|
|
normal.z = -1;
|
|
|
|
result = intersectionLinePlane(normal, planePoint, lineOrigPoint, directionVector, &k);
|
|
if(result && (smallestK > k)) {
|
|
smallestK = k;
|
|
nearestFace = 5;
|
|
}
|
|
|
|
// Top
|
|
planePoint.x = cubeX + 0.5;
|
|
planePoint.y = cubeY + 0.5;
|
|
planePoint.z = cubeZ + 1;
|
|
|
|
normal.x = 0;
|
|
normal.y = 0;
|
|
normal.z = 1;
|
|
|
|
result = intersectionLinePlane(normal, planePoint, lineOrigPoint, directionVector, &k);
|
|
if(result && (smallestK > k)) {
|
|
smallestK = k;
|
|
nearestFace = 4;
|
|
}
|
|
|
|
if(nearestFace < 0) {
|
|
return false;
|
|
} else {
|
|
if(distance != NULL) *distance = smallestK;
|
|
if(face != NULL) *face = nearestFace;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
void Map::testCubes() {
|
|
vect3D linePoint;
|
|
|
|
linePoint.x = Game::player->x();
|
|
linePoint.y = Game::player->y();
|
|
linePoint.z = Game::player->z();
|
|
|
|
vect3D directionVector;
|
|
|
|
directionVector.x = Game::player->pointTargetedx() - Game::player->x();
|
|
directionVector.y = Game::player->pointTargetedy() - Game::player->y();
|
|
directionVector.z = Game::player->pointTargetedz() - Game::player->z();
|
|
|
|
float distance = FAR;
|
|
Cube *cube = NULL;
|
|
int face = -1;
|
|
Chunk *chunk = NULL;
|
|
unordered_map<int, Cube*> cubes;
|
|
for(unsigned short i = 0 ; i < 7 ; i++) {
|
|
if(i == 6) cubes = currentChunk->cubes();
|
|
else {
|
|
if(currentChunk->surroundingChunks()[i] == NULL) continue;
|
|
cubes = currentChunk->surroundingChunks()[i]->cubes();
|
|
}
|
|
|
|
for(std::unordered_map<int, Cube*>::iterator it = cubes.begin() ; it != cubes.end() ; it++) {
|
|
if(it->second == NULL) continue;
|
|
|
|
it->second->setSelected(false, -1);
|
|
|
|
float d = -1;
|
|
s8 f = -1;
|
|
|
|
bool result = intersectionLineCube(it->second->x(), it->second->y(), it->second->z(), linePoint, directionVector, &d, &f);
|
|
|
|
if(result && (d < distance) && (d < 5)) {
|
|
distance = d;
|
|
cube = it->second;
|
|
face = f;
|
|
if(i == 6) chunk = currentChunk;
|
|
else chunk = currentChunk->surroundingChunks()[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
if(cube != NULL) {
|
|
selectedCube = cube;
|
|
cube->setSelected(true, face);
|
|
face = -1;
|
|
} else {
|
|
selectedCube->setSelected(false, -1);
|
|
}
|
|
|
|
if(chunk != NULL) {
|
|
selectedChunk = chunk;
|
|
} else {
|
|
selectedChunk = NULL;
|
|
}
|
|
}
|
|
|