604 lines
14 KiB
C++
604 lines
14 KiB
C++
#include <GL/glut.h>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <random>
|
|
#include <vector>
|
|
#include <math.h>
|
|
|
|
#include "PerlinNoise.hpp"
|
|
#include "random.h"
|
|
#include "graphics.h"
|
|
#include "blocks.h"
|
|
#include "player.h"
|
|
#include "world.h"
|
|
#include "math.h"
|
|
|
|
//int min_render_distance=50;
|
|
int render_distance_width=40;
|
|
int render_distance_height=10;
|
|
uint64_t sxyz[3];
|
|
Block *world;
|
|
|
|
/*std::vector<double> render_distance_average;
|
|
|
|
void on_early()
|
|
{
|
|
// Increase the render distance
|
|
render_distance += 1;
|
|
}
|
|
|
|
void on_late()
|
|
{
|
|
// Reduce the render distance
|
|
if(render_distance > min_render_distance) {
|
|
render_distance -= 1;
|
|
}
|
|
}
|
|
*/
|
|
void coords_to_int(int *id, int x, int y, int z) {
|
|
*id = z * sxyz[1] * sxyz[0] + y * sxyz[0] + x;
|
|
}
|
|
|
|
void int_to_coords(int id, int *x, int *y, int *z) {
|
|
*x = id % sxyz[0];
|
|
*y = (id/sxyz[0]) % sxyz[1];
|
|
*z = (id/sxyz[0]/sxyz[1]) % sxyz[2];
|
|
}
|
|
|
|
// Say if the block is in the world
|
|
bool block_in_world(int x, int y, int z) {
|
|
return (x >= 0) && (x < sxyz[0]) && (y >= 0) && (y < sxyz[1]) && (z >= 0) && (z < sxyz[2]);
|
|
}
|
|
|
|
// Say if the player is in the world
|
|
bool player_in_world(double x, double y, double z) {
|
|
return (x >= 0) && (x <= sxyz[0]) && (y >= 0) && (y <= sxyz[1]) && (z >= 0) && (z <= sxyz[2]);
|
|
}
|
|
|
|
bool block_is_ramp(double x, double y, double z)
|
|
{
|
|
// Get the id
|
|
int id;
|
|
coords_to_int(&id, x, y, z);
|
|
|
|
// Is the player on a ramp
|
|
if(world[id].type == BTYPE_RAMP)
|
|
{
|
|
// Get the part of the slope the player is on
|
|
double slope_part;
|
|
|
|
// North
|
|
if(world[id].direction == DIRECTION_N)
|
|
{
|
|
slope_part = 1-(z-int(z));
|
|
}
|
|
|
|
// South
|
|
if(world[id].direction == DIRECTION_S)
|
|
{
|
|
slope_part = z-int(z);
|
|
}
|
|
|
|
// East
|
|
if(world[id].direction == DIRECTION_E)
|
|
{
|
|
slope_part = 1-(x-int(x));
|
|
}
|
|
|
|
// West
|
|
if(world[id].direction == DIRECTION_W)
|
|
{
|
|
slope_part = x-int(x);
|
|
}
|
|
|
|
// Is the player above the slope part
|
|
return ((y-int(y)) < slope_part+0.25);
|
|
}
|
|
|
|
// The player isnt on a ramp
|
|
return false;
|
|
}
|
|
|
|
bool block_isnt_solid(double x, double y, double z)
|
|
{
|
|
// Setup some varibles
|
|
bool in_world_check = false;
|
|
double d = 0.25;
|
|
|
|
// Is the player inside the world
|
|
if(player_in_world(x, y, z)) in_world_check = true;
|
|
|
|
// Is the block in range
|
|
if(in_world_check)
|
|
{
|
|
if(block_is_ramp(x, y, z))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Get the ids
|
|
int id[8];
|
|
coords_to_int(&id[0], x+d, y+d, z+d);
|
|
coords_to_int(&id[1], x-d, y+d, z+d);
|
|
coords_to_int(&id[2], x-d, y+d, z-d);
|
|
coords_to_int(&id[3], x+d, y+d, z-d);
|
|
coords_to_int(&id[4], x+d, y-d, z+d);
|
|
coords_to_int(&id[5], x-d, y-d, z+d);
|
|
coords_to_int(&id[6], x-d, y-d, z-d);
|
|
coords_to_int(&id[7], x+d, y-d, z-d);
|
|
|
|
// Loop throgh the ids
|
|
for(int i=0;i<8;i++)
|
|
{
|
|
// Convert the id to coordinates
|
|
int cx, cy, cz;
|
|
int_to_coords(id[i], &cx, &cy, &cz);
|
|
|
|
// Are the coordinates out of range; then return true
|
|
if(cx < 0 || cy < 0 || cz < 0 || cx >= sxyz[0] || cy >= sxyz[1] || cz >= sxyz[2]) {
|
|
return true;
|
|
}
|
|
|
|
// Is the block not in the world
|
|
if(!block_in_world(cx, cy, cz))
|
|
{
|
|
// Return true
|
|
return true;
|
|
}
|
|
|
|
// Return false if this is not air and is a block
|
|
if(
|
|
world[id[i]].id != BLOCK_AIR &&
|
|
world[id[i]].type == BTYPE_BLOCK
|
|
) return false;
|
|
}
|
|
|
|
// Return true
|
|
return true;
|
|
}
|
|
|
|
// Return true
|
|
return true;
|
|
}
|
|
|
|
bool block_is_fluid(double x, double y, double z)
|
|
{
|
|
// Get the fluid motion
|
|
double fluid_motion = get_fluid_motion(x, z);
|
|
|
|
// Get the id
|
|
int id;
|
|
coords_to_int(&id, x, y+1-fluid_motion, z);
|
|
|
|
// Get the above id
|
|
int a_id;
|
|
coords_to_int(&a_id, x, y+2-fluid_motion, z);
|
|
|
|
// Is the player in a fluid
|
|
if(world[id].type == BTYPE_FLUID)
|
|
{
|
|
// Submurged under the water
|
|
if(world[a_id].type == BTYPE_FLUID)
|
|
{
|
|
// Return true
|
|
return true;
|
|
}
|
|
|
|
// Is the decimal height greater than the block height and the fluid motion
|
|
return y < int(y) + fluid_motion;
|
|
}
|
|
|
|
// Return false
|
|
return false;
|
|
}
|
|
|
|
int relative_block(int id, int x, int y, int z)
|
|
{
|
|
// Get the position of the current block
|
|
int cx, cy, cz;
|
|
int_to_coords(id, &cx, &cy, &cz);
|
|
|
|
// Add the relative coordinates
|
|
cx += x;
|
|
cy += y;
|
|
cz += z;
|
|
|
|
// Is this valid
|
|
if(cx < 0) return -1;
|
|
if(cy < 0) return -1;
|
|
if(cz < 0) return -1;
|
|
if(cx >= sxyz[0]) return -1;
|
|
if(cy >= sxyz[1]) return -1;
|
|
if(cz >= sxyz[2]) return -1;
|
|
|
|
// Get the id
|
|
int cid;
|
|
coords_to_int(&cid, cx, cy, cz);
|
|
|
|
// Return the block id
|
|
return cid;
|
|
}
|
|
|
|
bool relative_block_optimise(int id, int x, int y, int z, bool check)
|
|
{
|
|
// Is the check varible already false
|
|
if(!check) return false;
|
|
|
|
// Get the id of the relative block
|
|
int rb = relative_block(id, x, y, z);
|
|
|
|
// Is the result out of range (-1)
|
|
if(rb == -1) return false;
|
|
|
|
if(!(
|
|
// Is not air
|
|
world[rb].id != BLOCK_AIR &&
|
|
(
|
|
// Is the same type as the other block
|
|
world[rb].type == world[id].type ||
|
|
|
|
// Is the other block a block
|
|
world[rb].type == BTYPE_BLOCK
|
|
)
|
|
)) return true;
|
|
|
|
return (
|
|
(!blockGetSettings(world[id].id).transparent) &&
|
|
blockGetSettings(world[rb].id).transparent
|
|
);
|
|
|
|
/*// Check the angle of the face and the player
|
|
|
|
// Get the blocks coordinates
|
|
int cx, cy, cz;
|
|
int_to_coords(id, &cx, &cy, &cz);
|
|
double a;
|
|
|
|
if(x != 0 || z != 0)
|
|
{
|
|
a = atan2(cx-player_pos[0], cz-player_pos[2])*180.0/PI
|
|
}
|
|
|
|
// North / South
|
|
if(x != 0)
|
|
{
|
|
// Get the angle
|
|
a -= player_angle[0];
|
|
a *= x;
|
|
}
|
|
|
|
// East / West
|
|
if(z != 0)
|
|
{
|
|
// Get the angle
|
|
a = ;
|
|
a -= player_angle[0] - 90;
|
|
a *= z;
|
|
}
|
|
|
|
// Up / Down
|
|
if(y != 0)
|
|
{
|
|
// Get the angle
|
|
a = atan2(cy-player_pos[1], cz-player_pos[2])*180.0/PI;
|
|
a -= player_angle[0];
|
|
a *= y;
|
|
}
|
|
|
|
// Fix the angle
|
|
while(a < 0) a += 360;
|
|
while(a > 360) a -= 360;
|
|
|
|
return a > 0 && a < 180;*/
|
|
}
|
|
|
|
void world_load(const char *dir)
|
|
{
|
|
/*// Open the file
|
|
std::ifstream file(dir, std::ios::binary);
|
|
|
|
// Load the map size
|
|
file.read((char*)&sxyz[0], sizeof(uint64_t));
|
|
file.read((char*)&sxyz[1], sizeof(uint64_t));
|
|
file.read((char*)&sxyz[2], sizeof(uint64_t));
|
|
|
|
// Setup the world varible
|
|
world = new World[sxyz[0]*sxyz[1]*sxyz[2]];
|
|
|
|
// Read the world data
|
|
file.read((char*)world, sizeof(World)*sxyz[0]*sxyz[1]*sxyz[2]);
|
|
|
|
// Close the file
|
|
file.close();*/
|
|
}
|
|
|
|
void player_respawn()
|
|
{
|
|
// Set the player to the middle and at the top of the map
|
|
player_pos[0] = sxyz[0]/2;
|
|
player_pos[1] = sxyz[1]-2;
|
|
player_pos[2] = sxyz[2]/2;
|
|
}
|
|
|
|
void world_init()
|
|
{
|
|
// Initialse the blocks
|
|
blocks_init();
|
|
|
|
sxyz[0] = 256;
|
|
sxyz[1] = 120;
|
|
sxyz[2] = 256;
|
|
|
|
// Set the players spawn point
|
|
player_respawn();
|
|
|
|
// Setup the world varible
|
|
world = new Block[sxyz[0]*sxyz[1]*sxyz[2]];
|
|
|
|
// Zero out the world varible
|
|
zero_out_bytes((char*)world, sizeof(Block)*sxyz[0]*sxyz[1]*sxyz[2]);
|
|
|
|
// Set the seed
|
|
const siv::PerlinNoise perlin(random_get_seed());
|
|
|
|
// Set the frequency
|
|
double frequency = 1;
|
|
double xf = sxyz[0] / frequency;
|
|
double zf = sxyz[2] / frequency;
|
|
|
|
// Loop over x
|
|
for(int x=0;x<sxyz[0];x++)
|
|
{
|
|
// Loop over z
|
|
for(int z=0;z<sxyz[2];z++)
|
|
{
|
|
// Get some perlin noise
|
|
double f = 128;
|
|
int noise = perlin.octaveNoise0_1(x / f, z / f, 8)*100;
|
|
|
|
// Go up the world
|
|
for(int y=0;y<sxyz[1];y++)
|
|
{
|
|
// Get the id
|
|
int id;
|
|
coords_to_int(&id, x, y, z);
|
|
|
|
// Edge of the world
|
|
if(x == 0 || z == 0 || x == sxyz[0]-1 || z == sxyz[2]-1 || y == 0 || y == sxyz[1]-1)
|
|
{
|
|
// Set the block to a barrier
|
|
world[id] = make_block(BLOCK_BARRIER);
|
|
}
|
|
|
|
else
|
|
{
|
|
// 2 blocks under the ground
|
|
if(y < noise-2)
|
|
{
|
|
// Make stone
|
|
world[id] = make_block(BLOCK_STONE);
|
|
}
|
|
|
|
// Under the ground
|
|
else if(y < noise)
|
|
{
|
|
// Make dirt
|
|
world[id] = make_block(BLOCK_DIRT);
|
|
}
|
|
|
|
// At ground level
|
|
else if(y == noise && y >= 60)
|
|
{
|
|
// Make grass
|
|
world[id] = make_block(BLOCK_GRASS);
|
|
}
|
|
|
|
else if(y == noise && y < 60)
|
|
{
|
|
// Make grass
|
|
world[id] = make_block(BLOCK_SAND);
|
|
}
|
|
|
|
// Water level
|
|
else if(y > noise && y < 60)
|
|
{
|
|
// Make water
|
|
world[id] = make_block(BLOCK_WATER, BTYPE_FLUID);
|
|
}
|
|
|
|
// 1 block above ground
|
|
else if(y == noise+1)
|
|
{
|
|
// Get some perlin noise
|
|
double grass_noise = perlin.octaveNoise0_1(x / 10.0, z / 10.0, 10);
|
|
|
|
// Over a certain threshold
|
|
if(grass_noise > 0.6)
|
|
{
|
|
// Make grass plant
|
|
world[id] = make_block(BLOCK_GRASS_PLANT, BTYPE_PLANT);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
|
|
else if(y < 2)
|
|
{
|
|
world[id] = make_block(BLOCK_DIRT);
|
|
}
|
|
|
|
else if(y == 2)
|
|
{
|
|
world[id] = make_block(BLOCK_GRASS);
|
|
}
|
|
|
|
else if(x > 5 && y < z-100)
|
|
{
|
|
world[id] = make_block(BLOCK_DIRT);
|
|
}
|
|
|
|
else if(x > 5 && y == z-100)
|
|
{
|
|
world[id] = make_block(BLOCK_GRASS, BTYPE_RAMP, DIRECTION_S);
|
|
}
|
|
*/
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void render_coord(int x, int y, int z, int a1, int a2)
|
|
{
|
|
// Is this in range
|
|
if(block_in_world(x, y, z))
|
|
{
|
|
// Get the id
|
|
int id;
|
|
coords_to_int(&id, x, y, z);
|
|
int b = 60;
|
|
|
|
// Is this not air
|
|
if(world[id].id != BLOCK_AIR)
|
|
{
|
|
// Setup a Block Face Map
|
|
BlockFaceMap fm;
|
|
fm.n = relative_block_optimise(id, 0, 0,-1, a1 > 90 -b && a1 < 270+b || (a2 < -40 || a2 > 40));
|
|
fm.s = relative_block_optimise(id, 0, 0, 1, a1 > 270-b || a1 < 90 +b || (a2 < -40 || a2 > 40));
|
|
fm.e = relative_block_optimise(id, 1, 0, 0, a1 < 0 +b || a1 > 180-b || (a2 < -40 || a2 > 40));
|
|
fm.w = relative_block_optimise(id,-1, 0, 0, a1 < 180+b || a1 > 360-b || (a2 < -40 || a2 > 40));
|
|
fm.t = relative_block_optimise(id, 0, 1, 0, a2 > 0-b || world[id].type == BTYPE_FLUID);
|
|
fm.b = relative_block_optimise(id, 0,-1, 0, a2 < 0+b);
|
|
|
|
// Render the block
|
|
render_block(world[id], x, y, z, fm);
|
|
}
|
|
}
|
|
}
|
|
|
|
void world_render_block_inside_player()
|
|
{
|
|
// Bind the texture
|
|
bind_block_texture();
|
|
|
|
// Get the id
|
|
int id;
|
|
coords_to_int(&id, player_pos[0], player_pos[1], player_pos[2]);
|
|
|
|
// Render the block face infront of the player
|
|
render_block_face_here(world[id]);
|
|
}
|
|
|
|
void world_render()
|
|
{
|
|
// Set the colour to maximum opacity and colour
|
|
glColor4f(1.0f,1.0f,1.0f,1.0f);
|
|
|
|
// Bind the texture
|
|
bind_block_texture();
|
|
|
|
// Get the cameras angles
|
|
int a1 = (int)player_angle[0] % 360;
|
|
int a2 = (int)player_angle[1] % 180;
|
|
|
|
// Set the cutof render angles
|
|
int g1 = 80;
|
|
int g2 = 40;
|
|
|
|
// Render everything inwards
|
|
|
|
for(int x=render_distance_width;x>0;x-=1) {
|
|
for(int y=render_distance_height;y>0;y-=1) {
|
|
for(int z=render_distance_width;z>0;z-=1)
|
|
{
|
|
// Render the blocks
|
|
if((a1 < 180 +g1 && a1 > 90 -g1) || (a2 < 0 -g2)) render_coord(player_pos[0]+x, player_pos[1]+y, player_pos[2]+z, a1, a2);
|
|
if((a1 < 270 +g1 && a1 > 180 -g1) || (a2 < 0 -g2)) render_coord(player_pos[0]-x, player_pos[1]+y, player_pos[2]+z, a1, a2);
|
|
if((a1 < 0 +g1 || a1 > 270 -g1) || (a2 < 0 -g2)) render_coord(player_pos[0]-x, player_pos[1]+y, player_pos[2]-z, a1, a2);
|
|
if((a1 < 90 +g1 || a1 > 360 -g1) || (a2 < 0 -g2)) render_coord(player_pos[0]+x, player_pos[1]+y, player_pos[2]-z, a1, a2);
|
|
if((a1 < 180 +g1 && a1 > 90 -g1) || (a2 > 0 +g2)) render_coord(player_pos[0]+x, player_pos[1]-y, player_pos[2]+z, a1, a2);
|
|
if((a1 < 270 +g1 && a1 > 180 -g1) || (a2 > 0 +g2)) render_coord(player_pos[0]-x, player_pos[1]-y, player_pos[2]+z, a1, a2);
|
|
if((a1 < 0 +g1 || a1 > 270 -g1) || (a2 > 0 +g2)) render_coord(player_pos[0]-x, player_pos[1]-y, player_pos[2]-z, a1, a2);
|
|
if((a1 < 90 +g1 || a1 > 360 -g1) || (a2 > 0 +g2)) render_coord(player_pos[0]+x, player_pos[1]-y, player_pos[2]-z, a1, a2);
|
|
}
|
|
}
|
|
}
|
|
|
|
for(int x=render_distance_width;x>0;x-=1) {
|
|
for(int z=render_distance_width;z>0;z-=1)
|
|
{
|
|
// Render the coordinates
|
|
render_coord(player_pos[0]+x, player_pos[1], player_pos[2]+z, a1, a2);
|
|
render_coord(player_pos[0]+x, player_pos[1], player_pos[2]-z, a1, a2);
|
|
render_coord(player_pos[0]-x, player_pos[1], player_pos[2]-z, a1, a2);
|
|
render_coord(player_pos[0]-x, player_pos[1], player_pos[2]+z, a1, a2);
|
|
}
|
|
}
|
|
|
|
for(int x=render_distance_width;x>0;x-=1) {
|
|
for(int y=render_distance_height;y>0;y-=1)
|
|
{
|
|
// Render the coordinates
|
|
render_coord(player_pos[0]+x, player_pos[1]+y, player_pos[2], a1, a2);
|
|
render_coord(player_pos[0]+x, player_pos[1]-y, player_pos[2], a1, a2);
|
|
render_coord(player_pos[0]-x, player_pos[1]-y, player_pos[2], a1, a2);
|
|
render_coord(player_pos[0]-x, player_pos[1]+y, player_pos[2], a1, a2);
|
|
}
|
|
}
|
|
|
|
for(int z=render_distance_width;z>0;z-=1) {
|
|
for(int y=render_distance_height;y>0;y-=1)
|
|
{
|
|
// Render the coordinates
|
|
render_coord(player_pos[0], player_pos[1]+y, player_pos[2]+z, a1, a2);
|
|
render_coord(player_pos[0], player_pos[1]+y, player_pos[2]-z, a1, a2);
|
|
render_coord(player_pos[0], player_pos[1]-y, player_pos[2]-z, a1, a2);
|
|
render_coord(player_pos[0], player_pos[1]-y, player_pos[2]+z, a1, a2);
|
|
}
|
|
}
|
|
|
|
for(int x=render_distance_width;x>0;x-=1)
|
|
{
|
|
// Render the coordinates
|
|
render_coord(player_pos[0]+x, player_pos[1], player_pos[2], a1, a2);
|
|
render_coord(player_pos[0]-x, player_pos[1], player_pos[2], a1, a2);
|
|
}
|
|
|
|
for(int y=render_distance_height;y>0;y-=1)
|
|
{
|
|
// Render the coordinates
|
|
render_coord(player_pos[0], player_pos[1]+y, player_pos[2], a1, a2);
|
|
render_coord(player_pos[0], player_pos[1]-y, player_pos[2], a1, a2);
|
|
}
|
|
|
|
for(int z=render_distance_width;z>0;z-=1)
|
|
{
|
|
// Render the coordinates
|
|
render_coord(player_pos[0], player_pos[1], player_pos[2]+z, a1, a2);
|
|
render_coord(player_pos[0], player_pos[1], player_pos[2]-z, a1, a2);
|
|
}
|
|
|
|
// Render a coordinate where the player is standing
|
|
render_coord(player_pos[0], player_pos[1], player_pos[2], a1, a2);
|
|
|
|
// Unbind the texture
|
|
unbind_block_texture();
|
|
|
|
// Enable blending
|
|
//glEnable(GL_BLEND);
|
|
}
|
|
|
|
void set_block_at(int x, int y, int z, uint8_t id, uint8_t type, uint8_t direction)
|
|
{
|
|
// Get the id
|
|
int w_id;
|
|
coords_to_int(&w_id, x, y, z);
|
|
|
|
// Get the block
|
|
Block b = make_block(id, type, direction);
|
|
|
|
// Set the block
|
|
world[w_id] = b;
|
|
}
|