Init commit
commit
3c0ae6f49e
|
@ -0,0 +1,36 @@
|
|||
#include "AABB.hpp"
|
||||
#include <memory>
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
AABB::AABB(const glm::vec3 &vec1, const glm::vec3 &vec2) {
|
||||
set(vec1, vec2);
|
||||
}
|
||||
|
||||
void AABB::set(const glm::vec3 &vec1, const glm::vec3 &vec2) {
|
||||
v1 = vec1;
|
||||
v2 = vec2;
|
||||
if (v1.x > v2.x)
|
||||
std::swap(v1.x, v2.x);
|
||||
if (v1.y > v2.y)
|
||||
std::swap(v1.y, v2.y);
|
||||
if (v1.z > v2.z)
|
||||
std::swap(v1.z, v2.z);
|
||||
}
|
||||
|
||||
bool AABB::intersects(const glm::vec3 &point) const {
|
||||
if (point.x > v1.x && point.x < v2.x &&
|
||||
point.y > v1.y && point.y < v2.y &&
|
||||
point.z > v1.z && point.z < v2.z)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AABB::intersects(const AABB &other) const {
|
||||
bool xOverlap = !(other.v1.x > v2.x || other.v2.x < v1.x);
|
||||
bool yOverlap = !(other.v1.y > v2.y || other.v2.y < v1.y);
|
||||
bool zOverlap = !(other.v1.z > v2.z || other.v2.z < v1.z);
|
||||
return xOverlap && yOverlap && zOverlap;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef AABOX_HPP
|
||||
#define AABOX_HPP
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class AABB {
|
||||
public:
|
||||
glm::vec3 v1, v2;
|
||||
AABB(const glm::vec3 &vec1 = glm::vec3(), const glm::vec3 &vec2 = glm::vec3());
|
||||
void set(const glm::vec3 &vec1, const glm::vec3 &vec2);
|
||||
bool intersects(const glm::vec3 &point) const;
|
||||
bool intersects(const AABB &other) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,56 @@
|
|||
#ifndef ARR3_UTILS_HPP
|
||||
#define ARR3_UTILS_HPP
|
||||
#include <type_traits>
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
template <typename T> class Arr3 {
|
||||
private:
|
||||
T*** array;
|
||||
int x, y, z;
|
||||
public:
|
||||
Arr3(int x, int y, int z) : x(x), y(y), z(z) {
|
||||
array = new T**[x];
|
||||
for (int i = 0; i < x; i++) {
|
||||
array[i] = new T*[y];
|
||||
for (int j = 0; j < y; j++) {
|
||||
array[i][j] = new T[z];
|
||||
/* std::enable_if<std::is_pointer<T>::value> {
|
||||
for (int k = 0; k < z; k++) {
|
||||
array[i][j][k] = nullptr;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
~Arr3() {
|
||||
for(int i = 0; i < x; i++) {
|
||||
for(int j = 0; j < y; j++) {
|
||||
/* std::enable_if<std::is_pointer<T>::value> {
|
||||
for(int k = 0; k < z; k++) {
|
||||
if (array[x][y][z]) {
|
||||
delete array[x][y][z];
|
||||
}
|
||||
}
|
||||
}*/
|
||||
delete[] array[i][j];
|
||||
}
|
||||
delete[] array[i];
|
||||
}
|
||||
delete[] array;
|
||||
}
|
||||
T** operator[](std::size_t idx) const {
|
||||
return array[idx];
|
||||
}
|
||||
int getX() const { return x; }
|
||||
int getY() const { return y; }
|
||||
int getZ() const { return z; }
|
||||
bool sizeEqual(const Arr3 &r) const { return x == r.x && y == r.y && z == r.z; }
|
||||
};
|
||||
|
||||
typedef Arr3<float> Arr3f;
|
||||
typedef Arr3<int> Arr3i;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,112 @@
|
|||
#include "AtlasCreator.hpp"
|
||||
#include "stb_image.h"
|
||||
//#include <chrono>
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
AtlasCreator::AtlasCreator(int w, int h, int uw, int uh) : atlasWidth(w), atlasHeight(h), unitWidth(uw), unitHeight(uh), lastX(0), lastY(0) {
|
||||
if (w < 1 || h < 1 || uw < 1 || uh < 1 || uw > w || uh > h)
|
||||
throw std::invalid_argument("Bad dimensions");
|
||||
if (fmod((1 << (sizeof(Coord::x)*8))/(float)atlasWidth, 1) != 0 ||
|
||||
fmod((1 << (sizeof(Coord::y)*8))/(float)atlasHeight, 1) != 0)
|
||||
throw std::invalid_argument("Atlas W/H is not divisor of Coord's type");
|
||||
|
||||
atlasData = new uint8[w * h * 4];
|
||||
std::fill_n(atlasData, w * h * 4, static_cast<uint8>(0));
|
||||
}
|
||||
|
||||
AtlasCreator::Coord AtlasCreator::add(const std::string& path) {
|
||||
// Load image
|
||||
int width, height, channels;
|
||||
unsigned char *ptr = stbi_load(path.c_str(), &width, &height, &channels, STBI_rgb_alpha);
|
||||
if (!ptr || !width || !height) {
|
||||
const char *ptr =
|
||||
"\377\0\0\377\377\0\0\377\377\0\0\377\377\0\0\377\377\0\0\377\377\0\0\377"
|
||||
"\377\0\0\377\377\0\0\377\377\0\0\377\210\210\210\377\210\210\210\377\36\377"
|
||||
"\0\377\36\377\0\377\210\210\210\377\210\210\210\377\377\0\0\377\377\0\0\377"
|
||||
"\210\210\210\377\36\377\0\377\210\210\210\377\210\210\210\377\36\377\0\377"
|
||||
"\210\210\210\377\377\0\0\377\377\0\0\377\210\210\210\377\210\210\210\377"
|
||||
"\210\210\210\377\36\377\0\377\210\210\210\377\210\210\210\377\377\0\0\377"
|
||||
"\377\0\0\377\210\210\210\377\210\210\210\377\36\377\0\377\210\210\210\377"
|
||||
"\210\210\210\377\210\210\210\377\377\0\0\377\377\0\0\377\210\210\210\377"
|
||||
"\210\210\210\377\210\210\210\377\210\210\210\377\210\210\210\377\210\210"
|
||||
"\210\377\377\0\0\377\377\0\0\377\210\210\210\377\210\210\210\377\36\377\0"
|
||||
"\377\210\210\210\377\210\210\210\377\210\210\210\377\377\0\0\377\377\0\0"
|
||||
"\377\377\0\0\377\377\0\0\377\377\0\0\377\377\0\0\377\377\0\0\377\377\0\0"
|
||||
"\377\377\0\0\377"; // Made in 3 seconds under GIMP
|
||||
return add(8, 8, 4, (const uint8*)ptr);
|
||||
}
|
||||
if (width > unitWidth || height > unitHeight || width % 4 != 0 || height % 4 != 0) {
|
||||
getDebugStream() << path << " is bad: " << width << 'x' << height << std::endl;
|
||||
stbi_image_free(ptr);
|
||||
return Coord { 0, 0, 0, 0 };
|
||||
}
|
||||
|
||||
Coord result = add(width, height, channels, ptr);
|
||||
|
||||
// Free the image buffer
|
||||
stbi_image_free(ptr);
|
||||
return result;
|
||||
}
|
||||
|
||||
AtlasCreator::Coord AtlasCreator::add(int width, int height, int channels, const uint8 *data) {
|
||||
// Find a good coord
|
||||
uint16 targetX, targetY;
|
||||
if (lastX + unitWidth > atlasWidth) {
|
||||
targetX = 0;
|
||||
targetY = lastY + unitHeight;
|
||||
} else {
|
||||
targetX = lastX;
|
||||
targetY = lastY;
|
||||
}
|
||||
|
||||
//auto t1 = std::chrono::high_resolution_clock::now();
|
||||
// Blit the texture onto the atlas
|
||||
for(int sourceY = 0; sourceY < height; ++sourceY) {
|
||||
int fromPad = sourceY * width;
|
||||
int toPad = (targetY + sourceY) * atlasWidth;
|
||||
for(int sourceX = 0; sourceX < width; sourceX += 2) {
|
||||
int from = (fromPad + sourceX) * 4;
|
||||
int to = (toPad + (targetX + sourceX)) * 4;
|
||||
|
||||
// MMX-like copy, fast (actual MMX/SSE would be f'kin fast)
|
||||
#if HAS_NATIVE_64BIT
|
||||
*((int64*)(&atlasData[to])) = *((int64*)(&data[from]));
|
||||
#else
|
||||
*((int32*)(&atlasData[to])) = *((int32*)(&data[from]));
|
||||
*((int32*)(&atlasData[to + 4])) = *((int32*)(&data[from + 4]));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
//auto t2 = std::chrono::high_resolution_clock::now();
|
||||
//getDebugStream() << std::chrono::duration_cast<std::chrono::microseconds>(t2-t1).count() << std::endl;
|
||||
|
||||
lastX = targetX + unitWidth;
|
||||
lastY = targetY;
|
||||
uint glScaleX = (1 << (sizeof(Coord::x)*8))/atlasWidth,
|
||||
glScaleY = (1 << (sizeof(Coord::y)*8))/atlasHeight;
|
||||
//getDebugStream() << width << 'x' << height << '@' << targetX << ',' << targetY << " * " << glScaleX << ',' << glScaleY << std::endl;
|
||||
|
||||
Coord c = {
|
||||
targetX*glScaleX,
|
||||
targetY*glScaleY,
|
||||
(targetX + width)*glScaleX-1,
|
||||
(targetY + height)*glScaleY-1,
|
||||
};
|
||||
//getDebugStream() << '{' << c.x << ',' << c.y << ';' << c.u << ',' << c.v << '}' << std::endl;
|
||||
//getDebugStream() << '{' << targetX << ',' << targetY << "→" << (targetX + width) << ',' << (targetY + height) << '}' << std::endl;
|
||||
return c;
|
||||
}
|
||||
|
||||
Texture* AtlasCreator::getAtlas() {
|
||||
return new Texture(atlasWidth, atlasHeight, atlasData, Texture::PixelFormat::RGBA);
|
||||
}
|
||||
|
||||
AtlasCreator::~AtlasCreator() {
|
||||
delete[] atlasData;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef ATLAS_CREATOR_HPP
|
||||
#define ATLAS_CREATOR_HPP
|
||||
#include "Platform.hpp"
|
||||
#include "Texture.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class AtlasCreator {
|
||||
public:
|
||||
struct Coord {
|
||||
uint16 x, y, u, v;
|
||||
};
|
||||
int atlasWidth, atlasHeight,
|
||||
unitWidth, unitHeight;
|
||||
int lastX, lastY;
|
||||
|
||||
private:
|
||||
uint8 *atlasData;
|
||||
|
||||
// No copy
|
||||
AtlasCreator(const AtlasCreator&) = delete;
|
||||
AtlasCreator& operator=(const AtlasCreator&) = delete;
|
||||
|
||||
public:
|
||||
AtlasCreator(int w = 256, int h = 256, int uw = 64, int uh = 64);
|
||||
Coord add(const std::string &path);
|
||||
Coord add(int width, int height, int channels, const uint8* data);
|
||||
Texture* getAtlas();
|
||||
~AtlasCreator();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,149 @@
|
|||
#include "Audio.hpp"
|
||||
#include <AL/al.h>
|
||||
#include <AL/alext.h>
|
||||
#include "Game.hpp"
|
||||
#include "Sound.hpp"
|
||||
#include "GlobalProperties.hpp"
|
||||
|
||||
#define AUDIO_GC_DEBUG 0
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
Audio::Audio(Game* G) : G(G), sounds(m_sounds) {
|
||||
if (!GlobalProperties::IsSoundEnabled)
|
||||
return;
|
||||
|
||||
int alMajor, alMinor;
|
||||
alcGetIntegerv(nullptr, ALC_MAJOR_VERSION, 1, &alMajor);
|
||||
alcGetIntegerv(nullptr, ALC_MINOR_VERSION, 1, &alMinor);
|
||||
getDebugStream() << "OpenAL v" << alMajor << '.' << alMinor << std::endl;
|
||||
|
||||
ALboolean enumeration = alcIsExtensionPresent(nullptr, "ALC_ENUMERATION_EXT");
|
||||
if (enumeration == AL_FALSE)
|
||||
; // enumeration not supported
|
||||
else {
|
||||
const ALCchar *devices = alcGetString(nullptr, ALC_DEVICE_SPECIFIER);
|
||||
const ALCchar *device = devices, *next = devices + 1;
|
||||
size_t len = 0;
|
||||
while (device && *device != '\0' && next && *next != '\0') {
|
||||
len = strlen(device);
|
||||
device += (len + 1);
|
||||
next += (len + 2);
|
||||
}
|
||||
}
|
||||
const ALCchar *deviceName = nullptr;
|
||||
m_audioDevice = alcOpenDevice(deviceName);
|
||||
if (!m_audioDevice) {
|
||||
getDebugStream() << "Failed opening AL device '" << deviceName << "': " << alcGetError(m_audioDevice) << std::endl;
|
||||
GlobalProperties::IsSoundEnabled = false;
|
||||
}
|
||||
deviceName = alcGetString(m_audioDevice, ALC_DEVICE_SPECIFIER);
|
||||
getDebugStream() << "Using device '" << deviceName << '\'' << std::endl;
|
||||
|
||||
ALCint attrs[] = {
|
||||
0, 0
|
||||
};
|
||||
|
||||
m_audioContext = alcCreateContext(m_audioDevice, attrs);
|
||||
if (!alcMakeContextCurrent(m_audioContext)) {
|
||||
getDebugStream() << "Failed setting context on AL device '" << deviceName << "': " << alcGetError(m_audioDevice) << std::endl;
|
||||
alcCloseDevice(m_audioDevice);
|
||||
GlobalProperties::IsSoundEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
Audio::~Audio() {
|
||||
if (!GlobalProperties::IsSoundEnabled)
|
||||
return;
|
||||
|
||||
alcMakeContextCurrent(nullptr);
|
||||
alcDestroyContext(m_audioContext);
|
||||
alcCloseDevice(m_audioDevice);
|
||||
}
|
||||
|
||||
static ALfloat float3data[3], plrOrient[6];
|
||||
|
||||
void Audio::updateAngle() {
|
||||
const glm::vec3 &at = G->LP->camera.getLookAt(),
|
||||
&up = G->LP->camera.getUp();
|
||||
plrOrient[0] = at.x;
|
||||
plrOrient[1] = at.y;
|
||||
plrOrient[2] = at.z;
|
||||
plrOrient[3] = up.x;
|
||||
plrOrient[4] = up.y;
|
||||
plrOrient[5] = up.z;
|
||||
alListenerfv(AL_ORIENTATION, plrOrient);
|
||||
}
|
||||
|
||||
void Audio::updatePos() {
|
||||
float3data[0] = G->LP->position.x;
|
||||
float3data[1] = G->LP->position.y;
|
||||
float3data[2] = G->LP->position.z;
|
||||
alListenerfv(AL_POSITION, float3data);
|
||||
float3data[0] = G->LP->velocity.x;
|
||||
float3data[1] = G->LP->velocity.y;
|
||||
float3data[2] = G->LP->velocity.z;
|
||||
alListenerfv(AL_VELOCITY, float3data);
|
||||
}
|
||||
|
||||
void Audio::update() {
|
||||
updatePos();
|
||||
updateAngle();
|
||||
}
|
||||
|
||||
void Audio::loadSoundAssets() {
|
||||
std::string soundsDir = getAssetsDirectory() + "sounds/";
|
||||
for (const std::string &fn : fs::getFiles(soundsDir)) {
|
||||
if (fn.length() >= 4 && fn.substr(fn.length()-4) == ".ogg") {
|
||||
addSound(fn.substr(0, fn.length()-4), soundsDir+fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Audio::addSound(const std::string& name, const std::string& path) {
|
||||
m_sounds.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(name), std::forward_as_tuple()).first->second.loadOgg(path);
|
||||
}
|
||||
|
||||
void Audio::playSound(const std::string &name) {
|
||||
playSound(m_sounds.at(name));
|
||||
}
|
||||
|
||||
void Audio::playSound(const SoundBuffer &buf) {
|
||||
m_playing.emplace_back(&buf);
|
||||
m_playing.back().play();
|
||||
gc();
|
||||
}
|
||||
|
||||
void Audio::playSound(const std::string &name, const glm::vec3 &pos) {
|
||||
playSound(m_sounds.at(name), pos);
|
||||
}
|
||||
|
||||
void Audio::playSound(const SoundBuffer &buf, const glm::vec3& pos) {
|
||||
m_playing.emplace_back(&buf, false, pos);
|
||||
m_playing.back().play();
|
||||
gc();
|
||||
}
|
||||
|
||||
/// Garbage Collects the sound sources
|
||||
void Audio::gc() {
|
||||
#if AUDIO_GC_DEBUG
|
||||
uint freed = 0;
|
||||
#endif
|
||||
std::list<Sound>::const_iterator it = m_playing.begin();
|
||||
while (it != m_playing.end()) {
|
||||
if (!it->isPlaying()) {
|
||||
it = m_playing.erase(it);
|
||||
#if AUDIO_GC_DEBUG
|
||||
freed++;
|
||||
#endif
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
#if AUDIO_GC_DEBUG
|
||||
getDebugStream() << "GC'd " << freed << " sources" << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
#ifndef AUDIO_HPP
|
||||
#define AUDIO_HPP
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <glm/detail/type_vec3.hpp>
|
||||
#include <AL/alc.h>
|
||||
#include "Sound.hpp"
|
||||
#include "SoundBuffer.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class Game;
|
||||
|
||||
class Audio {
|
||||
private:
|
||||
Game *G;
|
||||
std::map<std::string, SoundBuffer> m_sounds;
|
||||
std::list<Sound> m_playing;
|
||||
void gc();
|
||||
ALCdevice *m_audioDevice;
|
||||
ALCcontext *m_audioContext;
|
||||
|
||||
public:
|
||||
Audio(Game *G);
|
||||
~Audio();
|
||||
|
||||
const std::map<std::string, SoundBuffer> &sounds;
|
||||
|
||||
void loadSoundAssets();
|
||||
|
||||
void addSound(const std::string &name, const std::string &path);
|
||||
void updatePos();
|
||||
void updateAngle();
|
||||
// Runs UpdatePos and UpdateAngle
|
||||
void update();
|
||||
void playSound(const std::string &name);
|
||||
void playSound(const SoundBuffer &buf);
|
||||
void playSound(const std::string &name, const glm::vec3 &pos);
|
||||
void playSound(const SoundBuffer &buf, const glm::vec3 &pos);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,189 @@
|
|||
#include "Blocks.hpp"
|
||||
#include "GlobalProperties.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
BlockTex sideTextures[(int)BlockType::LAST][6] = {
|
||||
/* Air */ {BlockTex::None, BlockTex::None, BlockTex::None, BlockTex::None, BlockTex::None, BlockTex::None},
|
||||
/* Dirt */ {BlockTex::Dirt, BlockTex::Dirt, BlockTex::Dirt, BlockTex::Dirt, BlockTex::Dirt, BlockTex::Dirt},
|
||||
/* Ore */ {BlockTex::Ore, BlockTex::Ore, BlockTex::Ore, BlockTex::Ore, BlockTex::Ore, BlockTex::Ore},
|
||||
/* Gold */ {BlockTex::Gold, BlockTex::Gold, BlockTex::Gold, BlockTex::Gold, BlockTex::Gold, BlockTex::Gold},
|
||||
/*Diamond*/ {BlockTex::Diamond, BlockTex::Diamond, BlockTex::Diamond, BlockTex::Diamond, BlockTex::Diamond, BlockTex::Diamond},
|
||||
/* Rock */ {BlockTex::Rock, BlockTex::Rock, BlockTex::Rock, BlockTex::Rock, BlockTex::Rock, BlockTex::Rock},
|
||||
/* Ladder */{BlockTex::Ladder, BlockTex::Ladder, BlockTex::LadderTop, BlockTex::LadderTop, BlockTex::Ladder, BlockTex::Ladder},
|
||||
/* TNT*/ {BlockTex::Explosive, BlockTex::Explosive, BlockTex::Explosive, BlockTex::Explosive, BlockTex::Explosive, BlockTex::Explosive},
|
||||
/* Jump */ {BlockTex::Jump, BlockTex::Jump, BlockTex::JumpTop, BlockTex::TeleBottom, BlockTex::Jump, BlockTex::Jump},
|
||||
/* Shock */ {BlockTex::TeleSideA, BlockTex::TeleSideA, BlockTex::TeleBottom, BlockTex::Spikes, BlockTex::TeleSideB, BlockTex::TeleSideB},
|
||||
/*BankRed*/ {BlockTex::BankFrontRed, BlockTex::BankBackRed, BlockTex::BankTopRed, BlockTex::BankTopRed, BlockTex::BankLeftRed, BlockTex::BankRightRed},
|
||||
/*BankBlue*/{BlockTex::BankFrontBlue, BlockTex::BankBackBlue, BlockTex::BankTopBlue, BlockTex::BankTopBlue, BlockTex::BankLeftBlue, BlockTex::BankRightBlue},
|
||||
/*BeaconR*/ {BlockTex::TeleSideA, BlockTex::TeleSideA, BlockTex::BeaconRed, BlockTex::LadderTop, BlockTex::TeleSideB, BlockTex::TeleSideB},
|
||||
/*BeaconB*/ {BlockTex::TeleSideA, BlockTex::TeleSideA, BlockTex::BeaconBlue, BlockTex::LadderTop, BlockTex::TeleSideB, BlockTex::TeleSideB},
|
||||
/* Road */ {BlockTex::Road, BlockTex::Road, BlockTex::Road, BlockTex::Road, BlockTex::Road, BlockTex::Road},
|
||||
/* SolidR */{BlockTex::SolidRed, BlockTex::SolidRed, BlockTex::SolidRed, BlockTex::SolidRed, BlockTex::SolidRed, BlockTex::SolidRed},
|
||||
/* SolidB */{BlockTex::SolidBlue, BlockTex::SolidBlue, BlockTex::SolidBlue, BlockTex::SolidBlue, BlockTex::SolidBlue, BlockTex::SolidBlue},
|
||||
/* Metal */ {BlockTex::Metal, BlockTex::Metal, BlockTex::Metal, BlockTex::Metal, BlockTex::Metal, BlockTex::Metal},
|
||||
/*DirtSign*/{BlockTex::DirtSign, BlockTex::DirtSign, BlockTex::DirtSign, BlockTex::DirtSign, BlockTex::DirtSign, BlockTex::DirtSign},
|
||||
/* Lava */ {BlockTex::Lava, BlockTex::Lava, BlockTex::Lava, BlockTex::Lava, BlockTex::Lava, BlockTex::Lava},
|
||||
/* TransR */{BlockTex::TransRed, BlockTex::TransRed, BlockTex::TransRed, BlockTex::TransRed, BlockTex::TransRed, BlockTex::TransRed},
|
||||
/* TransB */{BlockTex::TransBlue, BlockTex::TransBlue, BlockTex::TransBlue, BlockTex::TransBlue, BlockTex::TransBlue, BlockTex::TransBlue},
|
||||
};
|
||||
|
||||
bool Blocks::isTransparent(BlockType t) {
|
||||
switch (t) {
|
||||
case BlockType::Air:
|
||||
case BlockType::TransBlue:
|
||||
case BlockType::TransRed:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Blocks::isFaceRemoved(BlockType t, BlockType other) {
|
||||
if (isTransparent(t)) {
|
||||
return (t != other);
|
||||
} else {
|
||||
return isTransparent(other);
|
||||
}
|
||||
}
|
||||
|
||||
bool Blocks::canGoThrough(BlockType t, Player::Team team) {
|
||||
if (t == BlockType::Air)
|
||||
return true;
|
||||
if (t != BlockType::TransRed && t != BlockType::TransBlue)
|
||||
return false;
|
||||
return (t == BlockType::TransRed && team == Player::Team::Red) ||
|
||||
(t == BlockType::TransBlue && team == Player::Team::Blue);
|
||||
}
|
||||
|
||||
int32 blend(int32 c1, int32 c2, uint8 val) {
|
||||
uint ival = 256 - val;
|
||||
uint v1_1 = c1 & 0xFF00FF;
|
||||
uint v1_2 = c1 & 0x00FF00;
|
||||
uint v2_1 = c2 & 0xFF00FF;
|
||||
uint v2_2 = c2 & 0x00FF00;
|
||||
uint res =
|
||||
( ( ( ( v1_1 * ival ) + ( v2_1 * val ) ) >> 8 ) & 0xFF00FF ) |
|
||||
( ( ( ( v1_2 * ival ) + ( v2_2 * val ) ) >> 8 ) & 0x00FF00 );
|
||||
return res;
|
||||
}
|
||||
|
||||
int lerp(int a, int b, float x) {
|
||||
return a*(1-x) + b*x;
|
||||
}
|
||||
void makePerlin(int w, int h, uint8 *buf) {
|
||||
uint8 noise[(h/4)*(w/4)];
|
||||
for (int i=0; i < (h/4)*(w/4); i++)
|
||||
noise[i] = FastRand(255);
|
||||
for (int x=0; x < w; x++) {
|
||||
for (int y=0; y < h; y++) {
|
||||
int target = (x/4) + (y/4)*(w/4);
|
||||
buf[x+y*h] = sqrt(lerp(noise[target], noise[target+1], x%4/4.f) *
|
||||
lerp(noise[target], noise[target+(w/4)], y%4/4.f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define AddTex(x, y) m_coords[(int)x] = m_atlasCreator->add(getAssetPath("blocks", y))
|
||||
#define AddTexP(i, x, y, z, d) m_coords[(int)i] = m_atlasCreator->add(x, y, z, d)
|
||||
Blocks::Blocks() : m_atlas(nullptr) {
|
||||
m_atlasCreator = new AtlasCreator(64*8, 64*8); //64*((int)BlockTex::LAST/8));
|
||||
m_coords = new AtlasCreator::Coord[BlockTex::LAST];
|
||||
|
||||
if (GlobalProperties::UseProceduralTextures) {
|
||||
uint8 *data = new uint8[64*64*4];
|
||||
uint8 *perlin = new uint8[64*64*4];
|
||||
makePerlin(64, 64, perlin);
|
||||
for (int x=0; x < 64; x++) {
|
||||
for (int y=0; y < 64; y++) {
|
||||
int32 noise; // = blend(0x292018, 0xBF9860, FastRand(0, 255));
|
||||
if (x == 0 || x == 63 || y == 0 || y == 63) {
|
||||
noise = 0x1B120B;
|
||||
} else {
|
||||
/*switch (FastRand(0, 4)) {
|
||||
case 0: noise = 0x292018; break;
|
||||
case 1: noise = 0x593F28; break;
|
||||
case 2: noise = 0x87633E; break;
|
||||
case 3: noise = 0xBF9860; break;
|
||||
}*/
|
||||
/*if (perlin[x+y*64] < 64)
|
||||
noise = 0x292018;
|
||||
else if (perlin[x+y*64] < 128)
|
||||
noise = 0x593F28;
|
||||
else if (perlin[x+y*64] < 192)
|
||||
noise = 0x87633E;
|
||||
else
|
||||
noise = 0xBF9860;*/
|
||||
noise = ((int)perlin[x+y*64] << 16) + ((int)perlin[x+y*64] << 8) + (int)perlin[x+y*64];
|
||||
}
|
||||
data[0 + x*4 + y*64*4] = noise >> 16 & 0xFF;
|
||||
data[1 + x*4 + y*64*4] = noise >> 8 & 0xFF;
|
||||
data[2 + x*4 + y*64*4] = noise & 0xFF;
|
||||
data[3 + x*4 + y*64*4] = 255;
|
||||
}
|
||||
}
|
||||
AddTexP(BlockTex::Dirt, 64, 64, 4, data);
|
||||
delete[] data;
|
||||
delete[] perlin;
|
||||
} else {
|
||||
AddTex(BlockTex::Dirt, "tex_block_dirt.png");}
|
||||
AddTex(BlockTex::DirtSign, "tex_block_dirt_sign.png");
|
||||
AddTex(BlockTex::Rock, "tex_block_rock.png");
|
||||
AddTex(BlockTex::Ore, "tex_block_ore.png");
|
||||
AddTex(BlockTex::Gold, "tex_block_silver.png");
|
||||
AddTex(BlockTex::Diamond, "tex_block_diamond.png");
|
||||
AddTex(BlockTex::HomeRed, "tex_block_home_red.png");
|
||||
AddTex(BlockTex::HomeBlue, "tex_block_home_blue.png");
|
||||
AddTex(BlockTex::SolidRed, "tex_block_red.png");
|
||||
AddTex(BlockTex::SolidBlue, "tex_block_blue.png");
|
||||
AddTex(BlockTex::Ladder, "tex_block_ladder.png");
|
||||
AddTex(BlockTex::LadderTop, "tex_block_ladder_top.png");
|
||||
AddTex(BlockTex::Spikes, "tex_block_spikes.png");
|
||||
AddTex(BlockTex::Jump, "tex_block_jump.png");
|
||||
AddTex(BlockTex::JumpTop, "tex_block_jump_top.png");
|
||||
AddTex(BlockTex::Explosive, "tex_block_explosive.png");
|
||||
AddTex(BlockTex::Metal, "tex_block_metal.png");
|
||||
AddTex(BlockTex::BankTopRed, "tex_block_bank_top_red.png");
|
||||
AddTex(BlockTex::BankLeftRed, "tex_block_bank_left_red.png");
|
||||
AddTex(BlockTex::BankFrontRed, "tex_block_bank_front_red.png");
|
||||
AddTex(BlockTex::BankRightRed, "tex_block_bank_right_red.png");
|
||||
AddTex(BlockTex::BankBackRed, "tex_block_bank_back_red.png");
|
||||
AddTex(BlockTex::BankTopBlue, "tex_block_bank_top_blue.png");
|
||||
AddTex(BlockTex::BankLeftBlue, "tex_block_bank_left_blue.png");
|
||||
AddTex(BlockTex::BankFrontBlue, "tex_block_bank_front_blue.png");
|
||||
AddTex(BlockTex::BankRightBlue, "tex_block_bank_right_blue.png");
|
||||
AddTex(BlockTex::BankBackBlue, "tex_block_bank_back_blue.png");
|
||||
AddTex(BlockTex::TeleSideA, "tex_block_teleporter_a.png");
|
||||
AddTex(BlockTex::TeleSideB, "tex_block_teleporter_b.png");
|
||||
AddTex(BlockTex::TeleTop, "tex_block_teleporter_top.png");
|
||||
AddTex(BlockTex::TeleBottom, "tex_block_teleporter_bottom.png");
|
||||
AddTex(BlockTex::Lava, "tex_block_lava.png");
|
||||
AddTex(BlockTex::Road, "tex_block_road_orig.png");
|
||||
AddTex(BlockTex::RoadTop, "tex_block_road_top.png");
|
||||
AddTex(BlockTex::RoadBottom, "tex_block_road_bottom.png");
|
||||
AddTex(BlockTex::BeaconRed, "tex_block_beacon_top_red.png");
|
||||
AddTex(BlockTex::BeaconBlue, "tex_block_beacon_top_blue.png");
|
||||
AddTex(BlockTex::TransRed, "tex_block_trans_red.png");
|
||||
AddTex(BlockTex::TransBlue, "tex_block_trans_blue.png");
|
||||
|
||||
|
||||
m_atlas = m_atlasCreator->getAtlas();
|
||||
delete m_atlasCreator;
|
||||
}
|
||||
|
||||
Blocks::~Blocks() {
|
||||
if (m_atlas)
|
||||
delete m_atlas;
|
||||
delete[] m_coords;
|
||||
}
|
||||
|
||||
const AtlasCreator::Coord* Blocks::gTC(BlockType t, FaceDirection d) const {
|
||||
int idx = (int)sideTextures[(int)t][(int)d];
|
||||
return &(m_coords[idx]);
|
||||
}
|
||||
|
||||
Texture* Blocks::getAtlas() const {
|
||||
return m_atlas;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
#ifndef BLOCKS_HPP
|
||||
#define BLOCKS_HPP
|
||||
#include <stdint.h>
|
||||
#include "AtlasCreator.hpp"
|
||||
#include "Player.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
enum class BlockType : uint8_t {
|
||||
Air = 0,
|
||||
Dirt = 1,
|
||||
Ore = 2,
|
||||
Gold = 3,
|
||||
Diamond,
|
||||
Rock,
|
||||
Ladder,
|
||||
Explosive,
|
||||
Jump,
|
||||
Shock,
|
||||
BankRed,
|
||||
BankBlue,
|
||||
BeaconRed,
|
||||
BeaconBlue,
|
||||
Road,
|
||||
SolidRed,
|
||||
SolidBlue,
|
||||
Metal,
|
||||
DirtSign,
|
||||
Lava,
|
||||
TransRed,
|
||||
TransBlue,
|
||||
LAST
|
||||
};
|
||||
|
||||
enum class FaceDirection : uint8_t {
|
||||
XInc = 0,
|
||||
XDec = 1,
|
||||
YInc = 2,
|
||||
YDec = 3,
|
||||
ZInc = 4,
|
||||
ZDec = 5
|
||||
};
|
||||
|
||||
enum class BlockTex : uint8_t {
|
||||
None = 0,
|
||||
Dirt = 1,
|
||||
Ore = 2,
|
||||
Gold = 3,
|
||||
Diamond,
|
||||
Rock,
|
||||
Jump,
|
||||
JumpTop,
|
||||
Ladder,
|
||||
LadderTop,
|
||||
Explosive,
|
||||
Spikes,
|
||||
HomeRed,
|
||||
HomeBlue,
|
||||
BankTopRed,
|
||||
BankTopBlue,
|
||||
BankFrontRed,
|
||||
BankFrontBlue,
|
||||
BankLeftRed,
|
||||
BankLeftBlue,
|
||||
BankRightRed,
|
||||
BankRightBlue,
|
||||
BankBackRed,
|
||||
BankBackBlue,
|
||||
TeleTop,
|
||||
TeleBottom,
|
||||
TeleSideA,
|
||||
TeleSideB,
|
||||
SolidRed,
|
||||
SolidBlue,
|
||||
Metal,
|
||||
DirtSign,
|
||||
Lava,
|
||||
Road,
|
||||
RoadTop,
|
||||
RoadBottom,
|
||||
BeaconRed,
|
||||
BeaconBlue,
|
||||
TransRed,
|
||||
TransBlue,
|
||||
LAST
|
||||
};
|
||||
|
||||
inline bool operator!(BlockType& t) {
|
||||
return !static_cast<uint8_t>(t);
|
||||
}
|
||||
|
||||
class Blocks {
|
||||
private:
|
||||
AtlasCreator *m_atlasCreator;
|
||||
Texture *m_atlas;
|
||||
AtlasCreator::Coord *m_coords;
|
||||
|
||||
// No copy
|
||||
Blocks(const Blocks&) = delete;
|
||||
Blocks& operator=(const Blocks&) = delete;
|
||||
|
||||
public:
|
||||
Blocks();
|
||||
~Blocks();
|
||||
static bool isTransparent(BlockType t);
|
||||
static bool isFaceRemoved(BlockType t, BlockType other);
|
||||
static bool canGoThrough(BlockType t, Player::Team team);
|
||||
const AtlasCreator::Coord* gTC(BlockType, FaceDirection) const;
|
||||
Texture* getAtlas() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,36 @@
|
|||
cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
project(diggler)
|
||||
|
||||
add_subdirectory(enet)
|
||||
add_subdirectory(lzfx)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -Wno-narrowing -ffast-math -DGLM_FORCE_RADIANS")
|
||||
# makes GCC STFU about narrow converts, and GLM STFU about degree angles
|
||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
|
||||
|
||||
set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Build type")
|
||||
#set(USE_GCC_LTO FALSE CACHE BOOL "Use GCC LTOs")
|
||||
set(CMAKE_BUILD_TYPE "Debug")
|
||||
#set(CMAKE_BUILD_TYPE "MinSizeRel")
|
||||
message(">> Using ${CMAKE_BUILD_TYPE} configuration")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -DDEBUG -ggdb -Wall -Wuninitialized")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
|
||||
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -Os")
|
||||
#if(USE_GCC_LTO)
|
||||
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto")
|
||||
#endif()
|
||||
|
||||
find_package(OpenAL)
|
||||
find_package(OpenGL)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_search_module(GLFW REQUIRED glfw3)
|
||||
include_directories(${GLFW_INCLUDE_DIRS} ${OPENAL_INCLUDE_DIR} ${ENET_INCLUDE_DIR})
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__FILENAME__='\"$(subst ${CMAKE_SOURCE_DIR}/,,$(abspath $<))\"'")
|
||||
#"
|
||||
|
||||
add_executable(diggler Audio.cpp ui/Element.cpp ui/Manager.cpp UITestState.cpp ChunkChangeHelper.cpp Mutex.cpp PlayerList.cpp network/NetHelper.cpp network/Network.cpp Skybox.cpp Frustum.cpp CaveGenerator.cpp MessageState.cpp GameState.cpp network/Network.cpp Server.cpp Chatbox.cpp GlUtils.cpp ui/Button.cpp KeyBindings.cpp GameWindow.cpp GLFWHandler.cpp Player.cpp Clouds.cpp Game.cpp ProgramManager.cpp LocalPlayer.cpp AABB.cpp SoundBuffer.cpp Sound.cpp stb_vorbis.c Camera.cpp EscMenu.cpp ui/Text.cpp Font.cpp AtlasCreator.cpp Blocks.cpp GlobalProperties.cpp Superchunk.cpp Chunk.cpp FBO.cpp Texture.cpp stb_image.c Program.cpp VBO.cpp Shader.cpp Platform.cpp main.cpp)
|
||||
|
||||
target_link_libraries(diggler GLEW GL enet lzfx ${GLFW_STATIC_LIBRARIES} ${OPENAL_LIBRARY})
|
||||
|
||||
install(TARGETS diggler RUNTIME DESTINATION bin)
|
|
@ -0,0 +1,65 @@
|
|||
#include "Camera.hpp"
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtx/rotate_vector.hpp>
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
Camera::Camera() {
|
||||
m_worldUp = glm::vec3(0, 1, 0);
|
||||
update();
|
||||
}
|
||||
|
||||
void Camera::setProjection(const glm::mat4& m) {
|
||||
m_projMatrix = m;
|
||||
update();
|
||||
}
|
||||
|
||||
void Camera::setPersp(float rad, float ratio, float near, float far) {
|
||||
frustum.setCamInternals(rad, ratio, near, far);
|
||||
setProjection(glm::perspective(rad, ratio, near, far));
|
||||
}
|
||||
|
||||
void Camera::lookAt(const glm::vec3& l) {
|
||||
m_lookAt = l;
|
||||
update();
|
||||
}
|
||||
|
||||
void Camera::lookAtAbs(const glm::vec3& l) {
|
||||
m_lookAt = m_position - l;
|
||||
update();
|
||||
}
|
||||
|
||||
void Camera::setPosition(const glm::vec3& p) {
|
||||
m_position = p;
|
||||
update();
|
||||
}
|
||||
|
||||
void Camera::update() {
|
||||
m_viewMatrix = glm::lookAt(m_position, m_position + m_lookAt, m_worldUp);
|
||||
frustum.setCamDef(m_position, m_position + m_lookAt, m_worldUp);
|
||||
m_up = glm::normalize(glm::cross(glm::cross(m_lookAt, m_worldUp), m_lookAt));
|
||||
m_pvMatrix = m_projMatrix * m_viewMatrix;
|
||||
m_skyMatrix = m_projMatrix * glm::lookAt(glm::vec3(), m_lookAt, m_worldUp);
|
||||
}
|
||||
|
||||
const glm::mat4& Camera::getVMatrix() const {
|
||||
return m_viewMatrix;
|
||||
}
|
||||
|
||||
const glm::mat4& Camera::getPVMatrix() const {
|
||||
return m_pvMatrix;
|
||||
}
|
||||
|
||||
const glm::mat4& Camera::getSkyMatrix() const {
|
||||
return m_skyMatrix;
|
||||
}
|
||||
|
||||
const glm::vec3& Camera::getUp() const {
|
||||
return m_up;
|
||||
}
|
||||
|
||||
const glm::vec3& Camera::getLookAt() const {
|
||||
return m_lookAt;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef CAMERA_HPP
|
||||
#define CAMERA_HPP
|
||||
#include <glm/glm.hpp>
|
||||
#include "Frustum.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class Camera {
|
||||
friend class LocalPlayer;
|
||||
private:
|
||||
glm::mat4 m_projMatrix, m_viewMatrix, m_pvMatrix, m_skyMatrix;
|
||||
glm::vec3 m_position, m_lookAt, m_worldUp, m_up;
|
||||
|
||||
void update();
|
||||
void setProjection(const glm::mat4& m);
|
||||
|
||||
public:
|
||||
Frustum frustum;
|
||||
|
||||
Camera();
|
||||
void setPersp(float rad, float ratio, float near, float far);
|
||||
void setPosition(const glm::vec3 &p);
|
||||
void lookAtAbs(const glm::vec3 &l);
|
||||
void lookAt(const glm::vec3 &l);
|
||||
const glm::mat4& getVMatrix() const;
|
||||
const glm::mat4& getPVMatrix() const;
|
||||
const glm::mat4& getSkyMatrix() const;
|
||||
const glm::vec3& getUp() const;
|
||||
const glm::vec3& getLookAt() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,539 @@
|
|||
#include "CaveGenerator.hpp"
|
||||
#include "Blocks.hpp"
|
||||
#include "Chunk.hpp"
|
||||
#include <climits>
|
||||
#include <thread>
|
||||
#include "Platform.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
std::string CaveGenerator::CaveInfo = "";
|
||||
int CaveGenerator::GROUND_LEVEL = 0;
|
||||
|
||||
// Generates a set of constant values.
|
||||
void CaveGenerator::GenerateConstant(Superchunk &sc, BlockType value) {
|
||||
for (int x = 0; x < sc.getChunksX()*CX; x++)
|
||||
for (int y = 0; y < sc.getChunksY()*CY; y++)
|
||||
for (int z = 0; z < sc.getChunksZ()*CZ; z++)
|
||||
sc.set(x, y, z, value);
|
||||
}
|
||||
|
||||
void CaveGenerator::GenerateConstant(Arr3BT &data, BlockType value) {
|
||||
for (int x = 0; x < data.getX(); x++)
|
||||
for (int y = 0; y < data.getY(); y++)
|
||||
for (int z = 0; z < data.getZ(); z++)
|
||||
data[x][y][z] = value;
|
||||
}
|
||||
|
||||
// Create a cave system.
|
||||
void CaveGenerator::GenerateCaveSystem(Superchunk &sc, bool includeLava, uint oreFactor) {
|
||||
float gradientStrength = FastRand(0, 10000)/100000.f;
|
||||
getDebugStream() << gradientStrength << std::endl;
|
||||
const int xs = sc.getChunksX()*CX, ys = sc.getChunksY()*CY, zs = sc.getChunksZ()*CZ;
|
||||
GROUND_LEVEL = ys*7/8;
|
||||
int size = cbrt(xs*ys*zs);
|
||||
|
||||
//GenerateConstant(sc, BlockType::Dirt);
|
||||
for (int x = 0; x < xs; x++)
|
||||
for (int y = 0; y < ys; y++)
|
||||
for (int z = 0; z < zs; z++)
|
||||
sc.set(x, y, z, BlockType::Dirt);
|
||||
|
||||
// Add ore.
|
||||
Arr3f *oreNoise = new Arr3f(xs/2, ys/2, zs/2);
|
||||
GeneratePerlinNoise(*oreNoise);
|
||||
|
||||
Arr3f *interpOreNoise = InterpolateData(*oreNoise, 2);
|
||||
for (uint i = 0; i < oreFactor; i++)
|
||||
PaintWithRandomWalk(sc, *interpOreNoise, 1, BlockType::Ore, false);
|
||||
|
||||
delete oreNoise; delete interpOreNoise;
|
||||
|
||||
// Add minerals.
|
||||
AddGold(sc);
|
||||
AddDiamond(sc);
|
||||
|
||||
// Level off everything above ground level, replacing it with mountains.
|
||||
Arr3f *mountainNoiseBase = new Arr3f(xs/2, ys/2, zs/2);
|
||||
GeneratePerlinNoise(*mountainNoiseBase);
|
||||
Arr3f *mountainNoise = InterpolateData(*mountainNoiseBase, 2);
|
||||
delete mountainNoiseBase;
|
||||
/*for (int y = GROUND_LEVEL; y < ys; y++)
|
||||
getDebugStream() << y << '=' << std::max(0.f, 1.f-(y-GROUND_LEVEL)/(float)(ys-GROUND_LEVEL)) << std::endl;*/
|
||||
for (int x = 0; x < xs; x++)
|
||||
for (int y = GROUND_LEVEL; y < ys; y++)
|
||||
for (int z = 0; z < zs; z++)
|
||||
(*mountainNoise)[x][y][z] = std::max(0.f, 1.f-(y-GROUND_LEVEL)/(float)(ys-GROUND_LEVEL));
|
||||
|
||||
Arr3f *gradient = GenerateGradient(xs, ys, zs);
|
||||
AddDataTo(*mountainNoise, *gradient, 0.1f, 0.9f);
|
||||
delete gradient;
|
||||
Arr3BT *mountainData = new Arr3BT(xs, ys, zs);
|
||||
GenerateConstant(*mountainData, BlockType::Air);
|
||||
int numMountains = FastRand(size, size * 3);
|
||||
for (int i = 0; i < numMountains; i++)
|
||||
PaintWithRandomWalk(*mountainData, *mountainNoise, FastRand(2, 3), BlockType::Dirt, false);
|
||||
for (int x = 0; x < xs; x++)
|
||||
for (int y = ys-1; y >= GROUND_LEVEL; y--)
|
||||
for (int z = 0; z < zs; z++)
|
||||
if ((*mountainData)[x][y][z] == BlockType::Air)
|
||||
sc.set(x, y, z, BlockType::Air);
|
||||
|
||||
delete mountainNoise; delete mountainData;
|
||||
#if 1
|
||||
// Carve some caves into the ground.
|
||||
Arr3f *caveNoiseBase = new Arr3f(xs / 2, ys / 2, zs / 2); GeneratePerlinNoise(*caveNoiseBase);
|
||||
Arr3f *caveNoise = InterpolateData(*caveNoiseBase, 2);
|
||||
delete caveNoiseBase;
|
||||
gradient = GenerateGradient(xs, ys, zs);
|
||||
AddDataTo(*caveNoise, *gradient, 1 - gradientStrength, gradientStrength);
|
||||
delete gradient;
|
||||
int cavesToCarve = 2; //FastRand(size / 8, size / 4);
|
||||
for (int i = 0; i < cavesToCarve; i++)
|
||||
PaintWithRandomWalk(sc, *caveNoise, 2, BlockType::Air, false);
|
||||
delete caveNoise;
|
||||
|
||||
// Carve the map into a sphere.
|
||||
Arr3f *sphereGradient = GenerateRadialGradient(xs, ys, zs);
|
||||
cavesToCarve = 2; //FastRand(size / 8, size / 4);
|
||||
for (int i = 0; i < cavesToCarve; i++)
|
||||
PaintWithRandomWalk(sc, *sphereGradient, 2, BlockType::Air, true);
|
||||
delete sphereGradient;
|
||||
|
||||
// Add rocks.
|
||||
AddRocks(sc);
|
||||
|
||||
// Add lava.
|
||||
if (includeLava)
|
||||
AddLava(sc);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void CaveGenerator::AddRocks(Superchunk &sc) {
|
||||
int xs = sc.getChunksX()*CX, ys = sc.getChunksY()*CY, zs = sc.getChunksZ()*CZ;
|
||||
int size = cbrt(xs*ys*zs);
|
||||
int numRocks = FastRand(size, 2*size);
|
||||
CaveInfo += " numRocks=" + numRocks;
|
||||
for (int i = 0; i < numRocks; i++) {
|
||||
int x = FastRand(0, xs);
|
||||
int z = FastRand(0, zs);
|
||||
|
||||
// generate a random y-value weighted toward a deep depth
|
||||
float yf = 0;
|
||||
for (int j = 0; j < 4; j++)
|
||||
yf += FastRandF();
|
||||
yf /= 2;
|
||||
yf = 1 - fabs(yf - 1);
|
||||
int y = (int)(yf * ys)-ys+4;
|
||||
// +4: little padding so we can see the rockballs
|
||||
|
||||
int rockSize = (int)((FastRandF() + FastRandF() + FastRandF() + FastRandF()) * 2);
|
||||
|
||||
PaintAtPoint(sc, x, y, z, rockSize, BlockType::Rock);
|
||||
}
|
||||
}
|
||||
|
||||
void CaveGenerator::AddLava(Superchunk &sc) {
|
||||
const int xs = sc.getChunksX()*CX, ys = sc.getChunksY()*CY, zs = sc.getChunksZ()*CZ;
|
||||
// int numDiamonds = 16; for a 64x64x64 map
|
||||
// Changed to be more popular
|
||||
int numFlows = FastRand(cbrt(xs*ys*zs) / 16, cbrt(xs*ys*zs) / 2);
|
||||
//int numFlows = randGen.Next(size / 16, size / 2);
|
||||
while (numFlows > 0) {
|
||||
int x = FastRand(0, xs);
|
||||
int z = FastRand(0, zs);
|
||||
|
||||
//switch (randGen.Next(0, 4)) {
|
||||
// case 0: x = 0; break;
|
||||
// case 1: x = size - 1; break;
|
||||
// case 2: y = 0; break;
|
||||
// case 3: y = size - 1; break;
|
||||
//}
|
||||
|
||||
// generate a random y-value weighted toward a medium depth
|
||||
int y = FastRand(ys / 6, ys / 2);
|
||||
|
||||
if (z > 2 && sc.get(x, y , z) == BlockType::Air) {
|
||||
//sc.set(x, y, z, BlockType::Rock);
|
||||
sc.set(x, y, z, BlockType::Lava);
|
||||
numFlows -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CaveGenerator::AddDiamond(Superchunk &sc) {
|
||||
CaveInfo += "diamond";
|
||||
|
||||
const int xs = sc.getChunksX()*CX, ys = sc.getChunksY()*CY, zs = sc.getChunksZ()*CZ;
|
||||
// int numDiamonds = 16; for a 64x64x64 map
|
||||
// Changed to be more popular
|
||||
int numDiamonds = cbrt(xs*ys*zs) / 2;
|
||||
for (int i = 0; i < numDiamonds; i++) {
|
||||
int x = FastRand(xs);
|
||||
int z = FastRand(zs);
|
||||
|
||||
// generate a random y-value weighted toward a deep depth
|
||||
float yf = 0;
|
||||
for (int j = 0; j < 4; j++)
|
||||
yf += FastRandF();
|
||||
yf /= 2;
|
||||
yf = fabs(yf - 1);
|
||||
int y = (int)(yf * FastRand(ys));
|
||||
|
||||
sc.set(x, y, z, BlockType::Diamond);
|
||||
}
|
||||
}
|
||||
|
||||
// Gold appears in fairly numerous streaks, located at medium depths.
|
||||
void CaveGenerator::AddGold(Superchunk &sc) {
|
||||
CaveInfo += "gold";
|
||||
int xs = sc.getChunksX()*CX, ys = sc.getChunksY()*CY, zs = sc.getChunksY()*CY;
|
||||
|
||||
int numVeins = cbrt(xs * ys * zs) / 4; // 16 for 64x64x64
|
||||
for (int i = 0; i < numVeins; i++) {
|
||||
float mean = cbrt(xs * ys * zs);
|
||||
int fieldLength = FastRand(mean/3, mean);// Original: randGen.Next(size/3, size);
|
||||
float x = FastRandF() * xs;
|
||||
float z = FastRandF() * zs;
|
||||
|
||||
// generate a random z-value weighted toward a medium depth
|
||||
float yf = 0;
|
||||
for (int j = 0; j < 4; j++)
|
||||
yf += FastRandF();
|
||||
yf /= 2;
|
||||
yf = 1 - std::abs(yf - 1);
|
||||
float y = yf * ys;
|
||||
|
||||
float dx = FastRandF() * 2 - 1;
|
||||
float dy = FastRandF() * 2 - 1;
|
||||
float dz = FastRandF() * 2 - 1;
|
||||
float dl = sqrt(dx * dx + dy * dy + dz * dz);
|
||||
dx /= dl; dy /= dl; dz /= dl;
|
||||
|
||||
for (int j = 0; j < fieldLength; j++) {
|
||||
x += dx;
|
||||
y += dy;
|
||||
z += dz;
|
||||
if (x >= 0 && y >= 0 && z >= 0 && x < xs && y < ys && z < zs)
|
||||
sc.set((int)x, (int)y, (int)z, BlockType::Gold);
|
||||
int tx = 0, ty = 0, tz = 0;
|
||||
switch (FastRand(0, 3)) {
|
||||
case 0:
|
||||
tx += 1;
|
||||
break;
|
||||
case 1:
|
||||
ty += 1;
|
||||
break;
|
||||
case 2:
|
||||
tz += 1;
|
||||
break;
|
||||
}
|
||||
if (x + tx >= 0 && y + ty>= 0 && z+tz >= 0 && x+tx < xs && y+ty < ys && z+tz < zs)
|
||||
sc.set((int)x+tx, (int)y+ty, (int)z+tz, BlockType::Gold);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generates a cube of noise with sides of length size. Noise falls in a linear
|
||||
// distribution ranging from 0 to magnitude.
|
||||
Arr3f* CaveGenerator::GenerateNoise(int sx, int sy, int sz, float magnitude) {
|
||||
Arr3f *noiseArray = new Arr3f(sx, sy, sz);
|
||||
for (int x = 0; x < sx; x++)
|
||||
for (int y = 0; y < sy; y++)
|
||||
for (int z = 0; z < sz; z++)
|
||||
(*noiseArray)[x][y][z] = FastRandF() * magnitude;
|
||||
return noiseArray;
|
||||
}
|
||||
|
||||
// Generates some perlin noise!
|
||||
void CaveGenerator::GeneratePerlinNoise(Arr3f &data) {
|
||||
Arr3f *noise = nullptr, *noise2 = nullptr;
|
||||
for (int f = 4; f < 32; f *= 2) {
|
||||
noise = GenerateNoise(f, f, f, 2.f / f);
|
||||
noise2 = InterpolateData(*noise, data.getX()/f);
|
||||
delete noise;
|
||||
AddDataTo(data, *noise2);
|
||||
delete noise2;
|
||||
}
|
||||
}
|
||||
|
||||
// Does a random walk of noiseData, setting cells to 0 in caveData in the process.
|
||||
void CaveGenerator::PaintWithRandomWalk(Superchunk &sc, Arr3f &noiseData, int paintRadius, BlockType paintValue, bool dontStopAtEdge) {
|
||||
const int xs = noiseData.getX(), ys = noiseData.getY(), zs = noiseData.getZ();
|
||||
int x = FastRand(0, xs-1);
|
||||
int y = FastRand(0, ys-1);
|
||||
int z = FastRand(0, zs-1);
|
||||
int m = cbrt(xs*ys*zs);
|
||||
|
||||
if (y < ys/50)
|
||||
y = 0;
|
||||
|
||||
int count = 0;
|
||||
|
||||
while (count < m) {
|
||||
float oldNoise = noiseData[x][y][z];
|
||||
|
||||
PaintAtPoint(sc, x, y, z, paintRadius, paintValue);
|
||||
int dx = FastRand(-paintRadius, paintRadius);
|
||||
int dy = FastRand(-paintRadius, paintRadius);
|
||||
int dz = FastRand(-paintRadius, paintRadius);
|
||||
|
||||
x += dx;
|
||||
y += dy;
|
||||
z += dz;
|
||||
|
||||
if (x < 0 || y < 0 || z < 0 || x >= xs || y >= ys || z >= zs) {
|
||||
if (dontStopAtEdge) {
|
||||
++count;
|
||||
if (x < 0) x = 0;
|
||||
if (y < 0) y = 0;
|
||||
if (z < 0) z = 0;
|
||||
if (x >= xs) x = xs - 1;
|
||||
if (y >= ys) y = ys - 1;
|
||||
if (z >= zs) z = zs - 1;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if (y < 0)
|
||||
y = 0;
|
||||
|
||||
float newNoise = noiseData[x][y][z];
|
||||
|
||||
// If we're jumping to a higher value on the noise gradient, move twice as far.
|
||||
if (newNoise > oldNoise) {
|
||||
PaintAtPoint(sc, x, y, z, paintRadius, paintValue);
|
||||
x += dx;
|
||||
y += dy;
|
||||
z += dz;
|
||||
|
||||
if (x < 0 || y < 0 || z < 0 || x >= xs || y >= ys || z >= zs) {
|
||||
if (dontStopAtEdge) {
|
||||
++count;
|
||||
if (x < 0) x = 0;
|
||||
if (y < 0) y = 0;
|
||||
if (z < 0) z = 0;
|
||||
if (x >= xs) x = xs - 1;
|
||||
if (y >= ys) y = ys - 1;
|
||||
if (z >= zs) z = zs - 1;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if (y < 0)
|
||||
y = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CaveGenerator::PaintWithRandomWalk(Arr3BT &data, Arr3f &noiseData, int paintRadius, BlockType paintValue, bool dontStopAtEdge) {
|
||||
const int xs = data.getX(), ys = data.getY(), zs = data.getZ();
|
||||
int x = FastRand(0, xs-1);
|
||||
int y = FastRand(0, ys-1);
|
||||
int z = FastRand(0, zs-1);
|
||||
int m = cbrt(xs*ys*zs);
|
||||
|
||||
if (y < ys/50)
|
||||
y = 0;
|
||||
|
||||
int count = 0;
|
||||
|
||||
while (count < m) {
|
||||
float oldNoise = noiseData[x][y][z];
|
||||
|
||||
PaintAtPoint(data, x, y, z, paintRadius, paintValue);
|
||||
int dx = FastRand(-paintRadius, paintRadius);
|
||||
int dy = FastRand(-paintRadius, paintRadius);
|
||||
int dz = FastRand(-paintRadius, paintRadius);
|
||||
|
||||
x += dx;
|
||||
y += dy;
|
||||
z += dz;
|
||||
|
||||
if (x < 0 || y < 0 || z < 0 || x >= xs || y >= ys || z >= zs) {
|
||||
if (dontStopAtEdge) {
|
||||
++count;
|
||||
if (x < 0) x = 0;
|
||||
if (y < 0) y = 0;
|
||||
if (z < 0) z = 0;
|
||||
if (x >= xs) x = xs - 1;
|
||||
if (y >= ys) y = ys - 1;
|
||||
if (z >= zs) z = zs - 1;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if (y < 0)
|
||||
y = 0;
|
||||
|
||||
float newNoise = noiseData[x][y][z];
|
||||
|
||||
// If we're jumping to a higher value on the noise gradient, move twice as far.
|
||||
if (newNoise > oldNoise) {
|
||||
PaintAtPoint(data, x, y, z, paintRadius, paintValue);
|
||||
x += dx;
|
||||
y += dy;
|
||||
z += dz;
|
||||
|
||||
if (x < 0 || y < 0 || z < 0 || x >= xs || y >= ys || z >= zs) {
|
||||
if (dontStopAtEdge) {
|
||||
count += 1;
|
||||
if (x < 0) x = 0;
|
||||
if (y < 0) y = 0;
|
||||
if (z < 0) z = 0;
|
||||
if (x >= xs) x = xs - 1;
|
||||
if (y >= ys) y = ys - 1;
|
||||
if (z >= zs) z = zs - 1;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if (y < 0)
|
||||
y = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CaveGenerator::PaintAtPoint(Superchunk &sc, int x, int y, int z, int paintRadius, BlockType paintValue) {
|
||||
int prsq = paintRadius*paintRadius;
|
||||
for (int dx = -paintRadius; dx <= paintRadius; dx++) {
|
||||
int dxsq = dx*dx;
|
||||
for (int dy = -paintRadius; dy <= paintRadius; dy++) {
|
||||
int dxdysq = dxsq+dy*dy;
|
||||
for (int dz = -paintRadius; dz <= paintRadius; dz++)
|
||||
if (x+dx >= 0 && y+dy>= 0 && z+dz >= 0 && x+dx < sc.getChunksX()*CX && y+dy < sc.getChunksY()*CY && z+dz < sc.getChunksZ()*CZ)
|
||||
if (dxdysq+dz*dz<prsq)
|
||||
sc.set(x + dx, y + dy, z + dz, paintValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CaveGenerator::PaintAtPoint(Arr3BT &data, int x, int y, int z, int paintRadius, BlockType paintValue) {
|
||||
int prsq = paintRadius*paintRadius;
|
||||
for (int dx = -paintRadius; dx <= paintRadius; dx++) {
|
||||
int dxsq = dx*dx;
|
||||
for (int dy = -paintRadius; dy <= paintRadius; dy++) {
|
||||
int dxdysq = dxsq+dy*dy;
|
||||
for (int dz = -paintRadius; dz <= paintRadius; dz++)
|
||||
if (x+dx >= 0 && y+dy>= 0 && z+dz >= 0 && x+dx < data.getX() && y+dy < data.getY() && z+dz < data.getZ())
|
||||
if (dxdysq+dz*dz<prsq)
|
||||
data[x + dx][y + dy][z + dz] = paintValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Arr3f* CaveGenerator::GenerateGradient(int x, int y, int z) {
|
||||
Arr3f *data = new Arr3f(x, y, z);
|
||||
|
||||
for (int j = 0; j < y; j++) {
|
||||
float v = (float)j/y;
|
||||
for (int i = 0; i < x; i++)
|
||||
for (int k = 0; k < z; k++)
|
||||
(*data)[i][j][k] = v;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
template<typename T> T clamp(T v, T min, T max) {
|
||||
if (v > max)
|
||||
return max;
|
||||
if (v < min)
|
||||
return min;
|
||||
return v;
|
||||
}
|
||||
|
||||
// Radial gradient concentrated with high values at the outside.
|
||||
Arr3f* CaveGenerator::GenerateRadialGradient(int x, int y, int z) {
|
||||
Arr3f *data = new Arr3f(x, y, z);
|
||||
const int size = cbrt(x*y*z);
|
||||
|
||||
for (int i = 0; i < x; i++)
|
||||
for (int j = 0; j < y; j++)
|
||||
for (int k = 0; k < z; k++) {
|
||||
float dist = (float)sqrt(pow(x - size / 2, 2) + pow(y - size / 2, 2));
|
||||
(*data)[i][j][k] = clamp(dist / size * 0.3f * (float)z / size, 0.f, 1.f);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
// Adds the values in dataSrc to the values in dataDst, storing the result in dataDst.
|
||||
void CaveGenerator::AddDataTo(Arr3f &dataDst, Arr3f &dataSrc, float scalarDst, float scalarSrc) {
|
||||
assert(dataDst.sizeEqual(dataSrc));
|
||||
|
||||
for (int x = 0; x < dataDst.getX(); x++)
|
||||
for (int y = 0; y < dataDst.getY(); y++)
|
||||
for (int z = 0; z < dataDst.getZ(); z++)
|
||||
dataDst[x][y][z] = clamp(dataDst[x][y][z]*scalarDst + dataSrc[x][y][z]*scalarSrc, 1.f, 0.f);
|
||||
}
|
||||
|
||||
void CaveGenerator::AddDataTo(Arr3f &dataDst, Arr3f &dataSrc) {
|
||||
AddDataTo(dataDst, dataSrc, 1, 1);
|
||||
}
|
||||
|
||||
// Resizes dataIn, with size sizeIn, to be of size sizeOut.
|
||||
Arr3f* CaveGenerator::InterpolateData(const Arr3f &dataIn, int scale) {
|
||||
int xb = dataIn.getX(), yb = dataIn.getY(), zb = dataIn.getZ();
|
||||
int xs = xb*scale, ys = yb*scale, zs = zb*scale;
|
||||
Arr3f *dataOut = new Arr3f(xs, ys, zs);
|
||||
int r = scale;
|
||||
for (int x = 0; x < xs; x++)
|
||||
for (int y = 0; y < ys; y++)
|
||||
for (int z = 0; z < zs; z++) {
|
||||
int xIn0 = x / r, yIn0 = y / r, zIn0 = z / r;
|
||||
int xIn1 = xIn0 + 1, yIn1 = yIn0 + 1, zIn1 = zIn0 + 1;
|
||||
if (xIn1 >= xb)
|
||||
xIn1 = 0;
|
||||
if (yIn1 >= yb)
|
||||
yIn1 = 0;
|
||||
if (zIn1 >= zb)
|
||||
zIn1 = 0;
|
||||
|
||||
float v000 = dataIn[xIn0][yIn0][zIn0];
|
||||
float v100 = dataIn[xIn1][yIn0][zIn0];
|
||||
float v010 = dataIn[xIn0][yIn1][zIn0];
|
||||
float v110 = dataIn[xIn1][yIn1][zIn0];
|
||||
float v001 = dataIn[xIn0][yIn0][zIn1];
|
||||
float v101 = dataIn[xIn1][yIn0][zIn1];
|
||||
float v011 = dataIn[xIn0][yIn1][zIn1];
|
||||
float v111 = dataIn[xIn1][yIn1][zIn1];
|
||||
|
||||
float xS = ((float)(x % r)) / r;
|
||||
float yS = ((float)(y % r)) / r;
|
||||
float zS = ((float)(z % r)) / r;
|
||||
|
||||
(*dataOut)[x][y][z] = v000 * (1 - xS) * (1 - yS) * (1 - zS) +
|
||||
v100 * xS * (1 - yS) * (1 - zS) +
|
||||
v010 * (1 - xS) * yS * (1 - zS) +
|
||||
v001 * (1 - xS) * (1 - yS) * zS +
|
||||
v101 * xS * (1 - yS) * zS +
|
||||
v011 * (1 - xS) * yS * zS +
|
||||
v110 * xS * yS * (1 - zS) +
|
||||
v111 * xS * yS * zS;
|
||||
}
|
||||
return dataOut;
|
||||
}
|
||||
|
||||
void CaveGenerator::RenderSlice(const Arr3BT &data, int z, Texture &renderTexture) {
|
||||
int w = data.getX(), h = data.getY();
|
||||
uint8 *pixelData = new uint8[w * h * 4];
|
||||
for (int x = 0; x < 256; x++) {
|
||||
for (int y = 0; y < 256; y++) {
|
||||
uint c = 0xFF000000;
|
||||
if (data[x][y][z] == BlockType::Dirt)
|
||||
c = 0xFFFFFFFF;
|
||||
if (data[x][y][z] == BlockType::Ore)
|
||||
c = 0xFF888888;
|
||||
if (data[x][y][z] == BlockType::Gold)
|
||||
c = 0xFFFF0000;
|
||||
if (data[x][y][z] == BlockType::Rock)
|
||||
c = 0xFF0000FF;
|
||||
pixelData[y * w + x] = c;
|
||||
}
|
||||
}
|
||||
renderTexture.setTexture(w, h, pixelData, Texture::PixelFormat::RGBA);
|
||||
delete[] pixelData;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef CAVE_GENERATOR_HPP
|
||||
#define CAVE_GENERATOR_HPP
|
||||
#include <string>
|
||||
#include "Superchunk.hpp"
|
||||
#include "Arr3Utils.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
typedef Arr3<BlockType> Arr3BT;
|
||||
|
||||
class CaveGenerator {
|
||||
private:
|
||||
CaveGenerator();
|
||||
|
||||
public:
|
||||
static std::string CaveInfo;
|
||||
static int GROUND_LEVEL;
|
||||
static void GenerateConstant(Superchunk &sc, BlockType value);
|
||||
static void GenerateConstant(Arr3BT &data, BlockType value);
|
||||
static void GenerateCaveSystem(Superchunk& sc, bool includeLava, uint oreFactor);
|
||||
static Arr3f* GenerateNoise(int sx, int sy, int sz, float magnitude);
|
||||
static void GeneratePerlinNoise(Arr3f &data);
|
||||
static Arr3f* InterpolateData(const Arr3f &dataIn, int scale);
|
||||
static Arr3f* GenerateRadialGradient(int x, int y, int z);
|
||||
static Arr3f* GenerateGradient(int x, int y, int z);
|
||||
static void PaintWithRandomWalk(Superchunk &sc, Arr3f &noiseData, int paintRadius, BlockType paintValue, bool dontStopAtEdge);
|
||||
static void PaintWithRandomWalk(Arr3BT &data, Arr3f &noiseData, int paintRadius, BlockType paintValue, bool dontStopAtEdge);
|
||||
static void PaintAtPoint(Superchunk &sc, int x, int y, int z, int paintRadius, BlockType paintValue);
|
||||
static void PaintAtPoint(Arr3BT &data, int x, int y, int z, int paintRadius, BlockType paintValue);
|
||||
static void AddDataTo(Arr3f &dataDst, Arr3f &dataSrc);
|
||||
static void AddDataTo(Arr3f &dataDst, Arr3f &dataSrc, float scalarDst, float scalarSrc);
|
||||
static void AddDiamond(Superchunk &sc);
|
||||
static void AddGold(Superchunk &sc);
|
||||
static void AddRocks(Superchunk &sc);
|
||||
static void AddLava(Superchunk &sc);
|
||||
/// Renders a specific z-level of a 256x256x256 data array to a texture.
|
||||
void RenderSlice(const Arr3BT &data, int z, Texture &renderTexture);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,113 @@
|
|||
#include "Chatbox.hpp"
|
||||
#include "Game.hpp"
|
||||
#include "GameWindow.hpp"
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
const Program *Chatbox::RenderProgram = nullptr;
|
||||
GLint Chatbox::RenderProgram_coord = -1;
|
||||
GLint Chatbox::RenderProgram_color = -1;
|
||||
GLint Chatbox::RenderProgram_mvp = -1;
|
||||
|
||||
Chatbox::Chatbox(Game *G) : m_isChatting(false), G(G),
|
||||
m_posX(0), m_posY(0) {
|
||||
m_chatText = G->UIM->create<UI::Text>(G->F, "", 2, 2);
|
||||
if (RenderProgram == nullptr) {
|
||||
RenderProgram = G->PM->getProgram(PM_2D | PM_COLORED);
|
||||
RenderProgram_coord = RenderProgram->att("coord");
|
||||
RenderProgram_color = RenderProgram->att("color");
|
||||
RenderProgram_mvp = RenderProgram->uni("mvp");
|
||||
}
|
||||
Vertex verts[6] = {
|
||||
{0.f, 0.f, 0.f, 0.f, 0.f, .5f},
|
||||
{100.f, 0.f, 0.f, 0.f, 0.f, .5f},
|
||||
{0.f, 100.f, 0.f, 0.f, 0.f, .5f},
|
||||
{100.f, 100.f, 0.f, 0.f, 0.f, .5f},
|
||||
{100.f, 0.f, 0.f, 0.f, 0.f, .5f},
|
||||
{0.f, 100.f, 0.f, 0.f, 0.f, .5f}
|
||||
};
|
||||
m_vbo.setData(verts, 6);
|
||||
}
|
||||
|
||||
Chatbox::~Chatbox() {
|
||||
delete m_chatText;
|
||||
}
|
||||
|
||||
void Chatbox::setPosition(int x, int y) {
|
||||
m_posX = x;
|
||||
m_posY = y;
|
||||
}
|
||||
|
||||
bool Chatbox::isChatting() const {
|
||||
return m_isChatting;
|
||||
}
|
||||
|
||||
void Chatbox::setIsChatting(bool value) {
|
||||
m_chatString.clear();
|
||||
m_chatText->setText(m_chatString);
|
||||
m_isChatting = value;
|
||||
}
|
||||
|
||||
void Chatbox::addChatEntry(const std::string &text) {
|
||||
m_chatEntries.emplace_back();
|
||||
ChatEntry &entry = m_chatEntries.back();
|
||||
entry.date = system_clock::now();
|
||||
entry.text = G->UIM->create<UI::Text>(G->F, text);
|
||||
entry.height = entry.text->getSize().y;
|
||||
}
|
||||
|
||||
Chatbox::ChatEntry::~ChatEntry() {
|
||||
delete text;
|
||||
}
|
||||
|
||||
void Chatbox::handleChar(char32 unichar) {
|
||||
//getDebugStream() << unichar << std::endl;
|
||||
if (unichar >= ' ' && unichar <= '~') { // ASCII range
|
||||
// TODO: Update when libstdc++ supports locale codecvt facets
|
||||
//std::codecvt_utf8<char32_t> convert32;
|
||||
m_chatString.append(1, (char)unichar);
|
||||
m_chatText->setText(m_chatString);
|
||||
}
|
||||
}
|
||||
|
||||
void Chatbox::handleKey(int key, int scancode, int action, int mods) {
|
||||
if (action == GLFW_PRESS && key == GLFW_KEY_BACKSPACE) {
|
||||
if (m_chatString.size() > 0) {
|
||||
m_chatString.erase(m_chatString.end()-1);
|
||||
m_chatText->setText(m_chatString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Chatbox::render() {
|
||||
if (m_isChatting) {
|
||||
RenderProgram->bind();
|
||||
m_vbo.bind();
|
||||
glEnableVertexAttribArray(RenderProgram_coord);
|
||||
glEnableVertexAttribArray(RenderProgram_color);
|
||||
glUniformMatrix4fv(RenderProgram_mvp, 1, GL_FALSE, glm::value_ptr(*G->GW->UIM.PM));
|
||||
glVertexAttribPointer(RenderProgram_coord, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
|
||||
glVertexAttribPointer(RenderProgram_color, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, r));
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
glDisableVertexAttribArray(RenderProgram_color);
|
||||
glDisableVertexAttribArray(RenderProgram_coord);
|
||||
|
||||
m_chatText->render();
|
||||
}
|
||||
|
||||
int totalHeight = 0;
|
||||
for (const ChatEntry &entry : m_chatEntries)
|
||||
totalHeight += entry.height;
|
||||
totalHeight *= 2;
|
||||
glm::mat4 msgMatrix = glm::scale(glm::translate(*G->GW->UIM.PM, glm::vec3(m_posX, m_posY+totalHeight, 0)), glm::vec3(2));
|
||||
for (const ChatEntry &entry : m_chatEntries) {
|
||||
msgMatrix = glm::translate(msgMatrix, glm::vec3(0, -entry.height, 0));
|
||||
entry.text->render(msgMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
#ifndef CHATBOX_HPP
|
||||
#define CHATBOX_HPP
|
||||
#include <chrono>
|
||||
#include <list>
|
||||
#include <glm/glm.hpp>
|
||||
#include "Platform.hpp"
|
||||
#include "ui/Text.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class Program;
|
||||
|
||||
class Game;
|
||||
|
||||
class Chatbox {
|
||||
public:
|
||||
typedef std::chrono::system_clock system_clock;
|
||||
typedef std::chrono::time_point<system_clock> time_point;
|
||||
|
||||
private:
|
||||
static const Program *RenderProgram;
|
||||
static GLint RenderProgram_coord, RenderProgram_color, RenderProgram_mvp;
|
||||
|
||||
bool m_isChatting;
|
||||
Game *G;
|
||||
struct ChatEntry {
|
||||
time_point date;
|
||||
int height;
|
||||
UI::Text *text;
|
||||
|
||||
~ChatEntry();
|
||||
};
|
||||
std::list<ChatEntry> m_chatEntries;
|
||||
// TODO: Update when libstdc++ supports locale codecvt facets
|
||||
//std::u32string m_chatString;
|
||||
std::string m_chatString;
|
||||
UI::Text *m_chatText;
|
||||
struct Vertex {
|
||||
float x, y, r, g, b, a;
|
||||
};
|
||||
VBO m_vbo;
|
||||
int m_posX, m_posY;
|
||||
|
||||
public:
|
||||
Chatbox(Game *G);
|
||||
~Chatbox();
|
||||
|
||||
bool isChatting() const;
|
||||
void setIsChatting(bool value);
|
||||
|
||||
void addChatEntry(const std::string& text);
|
||||
|
||||
void handleChar(char32 unichar);
|
||||
void handleKey(int key, int scancode, int action, int mods);
|
||||
|
||||
void setPosition(int x, int y);
|
||||
|
||||
void render();
|
||||
|
||||
std::string getChatString() const { return m_chatString; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,265 @@
|
|||
#include "Chunk.hpp"
|
||||
#include "Platform.hpp"
|
||||
#include "GlobalProperties.hpp"
|
||||
#include "Blocks.hpp"
|
||||
#include "Game.hpp"
|
||||
#include "ChunkChangeHelper.hpp"
|
||||
#include <cstring>
|
||||
#include <cstddef>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#define CXY (CX*CY)
|
||||
#define I(x,y,z) (x+y*CX+z*CXY)
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
const Program *Chunk::RenderProgram = nullptr;
|
||||
Texture *Chunk::TextureAtlas = nullptr;
|
||||
Blocks *Chunk::BlkInf = nullptr;
|
||||
GLint Chunk::RenderProgram_attrib_texcoord = -1;
|
||||
GLint Chunk::RenderProgram_attrib_coord = -1;
|
||||
GLint Chunk::RenderProgram_attrib_color = -1;
|
||||
GLint Chunk::RenderProgram_uni_mvp = -1;
|
||||
|
||||
constexpr float Chunk::CullSphereRadius;
|
||||
constexpr float Chunk::MidX, Chunk::MidY, Chunk::MidZ;
|
||||
|
||||
Chunk::Chunk(bool buffer, int scx, int scy, int scz, Game *G) : blk2(nullptr),
|
||||
scx(scx), scy(scy), scz(scz), G(G), vbo(nullptr), lavaCount(0) {
|
||||
changed = true;
|
||||
blk = new BlockType[CX*CY*CZ];
|
||||
for (int i=0; i < CX*CY*CZ; ++i)
|
||||
blk[i] = BlockType::Air;
|
||||
|
||||
if (!buffer && GlobalProperties::IsClient) {
|
||||
vbo = new VBO;
|
||||
ibo = new VBO;
|
||||
if (RenderProgram == nullptr) {
|
||||
RenderProgram = G->PM->getProgram(PM_3D | PM_TEXTURED | PM_COLORED | PM_FOG);
|
||||
RenderProgram_attrib_coord = RenderProgram->att("coord");
|
||||
RenderProgram_attrib_color = RenderProgram->att("color");
|
||||
RenderProgram_attrib_texcoord = RenderProgram->att("texcoord");
|
||||
RenderProgram_uni_mvp = RenderProgram->uni("mvp");
|
||||
|
||||
BlkInf = new Blocks();
|
||||
|
||||
TextureAtlas = BlkInf->getAtlas();
|
||||
}
|
||||
}
|
||||
if (GlobalProperties::IsServer) {
|
||||
blk2 = new BlockType[CX*CY*CZ];
|
||||
}
|
||||
}
|
||||
|
||||
Chunk::~Chunk() {
|
||||
delete blk;
|
||||
delete[] blk2;
|
||||
delete vbo; delete ibo;
|
||||
}
|
||||
|
||||
BlockType Chunk::get(int x, int y, int z) {
|
||||
if ((x < 0 || y < 0 || z < 0 || x >= CX || y >= CY || z >= CZ) && G) {
|
||||
return G->SC->get(scx * CX + x, scy * CY + y, scz * CZ + z);
|
||||
}
|
||||
return blk[I(x,y,z)];
|
||||
}
|
||||
|
||||
void Chunk::set2(int x, int y, int z, BlockType type) {
|
||||
if ((x < 0 || y < 0 || z < 0 || x >= CX || y >= CY || z >= CZ) && G)
|
||||
return G->SC->set2(scx * CX + x, scy * CY + y, scz * CZ + z, type);
|
||||
register BlockType *b = &(blk2[I(x,y,z)]);
|
||||
if (*b == BlockType::Lava)
|
||||
lavaCount--;
|
||||
if (type == BlockType::Lava)
|
||||
lavaCount++;
|
||||
*b = type;
|
||||
if (G && G->CCH)
|
||||
G->CCH->add(scx * CX + x, scy * CY + y, scz * CZ + z, type);
|
||||
}
|
||||
|
||||
void Chunk::set(int x, int y, int z, BlockType type) {
|
||||
mut.lock();
|
||||
if ((x < 0 || y < 0 || z < 0 || x >= CX || y >= CY || z >= CZ) && G)
|
||||
return G->SC->set(scx * CX + x, scy * CY + y, scz * CZ + z, type);
|
||||
register BlockType *b = &(blk[I(x,y,z)]);
|
||||
if (*b == BlockType::Lava)
|
||||
lavaCount--;
|
||||
if (type == BlockType::Lava)
|
||||
lavaCount++;
|
||||
*b = type;
|
||||
changed = true;
|
||||
mut.unlock();
|
||||
}
|
||||
|
||||
void Chunk::updateServerPrepare() {
|
||||
memcpy(blk2, blk, CX*CY*CZ*sizeof(BlockType));
|
||||
}
|
||||
|
||||
void Chunk::updateServer() {
|
||||
if (lavaCount == 0)
|
||||
return;
|
||||
mut.lock();
|
||||
for (int x=0; x < CX; x++)
|
||||
for (int y=0; y < CY; y++)
|
||||
for (int z=0; z < CZ; z++) {
|
||||
if (blk[I(x,y,z)] == BlockType::Lava) {
|
||||
BlockType under = get(x, y-1, z);
|
||||
if (under == BlockType::Air) {
|
||||
set2(x, y-1, z, BlockType::Lava);
|
||||
} else if (under != BlockType::Lava) {
|
||||
if (get(x+1, y, z) == BlockType::Air)
|
||||
set2(x+1, y, z, BlockType::Lava);
|
||||
if (get(x-1, y, z) == BlockType::Air)
|
||||
set2(x-1, y, z, BlockType::Lava);
|
||||
if (get(x, y, z+1) == BlockType::Air)
|
||||
set2(x, y, z+1, BlockType::Lava);
|
||||
if (get(x, y, z-1) == BlockType::Air)
|
||||
set2(x, y, z-1, BlockType::Lava);
|
||||
}
|
||||
}
|
||||
}
|
||||
mut.unlock();
|
||||
}
|
||||
|
||||
void Chunk::updateServerSwap() {
|
||||
std::swap(blk, blk2);
|
||||
}
|
||||
|
||||
void Chunk::updateClient() {
|
||||
mut.lock();
|
||||
GLCoord vertex[CX * CY * CZ * 6 /* faces */ * 6 /* vertices */ / 2 /* face removing (HSR) makes a lower vert max */];
|
||||
GLushort index[CX * CY * CZ * 6 /* faces */ * 4 /* indexes */ / 2 /* HSR */];
|
||||
int v = 0, i = 0;
|
||||
|
||||
BlockType bt;
|
||||
const AtlasCreator::Coord *tc;
|
||||
for(uint8 x = 0; x < CX; x++) {
|
||||
for(uint8 y = 0; y < CY; y++) {
|
||||
for(uint8 z = 0; z < CZ; z++) {
|
||||
bt = blk[I(x,y,z)];
|
||||
|
||||
// Empty block?
|
||||
if (!bt)
|
||||
continue;
|
||||
|
||||
// View from negative x
|
||||
if (Blocks::isFaceRemoved(bt, get(x - 1, y, z))) {
|
||||
index[i++] = v; index[i++] = v+1; index[i++] = v+2;
|
||||
index[i++] = v+3; index[i++] = v+2; index[i++] = v+1;
|
||||
tc = BlkInf->gTC(bt, FaceDirection::XDec);
|
||||
vertex[v++] = {x, y, z, tc->x, tc->v, .6f, .6f, .6f};
|
||||
vertex[v++] = {x, y, z + 1, tc->u, tc->v, .6f, .6f, .6f};
|
||||
vertex[v++] = {x, y + 1, z, tc->x, tc->y, .6f, .6f, .6f};
|
||||
vertex[v++] = {x, y + 1, z + 1, tc->u, tc->y, .6f, .6f, .6f};
|
||||
}
|
||||
|
||||
// View from positive x
|
||||
if (Blocks::isFaceRemoved(bt, get(x + 1, y, z))) {
|
||||
index[i++] = v; index[i++] = v+1; index[i++] = v+2;
|
||||
index[i++] = v+3; index[i++] = v+2; index[i++] = v+1;
|
||||
tc = BlkInf->gTC(bt, FaceDirection::XInc);
|
||||
vertex[v++] = {x + 1, y, z, tc->u, tc->v, .6f, .6f, .6f};
|
||||
vertex[v++] = {x + 1, y + 1, z, tc->u, tc->y, .6f, .6f, .6f};
|
||||
vertex[v++] = {x + 1, y, z + 1, tc->x, tc->v, .6f, .6f, .6f};
|
||||
vertex[v++] = {x + 1, y + 1, z + 1, tc->x, tc->y, .6f, .6f, .6f};
|
||||
}
|
||||
|
||||
// Negative Y
|
||||
if (Blocks::isFaceRemoved(bt, get(x, y - 1, z))) {
|
||||
index[i++] = v; index[i++] = v+1; index[i++] = v+2;
|
||||
index[i++] = v+3; index[i++] = v+2; index[i++] = v+1;
|
||||
float shade = (blk[I(x,y,z)] == BlockType::Shock) ? 1.5f : .2f;;
|
||||
tc = BlkInf->gTC(bt, FaceDirection::YDec);
|
||||
vertex[v++] = {x, y, z, tc->x, tc->v, shade, shade, shade};
|
||||
vertex[v++] = {x + 1, y, z, tc->u, tc->v, shade, shade, shade};
|
||||
vertex[v++] = {x, y, z + 1, tc->x, tc->y, shade, shade, shade};
|
||||
vertex[v++] = {x + 1, y, z + 1, tc->u, tc->y, shade, shade, shade};
|
||||
}
|
||||
|
||||
// Positive Y
|
||||
if (Blocks::isFaceRemoved(bt, get(x, y + 1, z))) {
|
||||
index[i++] = v; index[i++] = v+1; index[i++] = v+2;
|
||||
index[i++] = v+3; index[i++] = v+2; index[i++] = v+1;
|
||||
tc = BlkInf->gTC(bt, FaceDirection::YInc);
|
||||
vertex[v++] = {x, y + 1, z, tc->u, tc->v, .8f, .8f, .8f};
|
||||
vertex[v++] = {x, y + 1, z + 1, tc->u, tc->y, .8f, .8f, .8f};
|
||||
vertex[v++] = {x + 1, y + 1, z, tc->x, tc->v, .8f, .8f, .8f};
|
||||
vertex[v++] = {x + 1, y + 1, z + 1, tc->x, tc->y, .8f, .8f, .8f};
|
||||
}
|
||||
|
||||
// Negative Z
|
||||
if (Blocks::isFaceRemoved(bt, get(x, y, z - 1))) {
|
||||
index[i++] = v; index[i++] = v+1; index[i++] = v+2;
|
||||
index[i++] = v+3; index[i++] = v+2; index[i++] = v+1;
|
||||
tc = BlkInf->gTC(bt, FaceDirection::ZDec);
|
||||
vertex[v++] = {x, y, z, tc->u, tc->v, .4f, .4f, .4f};
|
||||
vertex[v++] = {x, y + 1, z, tc->u, tc->y, .4f, .4f, .4f};
|
||||
vertex[v++] = {x + 1, y, z, tc->x, tc->v, .4f, .4f, .4f};
|
||||
vertex[v++] = {x + 1, y + 1, z, tc->x, tc->y, .4f, .4f, .4f};
|
||||
}
|
||||
|
||||
// Positive Z
|
||||
if (Blocks::isFaceRemoved(bt, get(x, y, z + 1))) {
|
||||
index[i++] = v; index[i++] = v+1; index[i++] = v+2;
|
||||
index[i++] = v+3; index[i++] = v+2; index[i++] = v+1;
|
||||
tc = BlkInf->gTC(bt, FaceDirection::ZInc);
|
||||
vertex[v++] = {x, y, z + 1, tc->x, tc->v, .4f, .4f, .4f};
|
||||
vertex[v++] = {x + 1, y, z + 1, tc->u, tc->v, .4f, .4f, .4f};
|
||||
vertex[v++] = {x, y + 1, z + 1, tc->x, tc->y, .4f, .4f, .4f};
|
||||
vertex[v++] = {x + 1, y + 1, z + 1, tc->u, tc->y, .4f, .4f, .4f};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vertices = v;
|
||||
vbo->setData(vertex, v);
|
||||
indices = i;
|
||||
ibo->setData(index, i);
|
||||
changed = false;
|
||||
mut.unlock();
|
||||
}
|
||||
|
||||
void Chunk::render(const glm::mat4 &transform) {
|
||||
if (changed)
|
||||
updateClient();
|
||||
if (!indices)
|
||||
return;
|
||||
|
||||
RenderProgram->bind();
|
||||
|
||||
glEnableVertexAttribArray(RenderProgram_attrib_coord);
|
||||
glEnableVertexAttribArray(RenderProgram_attrib_texcoord);
|
||||
glEnableVertexAttribArray(RenderProgram_attrib_color);
|
||||
glUniformMatrix4fv(RenderProgram_uni_mvp, 1, GL_FALSE, glm::value_ptr(transform));
|
||||
|
||||
TextureAtlas->bind();
|
||||
vbo->bind();
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo->id);
|
||||
glVertexAttribPointer(RenderProgram_attrib_coord, 3, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(GLCoord), 0);
|
||||
glVertexAttribPointer(RenderProgram_attrib_texcoord, 2, GL_UNSIGNED_SHORT, GL_TRUE, sizeof(GLCoord), (GLvoid*)offsetof(GLCoord, tx));
|
||||
glVertexAttribPointer(RenderProgram_attrib_color, 3, GL_FLOAT, GL_FALSE, sizeof(GLCoord), (GLvoid*)offsetof(GLCoord, r));
|
||||
glDrawElements(GL_TRIANGLES, indices, GL_UNSIGNED_SHORT, nullptr);
|
||||
|
||||
glDisableVertexAttribArray(RenderProgram_attrib_color);
|
||||
glDisableVertexAttribArray(RenderProgram_attrib_texcoord);
|
||||
glDisableVertexAttribArray(RenderProgram_attrib_coord);
|
||||
}
|
||||
|
||||
void Chunk::renderBatched(const glm::mat4& transform) {
|
||||
if (changed)
|
||||
updateClient();
|
||||
if (!indices)
|
||||
return;
|
||||
|
||||
glUniformMatrix4fv(RenderProgram_uni_mvp, 1, GL_FALSE, glm::value_ptr(transform));
|
||||
vbo->bind();
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo->id);
|
||||
glVertexAttribPointer(RenderProgram_attrib_coord, 3, GL_BYTE, GL_FALSE, sizeof(GLCoord), 0);
|
||||
glVertexAttribPointer(RenderProgram_attrib_texcoord, 2, GL_UNSIGNED_SHORT, GL_TRUE, sizeof(GLCoord), (GLvoid*)offsetof(GLCoord, tx));
|
||||
glVertexAttribPointer(RenderProgram_attrib_color, 3, GL_FLOAT, GL_FALSE, sizeof(GLCoord), (GLvoid*)offsetof(GLCoord, r));
|
||||
glDrawElements(GL_TRIANGLES, indices, GL_UNSIGNED_SHORT, nullptr);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
#ifndef CHUNK_HPP
|
||||
#define CHUNK_HPP
|
||||
#include <GL/glew.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include "VBO.hpp"
|
||||
#include "Program.hpp"
|
||||
#include "Texture.hpp"
|
||||
#include "Blocks.hpp"
|
||||
#include "Mutex.hpp"
|
||||
|
||||
#define CX 16
|
||||
#define CY 16
|
||||
#define CZ 16
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class Blocks;
|
||||
class Game;
|
||||
|
||||
class Chunk /*: Renderable*/ {
|
||||
private:
|
||||
friend class Superchunk;
|
||||
|
||||
BlockType *blk2;
|
||||
Mutex mut;
|
||||
void set2(int x, int y, int z, BlockType type);
|
||||
|
||||
public:
|
||||
struct GLCoord {
|
||||
uint8 x, y, z;
|
||||
uint16 tx, ty;
|
||||
float r, g, b;
|
||||
};
|
||||
constexpr static float CullSphereRadius = (CZ > (CX > CY ? CX : CY) ? CZ : (CX > CY ? CX : CY));// * 1.4142135623f; // sqrt(2)
|
||||
constexpr static float MidX = CX/2.f, MidY = CY/2.f, MidZ = CZ/2.f;
|
||||
static const Program *RenderProgram;
|
||||
static Texture *TextureAtlas;
|
||||
static Blocks *BlkInf;
|
||||
static GLint RenderProgram_attrib_coord, RenderProgram_attrib_color, RenderProgram_attrib_texcoord, RenderProgram_uni_mvp;
|
||||
BlockType *blk;
|
||||
int scx, scy, scz;
|
||||
Game *G;
|
||||
VBO *vbo, *ibo;
|
||||
int vertices, indices;
|
||||
bool changed;
|
||||
int lavaCount;
|
||||
|
||||
/// @param buffer Wether the chunk is just a buffer chunk
|
||||
Chunk(bool buffer = false, int scx = -1, int scy = -1, int scz = -1, Game *G = nullptr);
|
||||
~Chunk();
|
||||
BlockType get(int x, int y, int z);
|
||||
void set(int x, int y, int z, BlockType type);
|
||||
void updateClient();
|
||||
void updateServerPrepare();
|
||||
void updateServer();
|
||||
void updateServerSwap();
|
||||
void render(const glm::mat4 &transform);
|
||||
void renderBatched(const glm::mat4 &transform);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,41 @@
|
|||
#include "ChunkChangeHelper.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
ChunkChangeHelper::ChunkChangeHelper() {
|
||||
m_changes.reserve(64);
|
||||
}
|
||||
|
||||
void ChunkChangeHelper::add(int x, int y, int z, BlockType b) {
|
||||
for (Change &c : m_changes) {
|
||||
if (c.x == x && c.y == y && c.z == z) {
|
||||
c.b = b;
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_changes.emplace_back(Change {x, y, z, b});
|
||||
}
|
||||
|
||||
void ChunkChangeHelper::flush(Net::OutMessage &msg) {
|
||||
for (Change &c : m_changes) {
|
||||
msg.writeU16(c.x);
|
||||
msg.writeU16(c.y);
|
||||
msg.writeU16(c.z);
|
||||
msg.writeU8(static_cast<uint8>(c.b));
|
||||
}
|
||||
m_changes.clear();
|
||||
}
|
||||
|
||||
int ChunkChangeHelper::count() const {
|
||||
return m_changes.size();
|
||||
}
|
||||
|
||||
bool ChunkChangeHelper::empty() const {
|
||||
return m_changes.empty();
|
||||
}
|
||||
|
||||
void ChunkChangeHelper::discard(){
|
||||
m_changes.clear();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef CHUNK_CHANGE_HELPER_HPP
|
||||
#define CHUNK_CHANGE_HELPER_HPP
|
||||
#include "Blocks.hpp"
|
||||
#include "network/Network.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class ChunkChangeHelper {
|
||||
private:
|
||||
struct Change {
|
||||
int x, y, z;
|
||||
BlockType b;
|
||||
};
|
||||
std::vector<Change> m_changes;
|
||||
|
||||
public:
|
||||
ChunkChangeHelper();
|
||||
void add(int x, int y, int z, BlockType b);
|
||||
bool empty() const;
|
||||
int count() const;
|
||||
void flush(Net::OutMessage&);
|
||||
void discard();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,79 @@
|
|||
#include "Clouds.hpp"
|
||||
#include "Texture.hpp"
|
||||
#include "Platform.hpp"
|
||||
#include "Program.hpp"
|
||||
#include "Game.hpp"
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
const Program *Clouds::RenderProgram = nullptr;
|
||||
GLint Clouds::RenderProgram_attrib_texcoord = -1;
|
||||
GLint Clouds::RenderProgram_attrib_coord = -1;
|
||||
GLint Clouds::RenderProgram_uni_mvp = -1;
|
||||
GLint Clouds::RenderProgram_uni_texshift = -1;
|
||||
|
||||
Clouds::Clouds(Game *G, int w, int h, int layers) : m_layers(layers), G(G) {
|
||||
if (RenderProgram == nullptr) {
|
||||
RenderProgram = G->PM->getSpecialProgram("clouds"); //PM_3D | PM_TEXTURED | PM_TEXSHIFT);
|
||||
RenderProgram_attrib_coord = RenderProgram->att("coord");
|
||||
RenderProgram_attrib_texcoord = RenderProgram->att("texcoord");
|
||||
RenderProgram_uni_texshift = RenderProgram->uni("texshift");
|
||||
RenderProgram_uni_mvp = RenderProgram->uni("mvp");
|
||||
}
|
||||
|
||||
m_tex = new Texture*[m_layers];
|
||||
uint8 *data = new uint8[w * h * 4];
|
||||
for (int i=0; i < m_layers; i++) {
|
||||
for (int x=0; x < w; x++) {
|
||||
for (int y=0; y < h; y++) {
|
||||
data[(x+y*w)*4+0] = data[(x+y*w)*4+1] = data[(x+y*w)*4+2] = 255;
|
||||
data[(x+y*w)*4+3] = (FastRand(0, 255) > 230) ? 180 : 0;
|
||||
}
|
||||
}
|
||||
m_tex[i] = new Texture(w, h, data, Texture::PixelFormat::RGBA); //new Texture(getAssetPath("clouds.png"), Texture::PixelFormat::RGBA);
|
||||
}
|
||||
delete[] data;
|
||||
|
||||
Coord coords[m_layers * 6];
|
||||
for (int i=0; i < m_layers; i++) {
|
||||
coords[i*6+2] = {0, i, 0, 0, 0};
|
||||
|
||||
coords[i*6+1] = coords[i*6+3] = {0, i, 1, 0, 1};
|
||||
coords[i*6+0] = coords[i*6+4] = {1, i, 0, 1, 0};
|
||||
|
||||
coords[i*6+5] = {1, i, 1, 1, 1};
|
||||
}
|
||||
m_vbo.setData(coords, m_layers * 6);
|
||||
}
|
||||
|
||||
void Clouds::render(const glm::mat4 &transform) {
|
||||
RenderProgram->bind();
|
||||
|
||||
glEnableVertexAttribArray(RenderProgram_attrib_coord);
|
||||
glEnableVertexAttribArray(RenderProgram_attrib_texcoord);
|
||||
|
||||
m_vbo.bind();
|
||||
glVertexAttribPointer(RenderProgram_attrib_coord, 3, GL_BYTE, GL_FALSE, sizeof(Coord), 0);
|
||||
glVertexAttribPointer(RenderProgram_attrib_texcoord, 2, GL_BYTE, GL_FALSE, sizeof(Coord), (GLvoid*)offsetof(Coord, u));
|
||||
|
||||
float shift = G->Time/128;
|
||||
for (int i=m_layers-1; i >= 0; i--) {
|
||||
glUniformMatrix4fv(RenderProgram_uni_mvp, 1, GL_FALSE, glm::value_ptr(transform));
|
||||
glUniform2f(RenderProgram_uni_texshift, shift, shift);
|
||||
m_tex[i]->bind();
|
||||
glDrawArrays(GL_TRIANGLES, i*6, 6);
|
||||
}
|
||||
|
||||
glDisableVertexAttribArray(RenderProgram_attrib_texcoord);
|
||||
glDisableVertexAttribArray(RenderProgram_attrib_coord);
|
||||
}
|
||||
|
||||
Clouds::~Clouds() {
|
||||
for (int i=0; i < m_layers; i++) {
|
||||
delete m_tex[i];
|
||||
}
|
||||
delete[] m_tex;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef CLOUDS_HPP
|
||||
#define CLOUDS_HPP
|
||||
#include <glm/glm.hpp>
|
||||
#include <GL/glew.h>
|
||||
#include "VBO.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class Game;
|
||||
class Program;
|
||||
class Texture;
|
||||
|
||||
class Clouds {
|
||||
private:
|
||||
static const Program *RenderProgram;
|
||||
static GLint RenderProgram_attrib_coord, RenderProgram_attrib_texcoord, RenderProgram_uni_mvp, RenderProgram_uni_texshift;
|
||||
Texture **m_tex;
|
||||
VBO m_vbo;
|
||||
int m_layers;
|
||||
Game *G;
|
||||
struct Coord { uint8 x, y, z, u, v; };
|
||||
|
||||
public:
|
||||
Clouds(Game *G, int w, int h, int layers);
|
||||
void render(const glm::mat4 &transform);
|
||||
~Clouds();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,64 @@
|
|||
#include "EscMenu.hpp"
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include "Game.hpp"
|
||||
#include "ui/Text.hpp"
|
||||
#include "ui/Button.hpp"
|
||||
#include "GameWindow.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
const Program *EscMenu::RenderProgram = nullptr;
|
||||
GLint EscMenu::RenderProgram_att_color = -1;
|
||||
GLint EscMenu::RenderProgram_att_coord = -1;
|
||||
GLint EscMenu::RenderProgram_uni_mvp = -1;
|
||||
|
||||
static void makeBgVBO(VBO &vbo, int w, int h) {
|
||||
float verts[6*6] = {
|
||||
0.f, 0.f, 0.f, 0.f, 0.f, 0.6f,
|
||||
w , 0.f, 0.f, 0.f, 0.f, 0.6f,
|
||||
0.f, h , 0.f, 0.f, 0.f, 0.6f,
|
||||
|
||||
0.f, h , 0.f, 0.f, 0.f, 0.6f,
|
||||
w , 0.f, 0.f, 0.f, 0.f, 0.6f,
|
||||
w , h , 0.f, 0.f, 0.f, 0.6f,
|
||||
};
|
||||
vbo.setData(verts, 6*6);
|
||||
}
|
||||
|
||||
EscMenu::EscMenu(Game *G) : G(G) {
|
||||
makeBgVBO(vbo_background, G->GW->getW(), G->GW->getH());
|
||||
if (RenderProgram == nullptr) {
|
||||
RenderProgram = G->PM->getProgram(PM_2D | PM_COLORED);
|
||||
RenderProgram_att_coord = RenderProgram->att("coord");
|
||||
RenderProgram_att_color = RenderProgram->att("color");
|
||||
RenderProgram_uni_mvp = RenderProgram->uni("mvp");
|
||||
}
|
||||
|
||||
//txt_quit = new Text();
|
||||
//m_button = new UIButton(G, glm::mat);
|
||||
}
|
||||
|
||||
EscMenu::~EscMenu() {
|
||||
|
||||
}
|
||||
|
||||
void EscMenu::render() {
|
||||
glEnableVertexAttribArray(RenderProgram_att_coord);
|
||||
glEnableVertexAttribArray(RenderProgram_att_color);
|
||||
|
||||
RenderProgram->bind();
|
||||
vbo_background.bind();
|
||||
//glm::mat4 trz = glm::translate(transform, glm::vec3(x/640.f, y/480.f, 0));
|
||||
glUniformMatrix4fv(RenderProgram_uni_mvp, 1, GL_FALSE, glm::value_ptr(*G->GW->UIM.PM));
|
||||
glVertexAttribPointer(RenderProgram_att_coord, 2, GL_FLOAT, GL_FALSE, 6*sizeof(float), 0);
|
||||
glVertexAttribPointer(RenderProgram_att_color, 4, GL_FLOAT, GL_FALSE, 6*sizeof(float), (GLvoid*)(2*sizeof(float)));
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
|
||||
glDisableVertexAttribArray(RenderProgram_att_color);
|
||||
glDisableVertexAttribArray(RenderProgram_att_coord);
|
||||
|
||||
//txt_quit->render();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef ESC_MENU_HPP
|
||||
#define ESC_MENU_HPP
|
||||
#include <GL/glew.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include "VBO.hpp"
|
||||
#include "Program.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class Game;
|
||||
namespace UI {
|
||||
class Button;
|
||||
class Text;
|
||||
}
|
||||
|
||||
class EscMenu {
|
||||
private:
|
||||
static const Program *RenderProgram;
|
||||
static GLint RenderProgram_att_coord, RenderProgram_att_color, RenderProgram_uni_mvp;
|
||||
Game *G;
|
||||
VBO vbo_background;
|
||||
UI::Text *txt_quit;
|
||||
glm::mat4 matrix;
|
||||
UI::Button *m_button;
|
||||
|
||||
public:
|
||||
EscMenu(Game *G);
|
||||
~EscMenu();
|
||||
void render();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,69 @@
|
|||
#include "FBO.hpp"
|
||||
#include "GlUtils.hpp"
|
||||
#ifdef IN_IDE_PARSER
|
||||
void glCheck();
|
||||
#else
|
||||
#define glCheck() { GLenum glErr = glGetError(); \
|
||||
if (glErr) { \
|
||||
getDebugStream() << GlUtils::getErrorString(glErr) << std::endl; \
|
||||
} glErr = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (glErr != GL_FRAMEBUFFER_COMPLETE) { \
|
||||
getDebugStream() << GlUtils::getErrorString(glErr) << std::endl; \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
|
||||
Diggler::FBO::FBO(int w, int h, Diggler::Texture::PixelFormat format, bool stencil) : m_hasStencil(stencil) {
|
||||
GLint currentBoundFBO; glGetIntegerv(GL_FRAMEBUFFER_BINDING, ¤tBoundFBO);
|
||||
GLint currentBoundRBO; glGetIntegerv(GL_RENDERBUFFER_BINDING, ¤tBoundRBO);
|
||||
|
||||
tex = new Texture(w, h, format, false);
|
||||
glGenFramebuffers(1, &id);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, id);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *tex, 0);
|
||||
GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0};
|
||||
glDrawBuffers(1, DrawBuffers);
|
||||
|
||||
glGenRenderbuffers(1, &rboId);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, rboId);
|
||||
if (stencil)
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h);
|
||||
else
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, w, h);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, stencil ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboId);
|
||||
|
||||
glCheck();
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, currentBoundFBO);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, currentBoundRBO);
|
||||
}
|
||||
|
||||
void Diggler::FBO::resize(int w, int h) {
|
||||
GLint currentBoundRBO; glGetIntegerv(GL_RENDERBUFFER_BINDING, ¤tBoundRBO);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, rboId);
|
||||
if (m_hasStencil)
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h);
|
||||
else
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, w, h);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, currentBoundRBO);
|
||||
|
||||
tex->resize(w, h);
|
||||
}
|
||||
|
||||
bool Diggler::FBO::hasStencil() const {
|
||||
return m_hasStencil;
|
||||
}
|
||||
|
||||
void Diggler::FBO::bind() {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, id);
|
||||
}
|
||||
|
||||
void Diggler::FBO::unbind() {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
Diggler::FBO::~FBO() {
|
||||
glDeleteFramebuffers(1, &id);
|
||||
delete tex;
|
||||
glDeleteRenderbuffers(1, &rboId);
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef FBO_HPP
|
||||
#define FBO_HPP
|
||||
#include <vector>
|
||||
#include <GL/glew.h>
|
||||
#include <typeinfo>
|
||||
#include "Platform.hpp"
|
||||
#include "Texture.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class FBO {
|
||||
private:
|
||||
bool m_hasStencil;
|
||||
|
||||
public:
|
||||
Texture *tex;
|
||||
GLuint id, rboId;
|
||||
FBO(int w = 640, int h = 480, Texture::PixelFormat format = Texture::PixelFormat::RGB, bool stencil = false);
|
||||
operator GLuint() const { return id; }
|
||||
void resize(int w, int h);
|
||||
void bind();
|
||||
void unbind();
|
||||
bool hasStencil() const;
|
||||
~FBO();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,174 @@
|
|||
#include "Font.hpp"
|
||||
#include <fstream>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include "Texture.hpp"
|
||||
#include "Program.hpp"
|
||||
#include "VBO.hpp"
|
||||
#include "Game.hpp"
|
||||
#include "GlUtils.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
Program const *Font::RenderProgram = nullptr;
|
||||
GLint Font::RenderProgram_uni_mvp = -1;
|
||||
GLint Font::RenderProgram_att_coord = -1;
|
||||
GLint Font::RenderProgram_att_texcoord = -1;
|
||||
GLint Font::RenderProgram_att_color = -1;
|
||||
struct { float r, g, b; } ColorTable[16] = {
|
||||
{1.0f, 1.0f, 1.0f},
|
||||
{0.66f, 0.66f, 0.66f},
|
||||
{0.33f, 0.33f, 0.33f},
|
||||
{0.0f, 0.0f, 0.0f},
|
||||
{1.0f, 0.0f, 0.0f},
|
||||
{1.0f, 0.5f, 0.0f},
|
||||
{1.0f, 1.0f, 0.0f},
|
||||
{0.5f, 1.0f, 0.0f},
|
||||
{0.0f, 1.0f, 0.0f},
|
||||
{0.0f, 1.0f, 0.5f},
|
||||
{0.0f, 1.0f, 1.0f},
|
||||
{0.0f, 0.5f, 1.0f},
|
||||
{0.0f, 0.0f, 1.0f},
|
||||
{0.5f, 0.0f, 1.0f},
|
||||
{1.0f, 0.0f, 1.0f},
|
||||
{1.0f, 0.0f, 0.5f}
|
||||
};
|
||||
|
||||
Font::Font(Game *G, const std::string& path) : G(G) {
|
||||
if (!RenderProgram) {
|
||||
RenderProgram = G->PM->getProgram(PM_2D | PM_TEXTURED | PM_COLORED);
|
||||
RenderProgram_att_coord = RenderProgram->att("coord");
|
||||
RenderProgram_att_texcoord = RenderProgram->att("texcoord");
|
||||
RenderProgram_att_color = RenderProgram->att("color");
|
||||
RenderProgram_uni_mvp = RenderProgram->uni("mvp");
|
||||
}
|
||||
m_texture = new Texture(path, Texture::PixelFormat::RGBA);
|
||||
std::ifstream source(path + ".fdf", std::ios_base::binary);
|
||||
if (source.good()) {
|
||||
source.seekg(0, std::ios_base::end);
|
||||
int size = (int)source.tellg() - 1; // Last byte is font's height
|
||||
source.seekg(0, std::ios_base::beg);
|
||||
//widths = new uint8[size];
|
||||
texPos = new CharTexPos[95];
|
||||
uint8 widths[95];
|
||||
std::fill_n(widths, 95, 6);
|
||||
source.read((char*)widths, size);
|
||||
source.read((char*)&height, 1);
|
||||
int left = 0;
|
||||
for (uint8 i=0; i < 95; i++) {
|
||||
texPos[i].width = widths[i];
|
||||
texPos[i].left = (float)left / m_texture->getW();
|
||||
left += widths[i];
|
||||
texPos[i].right = (float)left / m_texture->getW();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Vertex { int x, y; float tx, ty; float r, g, b ,a; };
|
||||
|
||||
//#define eraseCurChar() std::fill_n((char*)&verts[i*6], sizeof(Vertex)*6, 0);
|
||||
#define eraseCurChar() elements -= 6;
|
||||
|
||||
int Font::updateVBO(VBO &vbo, const std::string &text) const {
|
||||
int elements = text.size()*6;
|
||||
Vertex *verts = new Vertex[elements];
|
||||
uint8 c, w; int line = 0, cx = 0, v = 0; float l, r;
|
||||
float cr = 1.0f, cg = 1.0f, cb = 1.0f, ca = 1.0f;
|
||||
for (uint i=0; i < text.size(); i++) {
|
||||
c = text[i];
|
||||
if (c == '\n') {
|
||||
eraseCurChar();
|
||||
line -= height;
|
||||
cx = 0;
|
||||
continue;
|
||||
}
|
||||
if (c == '\f' && i < text.length()) {
|
||||
/* Used to be '§', but wouldn't support UTF8
|
||||
Additionally, \f can mean "format" */
|
||||
eraseCurChar();
|
||||
uint8 arg = text[++i];
|
||||
eraseCurChar();
|
||||
if (arg >= '0' && arg <= '9') {
|
||||
uint8 index = arg - '0';
|
||||
cr = ColorTable[index].r; cg = ColorTable[index].g; cb = ColorTable[index].b;
|
||||
} else if (arg >= 'a' && arg <= 'f') {
|
||||
uint8 index = (arg - 'a') + 10;
|
||||
cr = ColorTable[index].r; cg = ColorTable[index].g; cb = ColorTable[index].b;
|
||||
} else if (arg == 't') { // 't'ransparency
|
||||
uint8 arg2 = text[++i];
|
||||
eraseCurChar();
|
||||
if (arg2 >= 'a' && arg2 <= 'z')
|
||||
ca = (float)(arg2 - 'a')/('z'-'a');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (c < ' ' || c > '~') {
|
||||
eraseCurChar();
|
||||
continue;
|
||||
}
|
||||
w = texPos[c - ' '].width;
|
||||
l = texPos[c - ' '].left;
|
||||
r = texPos[c - ' '].right;
|
||||
verts[v+0] = { cx, line, l, 1.0, cr, cg, cb, ca };
|
||||
verts[v+1] = { cx+w, line+height, r, 0.0, cr, cg, cb, ca };
|
||||
verts[v+2] = { cx, line+height, l, 0.0, cr, cg, cb, ca };
|
||||
verts[v+3] = { cx, line, l, 1.0, cr, cg, cb, ca };
|
||||
verts[v+4] = { cx+w, line, r, 1.0, cr, cg, cb, ca };
|
||||
verts[v+5] = { cx+w, line+height, r, 0.0, cr, cg, cb, ca };
|
||||
v += 6;
|
||||
cx += w;
|
||||
}
|
||||
vbo.setData(verts, elements, GL_STATIC_DRAW);
|
||||
delete[] verts;
|
||||
return elements;
|
||||
}
|
||||
|
||||
void Font::draw(const Diggler::VBO &vbo, int count, const glm::mat4& matrix) const {
|
||||
glEnableVertexAttribArray(RenderProgram_att_coord);
|
||||
glEnableVertexAttribArray(RenderProgram_att_texcoord);
|
||||
glEnableVertexAttribArray(RenderProgram_att_color);
|
||||
|
||||
RenderProgram->bind();
|
||||
m_texture->bind();
|
||||
vbo.bind();
|
||||
glUniformMatrix4fv(RenderProgram_uni_mvp, 1, GL_FALSE, glm::value_ptr(matrix));
|
||||
glVertexAttribPointer(RenderProgram_att_coord, 2, GL_INT, GL_FALSE, sizeof(Vertex), 0);
|
||||
glVertexAttribPointer(RenderProgram_att_texcoord, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, tx));
|
||||
glVertexAttribPointer(RenderProgram_att_color, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, r));
|
||||
glDrawArrays(GL_TRIANGLES, 0, count);
|
||||
|
||||
glDisableVertexAttribArray(RenderProgram_att_color);
|
||||
glDisableVertexAttribArray(RenderProgram_att_texcoord);
|
||||
glDisableVertexAttribArray(RenderProgram_att_coord);
|
||||
}
|
||||
|
||||
Font::Size Font::getSize(const std::string &text) const {
|
||||
uint8 c; int x = 0, y = height, cx = 0;
|
||||
for (uint i=0; i < text.size(); i++) {
|
||||
c = text[i];
|
||||
if (c == '\n') {
|
||||
y += height;
|
||||
if (cx > x)
|
||||
x = cx;
|
||||
cx = 0;
|
||||
continue;
|
||||
}
|
||||
if (c == '\f' && i < text.length()) { /* 167 = '§' */
|
||||
uint8 arg = text[++i];
|
||||
if (arg == 't') {
|
||||
i++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
x += texPos[c - ' '].width;
|
||||
}
|
||||
return Size { x, y };
|
||||
}
|
||||
|
||||
Font::~Font() {
|
||||
delete m_texture;
|
||||
if (texPos)
|
||||
delete[] texPos;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
#ifndef FONT_HPP
|
||||
#define FONT_HPP
|
||||
#include <GL/glew.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include "Platform.hpp"
|
||||
#include "VBO.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class Texture;
|
||||
class Program;
|
||||
class Game;
|
||||
|
||||
class Font {
|
||||
private:
|
||||
Texture *m_texture;
|
||||
struct CoordInfo {
|
||||
float x1, y1, x2, y2;
|
||||
} *m_coords;
|
||||
struct CharTexPos {
|
||||
int width;
|
||||
float left, right;
|
||||
} *texPos;
|
||||
uint8 height;
|
||||
static Program const *RenderProgram;
|
||||
static GLint RenderProgram_uni_mvp, RenderProgram_att_coord, RenderProgram_att_texcoord, RenderProgram_att_color;
|
||||
Game *G;
|
||||
|
||||
Font(const Font &other) = delete;
|
||||
|
||||
public:
|
||||
struct Size { int x, y; };
|
||||
|
||||
Font(Game *G, const std::string &path);
|
||||
|
||||
/// Updates the text VBO
|
||||
/// @returns the number of elements in the VBO
|
||||
///
|
||||
int updateVBO(VBO &vbo, const std::string &text) const;
|
||||
void draw(const Diggler::VBO &vbo, int count, const glm::mat4& matrix) const;
|
||||
Size getSize(const std::string &text) const;
|
||||
~Font();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,99 @@
|
|||
#include "Frustum.hpp"
|
||||
#include <cmath>
|
||||
#include <glm/glm.hpp>
|
||||
#define ANG2RAD 3.14159265358979323846/180.0
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
Frustum::Frustum() {
|
||||
|
||||
}
|
||||
|
||||
Frustum::~Frustum() {
|
||||
|
||||
}
|
||||
|
||||
void Frustum::setCamInternals(float rad, float ratio, float nearD, float farD) {
|
||||
this->ratio = ratio;
|
||||
this->angle = rad;
|
||||
this->nearD = nearD;
|
||||
this->farD = farD;
|
||||
|
||||
// Compute ALL the things
|
||||
tang = (float)std::tan(rad * 0.5f);
|
||||
nh = nearD * tang;
|
||||
nw = nh * ratio;
|
||||
fh = farD * tang;
|
||||
fw = fh * ratio;
|
||||
}
|
||||
|
||||
void Frustum::setCamDef(const glm::vec3 &p, const glm::vec3 &l, const glm::vec3 &u) {
|
||||
glm::vec3 dir,nc,fc,X,Y, Z = glm::normalize(p - l);
|
||||
|
||||
// X axis of camera with given "up" vector and Z axis
|
||||
X = glm::normalize(glm::cross(u, Z));
|
||||
|
||||
// the real "up" vector is the cross product of Z and X
|
||||
Y = glm::cross(Z, X);
|
||||
|
||||
// compute the centers of the near and far planes
|
||||
nc = p - Z * nearD;
|
||||
fc = p - Z * farD;
|
||||
{
|
||||
// compute the 4 corners of the frustum on the near plane
|
||||
ntl = nc + Y * nh - X * nw;
|
||||
ntr = nc + Y * nh + X * nw;
|
||||
nbl = nc - Y * nh - X * nw;
|
||||
nbr = nc - Y * nh + X * nw;
|
||||
|
||||
// compute the 4 corners of the frustum on the far plane
|
||||
ftl = fc + Y * fh - X * fw;
|
||||
ftr = fc + Y * fh + X * fw;
|
||||
fbl = fc - Y * fh - X * fw;
|
||||
fbr = fc - Y * fh + X * fw;
|
||||
}
|
||||
/*pl[NEAR].setNormalAndPoint(-Z,nc);
|
||||
pl[FAR].setNormalAndPoint(Z,fc);
|
||||
|
||||
glm::vec3 aux, normal;
|
||||
|
||||
aux = glm::normalize((nc + Y*nh) - p);
|
||||
normal = glm::cross(aux, X);
|
||||
pl[TOP].setNormalAndPoint(normal, nc+Y*nh);
|
||||
|
||||
aux = (nc - Y*nh) - p;
|
||||
normal = glm::normalize(glm::cross(aux, X));
|
||||
pl[BOTTOM].setNormalAndPoint(normal, nc-Y*nh);
|
||||
|
||||
aux = (nc - X*nw) - p;
|
||||
normal = glm::normalize(glm::cross(aux, X));
|
||||
pl[LEFT].setNormalAndPoint(normal, nc-X*nw);
|
||||
|
||||
aux = (nc + X*nw) - p;
|
||||
normal = glm::normalize(glm::cross(aux, X));
|
||||
pl[RIGHT].setNormalAndPoint(normal,nc+X*nw);*/
|
||||
pl[TOP].set3Points(ntr,ntl,ftl);
|
||||
pl[BOTTOM].set3Points(nbl,nbr,fbr);
|
||||
pl[LEFT].set3Points(ntl,nbl,fbl);
|
||||
pl[RIGHT].set3Points(nbr,ntr,fbr);
|
||||
pl[NEAR].set3Points(ntl,ntr,nbr);
|
||||
pl[FAR].set3Points(ftr,ftl,fbl);
|
||||
}
|
||||
|
||||
bool Frustum::pointInFrustum(const glm::vec3& p) {
|
||||
for(int i=0; i < 6; i++) {
|
||||
if (pl[i].distance(p) < 0.f)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Frustum::sphereInFrustum(const glm::vec3 &p, float raio) {
|
||||
for(int i=0; i < 6; i++) {
|
||||
if (pl[i].distance(p) < -raio)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
#ifndef FRUSTUM_HPP
|
||||
#define FRUSTUM_HPP
|
||||
#include <glm/glm.hpp>
|
||||
#include "AABB.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class Frustum {
|
||||
private:
|
||||
enum {
|
||||
TOP = 0, BOTTOM, LEFT,
|
||||
RIGHT, NEAR, FAR
|
||||
};
|
||||
|
||||
public:
|
||||
class Plane {
|
||||
public:
|
||||
glm::vec3 normal, point;
|
||||
float d;
|
||||
|
||||
Plane(const glm::vec3 &v1, const glm::vec3 &v2, const glm::vec3 &v3) {
|
||||
set3Points(v1, v2, v3);
|
||||
}
|
||||
Plane() {}
|
||||
~Plane() {}
|
||||
|
||||
void set3Points(const glm::vec3 &v1, const glm::vec3 &v2, const glm::vec3 &v3) {
|
||||
const glm::vec3 aux1 = v1 - v2, aux2 = v3 - v2;
|
||||
normal = glm::normalize(glm::cross(aux2, aux1));
|
||||
point = v2;
|
||||
d = -glm::dot(normal, point);
|
||||
}
|
||||
void setNormalAndPoint(const glm::vec3 &normal, const glm::vec3 &point) {
|
||||
this->normal = glm::normalize(normal);
|
||||
d = -glm::dot(normal, point);
|
||||
}
|
||||
void setCoefficients(float a, float b, float c, float d) {
|
||||
// set the normal vector
|
||||
normal = glm::vec3(a, b, c);
|
||||
//compute the lenght of the vector
|
||||
float l = normal.length();
|
||||
// normalize the vector
|
||||
normal = glm::vec3(a/l, b/l, c/l);
|
||||
// and divide d by th length as well
|
||||
this->d = d/l;
|
||||
}
|
||||
float distance(const glm::vec3 &p) const {
|
||||
return (d + glm::dot(normal, p));
|
||||
}
|
||||
} pl[6];
|
||||
glm::vec3 ntl,ntr,nbl,nbr,ftl,ftr,fbl,fbr;
|
||||
float nearD, farD, ratio, angle,tang;
|
||||
float nw,nh,fw,fh;
|
||||
|
||||
Frustum();
|
||||
~Frustum();
|
||||
void setCamInternals(float rad, float ratio, float nearD, float farD);
|
||||
void setCamDef(const glm::vec3 &p, const glm::vec3 &l, const glm::vec3 &u);
|
||||
bool pointInFrustum(const glm::vec3 &p);
|
||||
bool sphereInFrustum(const glm::vec3 &p, float raio);
|
||||
bool boxInFrustum(const AABB &b);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,31 @@
|
|||
#include "GLFWHandler.hpp"
|
||||
#include "GameWindow.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
void GLFWHandler::mouseButtonImpl(GLFWwindow *window, int key, int action, int mods) {
|
||||
win->cbMouseButton(key, action, mods);
|
||||
}
|
||||
|
||||
void GLFWHandler::cursorPosImpl(GLFWwindow *window, double x, double y) {
|
||||
win->cbCursorPos(x, y);
|
||||
}
|
||||
|
||||
void GLFWHandler::mouseScrollImpl(GLFWwindow *window, double x, double y) {
|
||||
win->cbMouseScroll(x, y);
|
||||
}
|
||||
|
||||
void GLFWHandler::keyImpl(GLFWwindow *window, int key, int scancode, int action, int mods) {
|
||||
win->cbKey(key, scancode, action, mods);
|
||||
}
|
||||
|
||||
void GLFWHandler::unicharImpl(GLFWwindow *window, unsigned int unichar) {
|
||||
win->cbChar(static_cast<char32>(unichar));
|
||||
}
|
||||
|
||||
void GLFWHandler::resizeImpl(GLFWwindow *window, int w, int h) {
|
||||
win->cbResize(w, h);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
#ifndef GLFW_HANDLER
|
||||
#define GLFW_HANDLER
|
||||
#include <GL/glew.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class GameWindow;
|
||||
|
||||
/// C++ wrapper for C-style GLFWwindow callbacks
|
||||
/// Currently handles one window, but could be modified to handle multiple
|
||||
/// (using a GLFWwindow* <-> Window instance map)
|
||||
class GLFWHandler {
|
||||
private:
|
||||
GameWindow *win;
|
||||
void mouseButtonImpl(GLFWwindow *window, int key, int action, int mods);
|
||||
void cursorPosImpl(GLFWwindow *window, double x, double y);
|
||||
void mouseScrollImpl(GLFWwindow *window, double x, double y);
|
||||
void keyImpl(GLFWwindow *window, int key, int scancode, int action, int mods);
|
||||
void unicharImpl(GLFWwindow *window, unsigned int unichar);
|
||||
void resizeImpl(GLFWwindow *window, int w, int h);
|
||||
|
||||
public:
|
||||
///
|
||||
/// @returns GLFWHandler singleton
|
||||
///
|
||||
static GLFWHandler& getInstance() {
|
||||
static GLFWHandler instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
static void setWindow(GameWindow *win, GLFWwindow *window) {
|
||||
getInstance().win = win;
|
||||
}
|
||||
|
||||
static void mouseButton(GLFWwindow *window, int key, int action, int mods) {
|
||||
getInstance().mouseButtonImpl(window, key, action, mods);
|
||||
}
|
||||
|
||||
static void cursorPos(GLFWwindow *window, double x, double y) {
|
||||
getInstance().cursorPosImpl(window, x, y);
|
||||
}
|
||||
|
||||
static void mouseScroll(GLFWwindow *window, double x, double y) {
|
||||
getInstance().mouseScrollImpl(window, x, y);
|
||||
}
|
||||
|
||||
static void key(GLFWwindow *window, int key, int scancode, int action, int mods) {
|
||||
getInstance().keyImpl(window, key, scancode, action, mods);
|
||||
}
|
||||
|
||||
static void unichar(GLFWwindow *window, unsigned int unichar) {
|
||||
getInstance().unicharImpl(window, unichar);
|
||||
}
|
||||
|
||||
static void resize(GLFWwindow *window, int w, int h) {
|
||||
getInstance().resizeImpl(window, w, h);
|
||||
}
|
||||
|
||||
private:
|
||||
GLFWHandler() {}
|
||||
GLFWHandler(const GLFWHandler&); // prevent copies
|
||||
void operator=(const GLFWHandler&);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,31 @@
|
|||
#include "Game.hpp"
|
||||
#include "GlobalProperties.hpp"
|
||||
#include "ChunkChangeHelper.hpp"
|
||||
#include "Audio.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
Game::Game() : players(this), CCH(nullptr), GW(nullptr), LP(nullptr), PM(nullptr) {
|
||||
SC = std::make_shared<Superchunk>(this);
|
||||
|
||||
if (GlobalProperties::IsClient) {
|
||||
PM = new ProgramManager;
|
||||
LP = new LocalPlayer(this);
|
||||
RP = new RenderProperties;
|
||||
A = new Audio(this);
|
||||
}
|
||||
if (GlobalProperties::IsServer) {
|
||||
CCH = new ChunkChangeHelper();
|
||||
}
|
||||
}
|
||||
|
||||
Game::~Game() {
|
||||
if (GlobalProperties::IsClient) {
|
||||
delete PM; delete LP; delete RP; delete A;
|
||||
}
|
||||
if (GlobalProperties::IsServer) {
|
||||
delete CCH;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef GAME_HPP
|
||||
#define GAME_HPP
|
||||
#include <memory>
|
||||
#include "Superchunk.hpp"
|
||||
#include "PlayerList.hpp"
|
||||
#include "LocalPlayer.hpp"
|
||||
#include "ProgramManager.hpp"
|
||||
|
||||
using std::shared_ptr;
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
namespace UI {
|
||||
class Manager;
|
||||
}
|
||||
|
||||
class Audio;
|
||||
|
||||
class Game {
|
||||
public:
|
||||
// Shared
|
||||
shared_ptr<Superchunk> SC;
|
||||
PlayerList players;
|
||||
double Time;
|
||||
Net::Host H;
|
||||
|
||||
// Server
|
||||
class Server *S;
|
||||
class ChunkChangeHelper *CCH;
|
||||
int Port = 61425;
|
||||
|
||||
// Client
|
||||
class GameWindow *GW;
|
||||
UI::Manager *UIM;
|
||||
LocalPlayer *LP;
|
||||
ProgramManager *PM;
|
||||
Font *F;
|
||||
struct RenderProperties {
|
||||
bool bloom;
|
||||
} *RP;
|
||||
Audio *A;
|
||||
Net::Peer NS; // Net Server
|
||||
|
||||
Game();
|
||||
~Game();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,628 @@
|
|||
#include "GameState.hpp"
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include "Game.hpp"
|
||||
#include "FBO.hpp"
|
||||
#include "Clouds.hpp"
|
||||
#include "Chatbox.hpp"
|
||||
#include "CaveGenerator.hpp"
|
||||
#include "Skybox.hpp"
|
||||
#include "EscMenu.hpp"
|
||||
#include "Audio.hpp"
|
||||
#include "network/NetHelper.hpp"
|
||||
|
||||
using std::unique_ptr;
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
const int GameState::BloomScale = 4;
|
||||
|
||||
GameState::GameState(GameWindow *W, const std::string &servHost, int servPort)
|
||||
: W(W), m_serverHost(servHost), m_serverPort(servPort) {
|
||||
G = W->G;
|
||||
|
||||
// Initialized in setupUI
|
||||
UI.EM = nullptr;
|
||||
m_chatBox = nullptr;
|
||||
|
||||
m_3dFbo = new FBO(640, 480, Texture::PixelFormat::RGB, true);
|
||||
m_3dRenderVBO = new VBO();
|
||||
m_clouds = new Clouds(G, 32, 32, 4);
|
||||
//m_sky = new Skybox(G, getAssetPath("alpine"));
|
||||
m_3dFboRenderer = G->PM->getProgram(PM_2D | PM_TEXTURED); //getSpecialProgram("effect3dRender");
|
||||
m_3dFboRenderer_coord = m_3dFboRenderer->att("coord");
|
||||
m_3dFboRenderer_texcoord = m_3dFboRenderer->att("texcoord");
|
||||
m_3dFboRenderer_mvp = m_3dFboRenderer->uni("mvp");
|
||||
|
||||
m_extractorFbo = new FBO(640/BloomScale, 480/BloomScale, Texture::PixelFormat::RGBA);
|
||||
m_bloomExtractorRenderer = G->PM->getSpecialProgram("bloomExtractor");
|
||||
m_bloomExtractorRenderer_coord = m_bloomExtractorRenderer->att("coord");
|
||||
m_bloomExtractorRenderer_texcoord = m_bloomExtractorRenderer->att("texcoord");
|
||||
m_bloomExtractorRenderer_mvp = m_bloomExtractorRenderer->uni("mvp");
|
||||
m_bloomFbo = new FBO(640/BloomScale, 480/BloomScale, Texture::PixelFormat::RGBA);
|
||||
m_bloomRenderer = G->PM->getSpecialProgram("bloom");
|
||||
m_bloomRenderer_coord = m_bloomRenderer->att("coord");
|
||||
m_bloomRenderer_texcoord = m_bloomRenderer->att("texcoord");
|
||||
m_bloomRenderer_mvp = m_bloomRenderer->uni("mvp");
|
||||
m_bloomRenderer_pixshift = m_bloomRenderer->uni("pixshift");
|
||||
|
||||
//"\f0H\f1e\f2l\f3l\f4l\f5o \f6d\f7e\f8m\f9b\faa\fbz\fcz\fde\fes\ff,\n\f0ye see,it werks purrfektly :D\n(and also; it's optimized)"
|
||||
|
||||
m_mouseLocked = false;
|
||||
nextNetUpdate = 0;
|
||||
|
||||
enableExtractor = enableDbg = true;
|
||||
}
|
||||
|
||||
void GameState::setupUI() {
|
||||
m_debugTxt = G->UIM->add<UI::Text>(G->F, "\\o/", 2, 2);
|
||||
m_debugTxt->setPos(20, 200);
|
||||
|
||||
UI.Ore = G->UIM->add<UI::Text>(G->F); UI.Ore->setScale(2, 2);
|
||||
UI.Loot = G->UIM->add<UI::Text>(G->F); UI.Loot->setScale(2, 2);
|
||||
UI.Weight = G->UIM->add<UI::Text>(G->F); UI.Weight->setScale(2, 2);
|
||||
UI.TeamOre = G->UIM->add<UI::Text>(G->F); UI.TeamOre->setScale(2, 2);
|
||||
UI.RedCash = G->UIM->add<UI::Text>(G->F); UI.RedCash->setScale(2, 2);
|
||||
UI.BlueCash = G->UIM->add<UI::Text>(G->F); UI.BlueCash->setScale(2, 2);
|
||||
UI.FPS = G->UIM->add<UI::Text>(G->F); UI.FPS->setScale(2, 2);
|
||||
UI.Altitude = G->UIM->add<UI::Text>(G->F); UI.Altitude->setScale(2, 2);
|
||||
UI.EM = new EscMenu(G);
|
||||
|
||||
m_chatBox = new Chatbox(G);
|
||||
|
||||
updateViewport();
|
||||
}
|
||||
|
||||
GameState::~GameState() {
|
||||
delete m_3dFbo; delete m_extractorFbo; delete m_clouds; delete m_bloomFbo;
|
||||
delete m_chatBox;
|
||||
//delete m_sky;
|
||||
}
|
||||
|
||||
void GameState::onChar(char32 unichar) {
|
||||
if (m_chatBox->isChatting()) {
|
||||
m_chatBox->handleChar(unichar);
|
||||
}
|
||||
}
|
||||
|
||||
void GameState::onKey(int key, int scancode, int action, int mods) {
|
||||
if (!(action == GLFW_PRESS || action == GLFW_RELEASE))
|
||||
return;
|
||||
|
||||
if (m_chatBox->isChatting()) {
|
||||
switch (key) {
|
||||
case GLFW_KEY_ENTER:
|
||||
if (action == GLFW_PRESS) {
|
||||
std::string str = m_chatBox->getChatString();
|
||||
if (str.size() > 0) {
|
||||
NetHelper::SendChat(G, str);
|
||||
}
|
||||
m_chatBox->setIsChatting(false);
|
||||
}
|
||||
break;
|
||||
case GLFW_KEY_ESCAPE:
|
||||
if (action == GLFW_PRESS)
|
||||
m_chatBox->setIsChatting(false);
|
||||
break;
|
||||
default:
|
||||
m_chatBox->handleKey(key, scancode, action, mods);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (key) {
|
||||
case GLFW_KEY_F1:
|
||||
if (action == GLFW_PRESS)
|
||||
enableExtractor = !enableExtractor;
|
||||
break;
|
||||
case GLFW_KEY_ESCAPE:
|
||||
//glfwSetWindowShouldClose(window, GL_TRUE);
|
||||
if (action == GLFW_PRESS)
|
||||
isEscapeToggled = !isEscapeToggled;
|
||||
break;
|
||||
/*case GLFW_KEY_TAB:
|
||||
if (action == GLFW_PRESS) {
|
||||
isTabDown = true;
|
||||
//glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
||||
} else {
|
||||
//glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
||||
isTabDown = false;
|
||||
}
|
||||
break;*/
|
||||
case GLFW_KEY_ENTER:
|
||||
if (action == GLFW_PRESS)
|
||||
m_chatBox->setIsChatting(true);
|
||||
break;
|
||||
case GLFW_KEY_W:
|
||||
G->LP->goForward(action == GLFW_PRESS);
|
||||
break;
|
||||
case GLFW_KEY_S:
|
||||
G->LP->goBackward(action == GLFW_PRESS);
|
||||
break;
|
||||
case GLFW_KEY_A:
|
||||
G->LP->goLeft(action == GLFW_PRESS);
|
||||
break;
|
||||
case GLFW_KEY_D:
|
||||
G->LP->goRight(action == GLFW_PRESS);
|
||||
break;
|
||||
case GLFW_KEY_V:
|
||||
G->LP->setHasNoclip(true);
|
||||
break;
|
||||
case GLFW_KEY_B:
|
||||
G->LP->setHasNoclip(false);
|
||||
break;
|
||||
case GLFW_KEY_SPACE:
|
||||
if (action == GLFW_PRESS)
|
||||
G->LP->jump();
|
||||
break;
|
||||
case GLFW_KEY_U:
|
||||
if (action == GLFW_PRESS)
|
||||
G->LP->special1();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameState::onMouseButton(int key, int action, int mods) {
|
||||
if (key != GLFW_MOUSE_BUTTON_LEFT)
|
||||
return;
|
||||
|
||||
//snd->play();
|
||||
|
||||
if (action == GLFW_PRESS) {
|
||||
glfwSetInputMode(*W, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
||||
double x, y;
|
||||
glfwGetCursorPos(*W, &x, &y);
|
||||
cX = (int)x; cY = (int)y;
|
||||
m_mouseLocked = true;
|
||||
} else {
|
||||
m_mouseLocked = false;
|
||||
glfwSetInputMode(*W, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
void GameState::onCursorPos(double x, double y) {
|
||||
if (!m_mouseLocked)
|
||||
return;
|
||||
int cx = (int)x, dx = cx-cX, cy = (int)y, dy = cy-cY;
|
||||
const float mousespeed = 0.003;
|
||||
|
||||
angles.x -= dx * mousespeed;
|
||||
angles.y -= dy * mousespeed;
|
||||
|
||||
if(angles.x < -M_PI)
|
||||
angles.x += M_PI * 2;
|
||||
else if(angles.x > M_PI)
|
||||
angles.x -= M_PI * 2;
|
||||
|
||||
if(angles.y < -M_PI / 2)
|
||||
angles.y = -M_PI / 2 + 0.001;
|
||||
if(angles.y > M_PI / 2)
|
||||
angles.y = M_PI / 2 - 0.001;
|
||||
|
||||
lookat.x = sinf(angles.x) * cosf(angles.y);
|
||||
lookat.y = sinf(angles.y);
|
||||
lookat.z = cosf(angles.x) * cosf(angles.y);
|
||||
|
||||
G->LP->lookAt(lookat);
|
||||
|
||||
cX = cx; cY = cy;
|
||||
}
|
||||
|
||||
void GameState::onResize(int w, int h) {
|
||||
//W->getW() = w; W->getH() = h;
|
||||
updateViewport();
|
||||
}
|
||||
|
||||
void GameState::onMouseScroll(double x, double y) {
|
||||
|
||||
}
|
||||
|
||||
void GameState::updateViewport() {
|
||||
int w = W->getW(), h = W->getH();
|
||||
glViewport(0, 0, w, h);
|
||||
G->LP->camera.setPersp((float)M_PI/180*75.0f, (float)w / h, 0.1f, 32.0f);
|
||||
m_3dFbo->resize(w, h);
|
||||
m_extractorFbo->resize(w/BloomScale, h/BloomScale);
|
||||
m_bloomFbo->resize(w/BloomScale, h/BloomScale);
|
||||
Coord2DTex renderQuad[6] = {
|
||||
{0, 0, 0, 0},
|
||||
{w, 0, 1, 0},
|
||||
{0, h, 0, 1},
|
||||
|
||||
{w, h, 1, 1},
|
||||
{0, h, 0, 1},
|
||||
{w, 0, 1, 0}
|
||||
};
|
||||
m_3dRenderVBO->setData(renderQuad, 6*sizeof(Coord2DTex));
|
||||
|
||||
char str[15]; std::snprintf(str, 15, "Loot: %d/%d", 0/*G->LP->ore*/, Player::getMaxOre(G->LP->playerclass));
|
||||
UI.Ore->setText(std::string(str));
|
||||
UI.Ore->setPos(4, h-14);
|
||||
|
||||
UI.Loot->setText("Loot: $0");
|
||||
UI.Loot->setPos(w/6, h-14);
|
||||
|
||||
UI.Weight->setText("Weight: 0");
|
||||
UI.Weight->setPos(w/3, h-14);
|
||||
|
||||
UI.TeamOre->setText("Team ore: 0");
|
||||
UI.TeamOre->setPos(w/2, h-14);
|
||||
|
||||
UI.RedCash->setText("\f4Red: $0");
|
||||
UI.RedCash->setPos((w*5)/8, h-14);
|
||||
|
||||
UI.BlueCash->setText("\fbBlue: $0");
|
||||
UI.BlueCash->setPos((w*6)/8, h-14);
|
||||
|
||||
UI.FPS->setPos(16, 16);
|
||||
UI.Altitude->setText("Altitude: XX");
|
||||
UI.Altitude->setPos(w-16-UI.Altitude->getSize().x, 16);
|
||||
UI.lastAltitude = INT_MAX;
|
||||
|
||||
m_debugTxt->update();
|
||||
m_chatBox->setPosition(4, 64);
|
||||
updateUI();
|
||||
}
|
||||
|
||||
void GameState::sendMsg(Net::OutMessage &msg, Net::Tfer mode) {
|
||||
G->H.send(G->NS, msg, mode);
|
||||
}
|
||||
|
||||
void GameState::run() {
|
||||
if (connectLoop()) return;
|
||||
|
||||
setupUI();
|
||||
gameLoop();
|
||||
}
|
||||
|
||||
bool GameState::connectLoop() {
|
||||
std::string &serverHost = m_serverHost;
|
||||
int serverPort = m_serverPort;
|
||||
bool finished = false, success = false; Game *G = this->G;
|
||||
m_networkThread = std::thread([G, &success, &finished, &serverHost, serverPort]() {
|
||||
try {
|
||||
G->H.create();
|
||||
G->NS = G->H.connect(serverHost, serverPort, 5000);
|
||||
success = true;
|
||||
} catch (const Net::Exception &e) {
|
||||
success = false;
|
||||
}
|
||||
finished = true;
|
||||
});
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
ConnectingUI cUI {
|
||||
G->UIM->create<UI::Text>(G->F, "Connecting"),
|
||||
G->UIM->create<UI::Text>(G->F, ".")
|
||||
};
|
||||
double T; glm::mat4 mat;
|
||||
|
||||
while (!finished && !glfwWindowShouldClose(*W)) { // Infinite loop \o/
|
||||
T = glfwGetTime();
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
UI::Text::Size sz = cUI.Connecting->getSize();
|
||||
mat = glm::scale(glm::translate(*G->GW->UIM.PM, glm::vec3(W->getW()/2-sz.x, W->getH()/2, 0.f)),
|
||||
glm::vec3(2.f, 2.f, 1.f));
|
||||
cUI.Connecting->render(mat);
|
||||
for (int i=0; i < 6; ++i) {
|
||||
mat = glm::scale(glm::translate(*G->GW->UIM.PM,
|
||||
glm::vec3(W->getW()/2 - 1 + sin(T*3+0.3*i)*sz.x, W->getH()/2-sz.y, 0.f)),
|
||||
glm::vec3(2.f, 2.f, 1.f));
|
||||
cUI.Dot->render(mat);
|
||||
}
|
||||
|
||||
glfwSwapBuffers(*W);
|
||||
glfwPollEvents();
|
||||
}
|
||||
if (glfwWindowShouldClose(*W))
|
||||
glfwHideWindow(*W);
|
||||
m_networkThread.join();
|
||||
delete cUI.Connecting; delete cUI.Dot;
|
||||
|
||||
if (glfwWindowShouldClose(*W))
|
||||
return true;
|
||||
if (!success) {
|
||||
std::ostringstream oss;
|
||||
oss << serverHost << ':' << serverPort << " did not respond";
|
||||
W->showMessage("Could not connect to server", oss.str());
|
||||
return true;
|
||||
}
|
||||
|
||||
char name[5];
|
||||
for (int i=0; i < 4; i++)
|
||||
name[i] = 'A' + FastRand(25);
|
||||
name[4] = '\0';
|
||||
|
||||
Net::OutMessage join(Net::MessageType::PlayerJoin);
|
||||
std::string strname(name);
|
||||
join.writeString(strname);
|
||||
sendMsg(join, Net::Tfer::Rel);
|
||||
|
||||
bool received = G->H.recv(m_msg, 5000);
|
||||
if (!received) {
|
||||
W->showMessage("Connected but got no response", "after 5 seconds");
|
||||
return true;
|
||||
}
|
||||
switch (m_msg.getType()) {
|
||||
case Net::MessageType::PlayerJoin: {
|
||||
G->LP->id = m_msg.readU32();
|
||||
} break;
|
||||
case Net::MessageType::PlayerQuit: {
|
||||
std::string desc = m_msg.readString();
|
||||
W->showMessage("Disconnected", desc);
|
||||
} return true;
|
||||
default: {
|
||||
std::ostringstream sstm;
|
||||
sstm << "Type: " << (int)m_msg.getType() << " Subtype: " << (int)m_msg.getSubtype();
|
||||
W->showMessage("Received weird packet", sstm.str());
|
||||
} return true;
|
||||
}
|
||||
|
||||
getDebugStream() << "Joined as " << name << '/' << G->LP->id << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
void GameState::gameLoop() {
|
||||
double lastT, deltaT, T, fpsT = 0; int frames = 0;
|
||||
LocalPlayer *LP = G->LP;
|
||||
LP->position = glm::vec3(2, G->SC->getChunksY()*CY/2, 2);
|
||||
angles.x = M_PI/4; angles.y = M_PI/4;
|
||||
lookat.x = sinf(angles.x) * cosf(angles.y);
|
||||
lookat.y = sinf(angles.y);
|
||||
lookat.z = cosf(angles.x) * cosf(angles.y);
|
||||
LP->lookAt(lookat);
|
||||
LP->forceCameraUpdate();
|
||||
G->A->update();
|
||||
LP->setHasNoclip(true);
|
||||
while (!glfwWindowShouldClose(*W)) {
|
||||
if (!processNetwork()) return;
|
||||
|
||||
T = glfwGetTime(); deltaT = T - lastT;
|
||||
G->Time = T;
|
||||
if (T > fpsT) {
|
||||
char str[8]; std::sprintf(str, "FPS: %d", frames); //\f
|
||||
UI.FPS->setText(std::string(str));
|
||||
fpsT = T+1;
|
||||
frames = 0;
|
||||
}
|
||||
if (T > nextNetUpdate) {
|
||||
Net::OutMessage msg(Net::MessageType::PlayerUpdate, Net::PlayerUpdateType::Move);
|
||||
msg.writeVec3(LP->position);
|
||||
msg.writeVec3(LP->velocity);
|
||||
msg.writeVec3(LP->accel);
|
||||
sendMsg(msg, Net::Tfer::Unrel);
|
||||
nextNetUpdate = T+0.25;//+1;
|
||||
}
|
||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
m_3dFbo->bind();
|
||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
LP->update(deltaT);
|
||||
// TODO: disable teleport and kill player
|
||||
if (LP->position.y < -32-LP->size.y) {
|
||||
LP->position.y = G->SC->getChunksY()*CY+32+LP->size.y;
|
||||
}
|
||||
|
||||
glm::mat4 m_transform = LP->getPVMatrix();
|
||||
|
||||
/*** 3D PART ***/
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
//m_sky->render(LP->camera.getSkyMatrix());
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
G->SC->render(m_transform);
|
||||
for (Player &p : G->players) {
|
||||
p.update(deltaT);
|
||||
p.render(m_transform);
|
||||
}
|
||||
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
glm::mat4 cloudmat = glm::scale(glm::translate(m_transform, glm::vec3(0.f, (G->SC->getChunksY()*CY/4)+.5f, 0.f)), glm::vec3(G->SC->getChunksX()*CX, 2, G->SC->getChunksZ()*CZ));
|
||||
m_clouds->render(cloudmat);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
m_3dFbo->unbind();
|
||||
m_3dFbo->tex->bind();
|
||||
m_3dFboRenderer->bind();
|
||||
glEnableVertexAttribArray(m_3dFboRenderer_coord);
|
||||
glEnableVertexAttribArray(m_3dFboRenderer_texcoord);
|
||||
m_3dRenderVBO->bind();
|
||||
glUniformMatrix4fv(m_3dFboRenderer_mvp, 1, GL_FALSE, glm::value_ptr(*G->GW->UIM.PM));
|
||||
glVertexAttribPointer(m_3dFboRenderer_coord, 2, GL_INT, GL_FALSE, sizeof(Coord2DTex), 0);
|
||||
glVertexAttribPointer(m_3dFboRenderer_texcoord, 2, GL_BYTE, GL_FALSE, sizeof(Coord2DTex), (GLvoid*)offsetof(Coord2DTex, u));
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
glDisableVertexAttribArray(m_3dFboRenderer_texcoord);
|
||||
glDisableVertexAttribArray(m_3dFboRenderer_coord);
|
||||
|
||||
if (enableExtractor) {
|
||||
glViewport(0, 0, W->getW()/BloomScale, W->getH()/BloomScale);
|
||||
m_3dFbo->tex->setFiltering(Texture::Filter::Linear, Texture::Filter::Linear);
|
||||
m_extractorFbo->bind();
|
||||
glClearColor(0.f, 0.f, 0.f, 0.f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
m_bloomExtractorRenderer->bind();
|
||||
glEnableVertexAttribArray(m_bloomExtractorRenderer_coord);
|
||||
glEnableVertexAttribArray(m_bloomExtractorRenderer_texcoord);
|
||||
m_3dRenderVBO->bind();
|
||||
glUniformMatrix4fv(m_bloomExtractorRenderer_mvp, 1, GL_FALSE, glm::value_ptr(*G->GW->UIM.PM));
|
||||
glVertexAttribPointer(m_bloomExtractorRenderer_coord, 2, GL_INT, GL_FALSE, sizeof(Coord2DTex), 0);
|
||||
glVertexAttribPointer(m_bloomExtractorRenderer_texcoord, 2, GL_BYTE, GL_FALSE, sizeof(Coord2DTex), (GLvoid*)offsetof(Coord2DTex, u));
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
glDisableVertexAttribArray(m_bloomExtractorRenderer_texcoord);
|
||||
glDisableVertexAttribArray(m_bloomExtractorRenderer_coord);
|
||||
|
||||
m_3dFbo->tex->setFiltering(Texture::Filter::Nearest, Texture::Filter::Nearest);
|
||||
m_extractorFbo->unbind();
|
||||
|
||||
m_bloomFbo->bind();
|
||||
glClearColor(0.f, 0.f, 0.f, 0.f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
m_extractorFbo->tex->bind();
|
||||
m_bloomRenderer->bind();
|
||||
glEnableVertexAttribArray(m_bloomRenderer_coord);
|
||||
glEnableVertexAttribArray(m_bloomRenderer_texcoord);
|
||||
m_3dRenderVBO->bind();
|
||||
glUniformMatrix4fv(m_bloomRenderer_mvp, 1, GL_FALSE, glm::value_ptr(*G->GW->UIM.PM));
|
||||
GLfloat pixshift[2] = { 1.f/(W->getW()/BloomScale), 1.f/(W->getH()/BloomScale) };
|
||||
glUniform2fv(m_bloomRenderer_pixshift, 1, pixshift);
|
||||
glVertexAttribPointer(m_bloomRenderer_coord, 2, GL_INT, GL_FALSE, sizeof(Coord2DTex), 0);
|
||||
glVertexAttribPointer(m_bloomRenderer_texcoord, 2, GL_BYTE, GL_FALSE, sizeof(Coord2DTex), (GLvoid*)offsetof(Coord2DTex, u));
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
glDisableVertexAttribArray(m_bloomRenderer_texcoord);
|
||||
glDisableVertexAttribArray(m_bloomRenderer_coord);
|
||||
m_bloomFbo->unbind();
|
||||
|
||||
// render to real surface
|
||||
glViewport(0, 0, W->getW(), W->getH());
|
||||
m_bloomFbo->tex->bind();
|
||||
m_bloomFbo->tex->setFiltering(Texture::Filter::Linear, Texture::Filter::Linear);
|
||||
m_bloomRenderer->bind();
|
||||
glEnableVertexAttribArray(m_bloomRenderer_coord);
|
||||
glEnableVertexAttribArray(m_bloomRenderer_texcoord);
|
||||
m_3dRenderVBO->bind();
|
||||
glUniformMatrix4fv(m_bloomRenderer_mvp, 1, GL_FALSE, glm::value_ptr(*G->GW->UIM.PM));
|
||||
glVertexAttribPointer(m_bloomRenderer_coord, 2, GL_INT, GL_FALSE, sizeof(Coord2DTex), 0);
|
||||
glVertexAttribPointer(m_bloomRenderer_texcoord, 2, GL_BYTE, GL_FALSE, sizeof(Coord2DTex), (GLvoid*)offsetof(Coord2DTex, u));
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
glDisableVertexAttribArray(m_bloomRenderer_texcoord);
|
||||
glDisableVertexAttribArray(m_bloomRenderer_coord);
|
||||
}
|
||||
|
||||
/*** 2D PART ***/
|
||||
updateUI();
|
||||
drawUI();
|
||||
|
||||
glfwSwapBuffers(*W);
|
||||
glfwPollEvents();
|
||||
|
||||
lastT = T;
|
||||
frames++;
|
||||
}
|
||||
Net::OutMessage quit(Net::MessageType::PlayerQuit);
|
||||
sendMsg(quit, Net::Tfer::Rel);
|
||||
}
|
||||
|
||||
void GameState::updateUI() {
|
||||
int altitude = G->LP->position.y;
|
||||
if (altitude != UI.lastAltitude) {
|
||||
char str[15]; std::snprintf(str, 15, "Altitude: %d", altitude);
|
||||
UI.lastAltitude = altitude;
|
||||
UI.Altitude->setText(std::string(str));
|
||||
}
|
||||
/*std::ostringstream oss;
|
||||
oss << G->LP->position.y << std::endl << i(G->LP->position.y) << std::endl <<
|
||||
G->LP->velocity.x << " " << G->LP->velocity.y << " " << G->LP->velocity.z;
|
||||
m_debugTxt->setText(oss.str());*/
|
||||
}
|
||||
|
||||
void GameState::drawUI() {
|
||||
G->UIM->render();
|
||||
m_chatBox->render();
|
||||
if (enableDbg)
|
||||
m_debugTxt->render();
|
||||
if (isEscapeToggled)
|
||||
UI.EM->render();
|
||||
|
||||
static _<Texture> tex(getAssetPath("tools", "tex_tool_build.png"), Texture::PixelFormat::RGBA);
|
||||
G->UIM->drawTexRect(UI::Element::Area {20, 20, 40, 40}, *tex);
|
||||
}
|
||||
|
||||
bool GameState::processNetwork() {
|
||||
while (G->H.recv(m_msg, 0)) {
|
||||
switch (m_msg.getType()) {
|
||||
case Net::MessageType::Disconnect:
|
||||
W->showMessage("Disconnected", "Timed out");
|
||||
return false;
|
||||
|
||||
case Net::MessageType::MapTransfer: {
|
||||
G->SC->readMsg(m_msg);
|
||||
} break;
|
||||
case Net::MessageType::Chat: {
|
||||
m_chatBox->addChatEntry(m_msg.readString());
|
||||
} break;
|
||||
case Net::MessageType::PlayerJoin: {
|
||||
Player &plr = G->players.add();
|
||||
plr.id = m_msg.readU32();
|
||||
plr.name = m_msg.readString();
|
||||
getDebugStream() << "Player " << plr.name << '(' << plr.id << ") joined the party!" << std::endl;
|
||||
} break;
|
||||
case Net::MessageType::PlayerQuit: {
|
||||
uint32 id = m_msg.readU32();
|
||||
try {
|
||||
Player &plr = G->players.getById(id);
|
||||
getOutputStream() << plr.name << " is gone :(" << std::endl;
|
||||
G->players.remove(plr);
|
||||
} catch (const std::out_of_range &e) {
|
||||
getOutputStream() << "Phantom player #" << id << " disconnected" << std::endl;
|
||||
}
|
||||
} break;
|
||||
case Net::MessageType::PlayerUpdate: {
|
||||
uint32 id = m_msg.readU32();
|
||||
try {
|
||||
Player &plr = G->players.getById(id);
|
||||
switch (m_msg.getSubtype()) {
|
||||
case Net::PlayerUpdateType::Move: {
|
||||
glm::vec3 pos = m_msg.readVec3(),
|
||||
vel = m_msg.readVec3(),
|
||||
acc = m_msg.readVec3();
|
||||
plr.setPosVel(pos, vel, acc);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (const std::out_of_range &e) {
|
||||
getOutputStream() << "Invalid player update: #" << id << " is not on server" << std::endl;
|
||||
}
|
||||
} break;
|
||||
case Net::MessageType::MapUpdate: {
|
||||
int count = m_msg.getSize() /
|
||||
(sizeof(uint16) * 3 + sizeof(uint8));
|
||||
for (int i=0; i < count; ++i) {
|
||||
int x = m_msg.readU16(),
|
||||
y = m_msg.readU16(),
|
||||
z = m_msg.readU16();
|
||||
uint8 b = m_msg.readU8();
|
||||
G->SC->set(x, y, z, static_cast<BlockType>(b));
|
||||
}
|
||||
} break;
|
||||
case Net::MessageType::Event: {
|
||||
switch (m_msg.getSubtype()) {
|
||||
case Net::EventType::ExplosivesBlow: {
|
||||
glm::vec3 pos = m_msg.readVec3();
|
||||
G->A->playSound("explosion", pos);
|
||||
} break;
|
||||
case Net::PlayerJumpOnPad: {
|
||||
uint32 id = m_msg.readU32();
|
||||
if (id == G->LP->id) { // Is it a-me?
|
||||
G->A->playSound("jumpblock");
|
||||
} else {
|
||||
Player &plr = G->players.getById(id);
|
||||
G->A->playSound("jumpblock", plr.position);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
#ifndef GAME_STATE_HPP
|
||||
#define GAME_STATE_HPP
|
||||
#include "State.hpp"
|
||||
#include <thread>
|
||||
#include <glm/detail/type_vec2.hpp>
|
||||
#include <glm/detail/type_vec.hpp>
|
||||
#include "GameWindow.hpp"
|
||||
#include "VBO.hpp"
|
||||
#include "network/Network.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class Skybox;
|
||||
class KeyBindings;
|
||||
class Font;
|
||||
class Program;
|
||||
class Clouds;
|
||||
class FBO;
|
||||
class Game;
|
||||
class Chatbox;
|
||||
namespace UI {
|
||||
class Text;
|
||||
}
|
||||
|
||||
class GameState : public State {
|
||||
private:
|
||||
GameWindow *W;
|
||||
Game *G;
|
||||
|
||||
std::string m_serverHost;
|
||||
int m_serverPort;
|
||||
|
||||
glm::vec3 m_lookat;
|
||||
glm::vec2 m_angles;
|
||||
|
||||
FBO *m_3dFbo;
|
||||
const Program *m_3dFboRenderer;
|
||||
GLuint m_3dFboRenderer_coord, m_3dFboRenderer_texcoord, m_3dFboRenderer_mvp;
|
||||
|
||||
bool enableExtractor, enableDbg;
|
||||
FBO *m_extractorFbo;
|
||||
const Program *m_bloomExtractorRenderer;
|
||||
GLuint m_bloomExtractorRenderer_coord, m_bloomExtractorRenderer_texcoord, m_bloomExtractorRenderer_mvp;
|
||||
static const int BloomScale;
|
||||
FBO *m_bloomFbo;
|
||||
const Program *m_bloomRenderer;
|
||||
GLuint m_bloomRenderer_coord, m_bloomRenderer_texcoord, m_bloomRenderer_mvp, m_bloomRenderer_pixshift;
|
||||
|
||||
VBO *m_3dRenderVBO;
|
||||
struct Coord2DTex { int x, y; uint8 u, v; };
|
||||
Clouds *m_clouds;
|
||||
Skybox *m_sky;
|
||||
|
||||
UI::Text *m_debugTxt;
|
||||
|
||||
KeyBindings *m_keybinds;
|
||||
|
||||
Chatbox *m_chatBox;
|
||||
|
||||
bool m_mouseLocked; int cX, cY;
|
||||
glm::vec3 angles, lookat;
|
||||
|
||||
std::thread m_networkThread;
|
||||
|
||||
Net::InMessage m_msg;
|
||||
float nextNetUpdate;
|
||||
|
||||
struct ConnectingUI {
|
||||
UI::Text *Connecting;
|
||||
UI::Text *Dot;
|
||||
};
|
||||
|
||||
bool isEscapeToggled = false;
|
||||
struct {
|
||||
UI::Text *Ore;
|
||||
UI::Text *Loot;
|
||||
UI::Text *Weight;
|
||||
UI::Text *TeamOre;
|
||||
UI::Text *RedCash;
|
||||
UI::Text *BlueCash;
|
||||
|
||||
UI::Text *FPS;
|
||||
UI::Text *Altitude;
|
||||
int lastAltitude;
|
||||
|
||||
class EscMenu *EM;
|
||||
} UI;
|
||||
|
||||
void setupUI();
|
||||
|
||||
public:
|
||||
GameState(GameWindow *W, const std::string &servHost, int servPort);
|
||||
~GameState();
|
||||
|
||||
void onMouseButton(int key, int action, int mods);
|
||||
void onCursorPos(double x, double y);
|
||||
void onMouseScroll(double x, double y);
|
||||
void onKey(int key, int scancode, int action, int mods);
|
||||
void onChar(char32 unichar);
|
||||
void onResize(int w, int h);
|
||||
void run();
|
||||
|
||||
void updateViewport();
|
||||
|
||||
bool connectLoop();
|
||||
bool teamSelectLoop();
|
||||
bool classSelectLoop();
|
||||
void gameLoop();
|
||||
|
||||
void updateUI();
|
||||
void drawUI();
|
||||
bool processNetwork();
|
||||
|
||||
void sendMsg(Net::OutMessage &msg, Net::Tfer mode);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,136 @@
|
|||
#include "GameWindow.hpp"
|
||||
#include <al.h>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include "Game.hpp"
|
||||
#include "GlobalProperties.hpp"
|
||||
#include "GLFWHandler.hpp"
|
||||
#include "GameState.hpp"
|
||||
#include "MessageState.hpp"
|
||||
#include "Audio.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
int GameWindow::InstanceCount = 0;
|
||||
bool GameWindow::IsGlewInited = false;
|
||||
|
||||
GameWindow::GameWindow() {
|
||||
if (InstanceCount++ == 0) {
|
||||
int glfwStatus = glfwInit();
|
||||
if (glfwStatus != GL_TRUE) {
|
||||
getDebugStream() << "GLFW init failed: " << glfwStatus << std::endl;
|
||||
std::terminate();
|
||||
}
|
||||
std::cout << "GLFW " << glfwGetVersionString() << std::endl;
|
||||
}
|
||||
|
||||
GLFWHandler::getInstance().setWindow(this, m_window);
|
||||
|
||||
m_w = 640; m_h = 480;
|
||||
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
||||
glfwWindowHint(GLFW_SAMPLES, 0); // Gimme aliasing everywhere
|
||||
//glfwWindowHint(GLFW_STENCIL_BITS, 8);
|
||||
m_window = glfwCreateWindow(m_w, m_h, "Diggler", nullptr, nullptr);
|
||||
if (!m_window) {
|
||||
glfwTerminate();
|
||||
std::terminate();
|
||||
}
|
||||
glfwSetFramebufferSizeCallback(m_window, GLFWHandler::resize);
|
||||
glfwSetCursorPosCallback(m_window, GLFWHandler::cursorPos);
|
||||
glfwSetKeyCallback(m_window, GLFWHandler::key);
|
||||
glfwSetMouseButtonCallback(m_window, GLFWHandler::mouseButton);
|
||||
glfwSetCharCallback(m_window, GLFWHandler::unichar);
|
||||
glfwMakeContextCurrent(m_window);
|
||||
glfwSwapInterval(1);
|
||||
/*GLint bits;
|
||||
glGetIntegerv(GL_STENCIL_BITS, &bits);
|
||||
getDebugStream() << bits << " stencil bits" << std::endl;*/
|
||||
|
||||
if (!IsGlewInited) {
|
||||
GLenum glewStatus = glewInit();
|
||||
if (glewStatus != GLEW_OK) {
|
||||
getDebugStream() << "GLEW init failed: " << glewStatus << std::endl;
|
||||
std::terminate();
|
||||
}
|
||||
IsGlewInited = true;
|
||||
std::cout << "GLEW " << glewGetString(GLEW_VERSION) << std::endl;
|
||||
}
|
||||
|
||||
if (InstanceCount == 1) { // If we're the first instance
|
||||
const uint8 *GL_version = glGetString(GL_VERSION);
|
||||
//const uint8 *GL_vendor = glGetString(GL_VENDOR);
|
||||
const uint8 *GL_renderer = glGetString(GL_RENDERER);
|
||||
std::cout << "GL " << GL_version << " / " << GL_renderer << std::endl;
|
||||
}
|
||||
|
||||
UIM.setProjMat(glm::ortho(0.0f, (float)m_w, 0.0f, (float)m_h));
|
||||
|
||||
G = new Game;
|
||||
UIM.setup(G);
|
||||
G->GW = this;
|
||||
G->UIM = &UIM;
|
||||
G->A->loadSoundAssets();
|
||||
|
||||
G->F = new Font(G, getAssetPath("04b08.png"));
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
|
||||
GameWindow::~GameWindow() {
|
||||
delete G->F;
|
||||
delete G;
|
||||
|
||||
glfwDestroyWindow(m_window);
|
||||
|
||||
if (--InstanceCount == 0) {
|
||||
glfwTerminate();
|
||||
}
|
||||
}
|
||||
|
||||
void GameWindow::cbChar(char32 unichar) {
|
||||
m_currentState->onChar(unichar);
|
||||
}
|
||||
|
||||
void GameWindow::cbKey(int key, int scancode, int action, int mods) {
|
||||
m_currentState->onKey(key, scancode, action, mods);
|
||||
}
|
||||
|
||||
void GameWindow::cbMouseButton(int key, int action, int mods) {
|
||||
m_currentState->onMouseButton(key, action, mods);
|
||||
}
|
||||
|
||||
void GameWindow::cbCursorPos(double x, double y) {
|
||||
m_currentState->onCursorPos(x, y);
|
||||
}
|
||||
|
||||
void GameWindow::cbMouseScroll(double x, double y) {
|
||||
m_currentState->onMouseScroll(x, y);
|
||||
}
|
||||
|
||||
void GameWindow::cbResize(int w, int h) {
|
||||
m_w = w; m_h = h;
|
||||
UIM.setProjMat(glm::ortho(0.0f, (float)w, 0.0f, (float)h));
|
||||
m_currentState->onResize(w, h);
|
||||
}
|
||||
|
||||
void GameWindow::setNextState(const shared_ptr<State> next) {
|
||||
m_nextState = next;
|
||||
}
|
||||
|
||||
void GameWindow::run() {
|
||||
while (m_nextState != nullptr && !glfwWindowShouldClose(m_window)) {
|
||||
m_currentState = m_nextState;
|
||||
m_nextState = nullptr;
|
||||
UIM.clear();
|
||||
m_currentState->run();
|
||||
}
|
||||
}
|
||||
|
||||
void GameWindow::showMessage(const std::string &msg, const std::string &submsg) {
|
||||
setNextState(std::make_shared<MessageState>(this, msg, submsg));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
#ifndef GAME_WINDOW_HPP
|
||||
#define GAME_WINDOW_HPP
|
||||
#include <alc.h>
|
||||
#include <GL/glew.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <memory>
|
||||
#include <glm/detail/type_mat.hpp>
|
||||
#include "Platform.hpp"
|
||||
#include "ui/Manager.hpp"
|
||||
|
||||
using std::shared_ptr;
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class Game;
|
||||
class State;
|
||||
|
||||
class GameWindow {
|
||||
private:
|
||||
static int InstanceCount;
|
||||
static bool IsGlewInited;
|
||||
|
||||
GLFWwindow *m_window;
|
||||
int m_w, m_h;
|
||||
|
||||
shared_ptr<State> m_currentState, m_nextState;
|
||||
|
||||
public:
|
||||
UI::Manager UIM;
|
||||
|
||||
Game *G;
|
||||
|
||||
GameWindow();
|
||||
~GameWindow();
|
||||
|
||||
operator GLFWwindow&() const { return *m_window; }
|
||||
operator GLFWwindow*() const { return m_window; }
|
||||
|
||||
inline int getW() const { return m_w; }
|
||||
inline int getH() const { return m_h; }
|
||||
|
||||
void cbMouseButton(int key, int action, int mods);
|
||||
void cbCursorPos(double x, double y);
|
||||
void cbMouseScroll(double x, double y);
|
||||
void cbKey(int key, int scancode, int action, int mods);
|
||||
void cbChar(char32 unichar);
|
||||
void cbResize(int w, int h);
|
||||
|
||||
void updateViewport();
|
||||
|
||||
void setNextState(const shared_ptr<State> next);
|
||||
void run();
|
||||
|
||||
void showMessage(const std::string &msg, const std::string &submsg = "");
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,45 @@
|
|||
#include "GlUtils.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
std::string GlUtils::getErrorString(GLenum code) {
|
||||
switch (code) {
|
||||
case GL_NO_ERROR:
|
||||
return "GL_NO_ERROR";
|
||||
case GL_INVALID_ENUM:
|
||||
return "GL_INVALID_ENUM";
|
||||
case GL_INVALID_VALUE:
|
||||
return "GL_INVALID_VALUE";
|
||||
case GL_INVALID_OPERATION:
|
||||
return "GL_INVALID_OPERATION";
|
||||
case GL_STACK_OVERFLOW:
|
||||
return "GL_STACK_OVERFLOW";
|
||||
case GL_STACK_UNDERFLOW:
|
||||
return "GL_STACK_UNDERFLOW";
|
||||
case GL_OUT_OF_MEMORY:
|
||||
return "GL_OUT_OF_MEMORY";
|
||||
case GL_INVALID_FRAMEBUFFER_OPERATION:
|
||||
return "GL_INVALID_FRAMEBUFFER_OPERATION";
|
||||
case GL_TABLE_TOO_LARGE:
|
||||
return "GL_TABLE_TOO_LARGE";
|
||||
case GL_FRAMEBUFFER_COMPLETE:
|
||||
return "GL_FRAMEBUFFER_COMPLETE";
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
|
||||
return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
|
||||
#ifdef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
|
||||
return "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
|
||||
#endif
|
||||
#ifdef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
|
||||
return "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT";
|
||||
#endif
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
|
||||
return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
|
||||
case GL_FRAMEBUFFER_UNSUPPORTED:
|
||||
return "GL_FRAMEBUFFER_UNSUPPORTED";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
#ifndef GL_UTILS_HPP
|
||||
#define GL_UTILS_HPP
|
||||
#include <GL/glew.h>
|
||||
#include <string>
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class GlUtils {
|
||||
private:
|
||||
GlUtils();
|
||||
public:
|
||||
static std::string getErrorString(GLenum code);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,18 @@
|
|||
#include "GlobalProperties.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
bool GlobalProperties::IsClient = true;
|
||||
bool GlobalProperties::IsServer = false;
|
||||
|
||||
const char* GlobalProperties::DefaultServerHost = "localhost";
|
||||
const int GlobalProperties::DefaultServerPort = 61425;
|
||||
|
||||
bool GlobalProperties::UseProceduralTextures = false;
|
||||
|
||||
bool GlobalProperties::IsSoundEnabled = true;
|
||||
|
||||
int GlobalProperties::UIScale = 2;
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
#ifndef GLOBAL_PROPERTIES_HPP
|
||||
#define GLOBAL_PROPERTIES_HPP
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
namespace GlobalProperties {
|
||||
extern bool IsClient;
|
||||
extern bool IsServer;
|
||||
|
||||
extern const char* DefaultServerHost;
|
||||
extern const int DefaultServerPort;
|
||||
|
||||
extern bool UseProceduralTextures;
|
||||
|
||||
extern bool IsSoundEnabled;
|
||||
|
||||
extern int UIScale;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,60 @@
|
|||
#include "KeyBindings.hpp"
|
||||
#include "Platform.hpp"
|
||||
#include <cstdio>
|
||||
#include <stdexcept>
|
||||
|
||||
using std::fopen;
|
||||
using std::fwrite;
|
||||
using std::fread;
|
||||
using std::fclose;
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
KeyBindings::KeyBindings() {
|
||||
|
||||
}
|
||||
|
||||
void KeyBindings::load(const std::string &path) {
|
||||
FILE *f = fopen(path.c_str(), "r");
|
||||
if (!f)
|
||||
return;
|
||||
int32 count; fread(&count, sizeof(count), 1, f);
|
||||
for (int i=0; i < count; i++) {
|
||||
Bind bind; fread(&bind, sizeof(Bind), 1, f);
|
||||
int key; fread(&key, sizeof(key), 1, f); // Ambiguous int size, but GLFW uses ints as-is
|
||||
auto found = m_bindings.find(bind);
|
||||
if (found != m_bindings.end())
|
||||
m_bindings.erase(found);
|
||||
m_bindings.insert(std::pair<Bind, int>(bind, key));
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void KeyBindings::save(const std::string &path) const {
|
||||
FILE *f = fopen(path.c_str(), "w");
|
||||
if (!f)
|
||||
return;
|
||||
int32 count = m_bindings.size(); fwrite(&count, sizeof(count), 1, f);
|
||||
for (auto iter=m_bindings.begin(); iter != m_bindings.end(); iter++) {
|
||||
fwrite(&iter->first, sizeof(iter->first), 1, f);
|
||||
fwrite(&iter->second, sizeof(iter->second), 1, f);
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
int KeyBindings::get(Bind bind) {
|
||||
try {
|
||||
return m_bindings.at(bind);
|
||||
} catch (const std::out_of_range &e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void KeyBindings::set(Bind bind, int key) {
|
||||
auto found = m_bindings.find(bind);
|
||||
if (found != m_bindings.end())
|
||||
m_bindings.erase(found);
|
||||
m_bindings.insert(std::pair<Bind, int>(bind, key));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef KEY_BINDINGS_HPP
|
||||
#define KEY_BINDINGS_HPP
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class KeyBindings {
|
||||
public:
|
||||
enum Bind {
|
||||
Forward,
|
||||
Backward,
|
||||
Left,
|
||||
Right,
|
||||
Jump,
|
||||
Sprint,
|
||||
PlayerList,
|
||||
};
|
||||
|
||||
private:
|
||||
std::map<Bind, int> m_bindings;
|
||||
|
||||
public:
|
||||
KeyBindings();
|
||||
void set(Bind bind, int key);
|
||||
int get(Bind bind);
|
||||
void save(const std::string &path) const;
|
||||
void load(const std::string &path);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,674 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
|
@ -0,0 +1,255 @@
|
|||
#include "LocalPlayer.hpp"
|
||||
#include "Game.hpp"
|
||||
#include <cstdio>
|
||||
#include <glm/gtx/rotate_vector.hpp>
|
||||
#include <sstream>
|
||||
#include "Audio.hpp"
|
||||
#include "network/NetHelper.hpp"
|
||||
|
||||
// I'm shit at player physics, so I kinda copied jMonkey
|
||||
// https://github.com/jMonkeyEngine/jmonkeyengine/blob/1b0f6d0f59650772bac4588d1733c061ff50d8c8/jme3-bullet/src/common/java/com/jme3/bullet/control/BetterCharacterControl.java
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
static float Acceleration = 24.0f;
|
||||
|
||||
static float MvmtDamping = 1/24.0f;
|
||||
|
||||
static float Gravity = 18.0f; // -Y acceleration (blocks/sec/sec)
|
||||
|
||||
static float JumpForce = Gravity/2.8f;
|
||||
|
||||
static float MaxSpeed = 6.f;
|
||||
static float RoadMaxSpeed = 12.f;
|
||||
|
||||
static int i(const float &f) {
|
||||
if (f >= 0)
|
||||
return (int)f;
|
||||
return ((int)f)-1;
|
||||
}
|
||||
|
||||
LocalPlayer::LocalPlayer(Game *G) : Player(G), goingForward(false), goingBackward(false), goingLeft(false), goingRight(false),
|
||||
hasGravity(true), hasNoclip(false), t(nullptr) {
|
||||
size = glm::vec3(0.3f, 1.9f, 0.3f);
|
||||
eyesPos = glm::vec3(0.f, 1.7f, 0.f);
|
||||
velocity = position = glm::vec3(0.f);
|
||||
}
|
||||
|
||||
void LocalPlayer::special1() {
|
||||
}
|
||||
|
||||
// thx http://martin.ankerl.com/2012/01/25/optimized-approximative-pow-in-c-and-cpp/
|
||||
inline double fpow(double a, double b) {
|
||||
union {
|
||||
double d;
|
||||
int x[2];
|
||||
} u = { a };
|
||||
u.x[1] = (int)(b * (u.x[1] - 1072632447) + 1072632447);
|
||||
u.x[0] = 0;
|
||||
return u.d;
|
||||
}
|
||||
|
||||
void LocalPlayer::lookAt(const glm::vec3& at) {
|
||||
camera.lookAt(at);
|
||||
G->A->updateAngle();
|
||||
}
|
||||
|
||||
void LocalPlayer::update(const float &delta) {
|
||||
bool moving = goingForward || goingBackward || goingLeft || goingRight;
|
||||
glm::vec3 initialVel = velocity;
|
||||
if (!moving) {
|
||||
float finalDamp = fpow(MvmtDamping, delta);
|
||||
velocity.x *= finalDamp;
|
||||
velocity.z *= finalDamp;
|
||||
if (!this->hasGravity)
|
||||
velocity.y *= finalDamp;
|
||||
}
|
||||
|
||||
// Apply player's will movement
|
||||
glm::vec3 normMove = glm::normalize(glm::vec3(camera.m_lookAt.x, camera.m_lookAt.y * !hasGravity, camera.m_lookAt.z));
|
||||
float acceleration = Acceleration*delta;
|
||||
if (goingForward) {
|
||||
velocity += acceleration * normMove;
|
||||
}
|
||||
if (goingBackward) {
|
||||
velocity -= acceleration * normMove;
|
||||
}
|
||||
if (goingLeft) {
|
||||
velocity += acceleration * glm::rotateY(glm::normalize(glm::vec3(normMove.x, 0, normMove.z)), (float)M_PI/2);
|
||||
}
|
||||
if (goingRight) {
|
||||
velocity -= acceleration * glm::rotateY(glm::normalize(glm::vec3(normMove.x, 0, normMove.z)), (float)M_PI/2);
|
||||
}
|
||||
|
||||
// Apply gravity
|
||||
if (hasGravity) {
|
||||
if (onGround) {
|
||||
BlockType b = G->SC->get(position.x, position.y-1, position.z);
|
||||
onRoad = (b == BlockType::Road);
|
||||
onGround = !Blocks::canGoThrough(b, team);
|
||||
}
|
||||
if (!onGround)
|
||||
velocity.y -= Gravity * delta;
|
||||
}
|
||||
|
||||
if (!hasNoclip) {
|
||||
glm::vec3 velXZ(velocity.x, 0, velocity.z);
|
||||
int maxSpeed = (onRoad ? RoadMaxSpeed : MaxSpeed);
|
||||
if (glm::length(velXZ) > maxSpeed) {
|
||||
velXZ = glm::normalize(velXZ);
|
||||
velocity.x = velXZ.x * maxSpeed;
|
||||
velocity.z = velXZ.z * maxSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
if (velocity.x > -0.001f && velocity.x < 0.001f) velocity.x = 0.f;
|
||||
if (velocity.y > -0.001f && velocity.y < 0.001f) velocity.y = 0.f;
|
||||
if (velocity.z > -0.001f && velocity.z < 0.001f) velocity.z = 0.f;
|
||||
|
||||
glm::vec3 destPos = position + velocity * delta;
|
||||
accel = velocity - initialVel;
|
||||
|
||||
if (velocity != glm::vec3(0.f)) { // avoids useless calculus
|
||||
if (hasNoclip) {
|
||||
position = destPos;
|
||||
} else {
|
||||
float x = destPos.x, y = destPos.y, z = destPos.z;
|
||||
BlockType bTop = G->SC->get(x, y+size.y, z),
|
||||
bBottom = G->SC->get(x, y, z);
|
||||
if (velocity.y > 0.f)
|
||||
if (!Blocks::canGoThrough(bTop, team)) {
|
||||
velocity.y = 0.f;
|
||||
position.y = (int)(position.y+size.y+1)-size.y;
|
||||
}
|
||||
if (velocity.y < 0.f)
|
||||
if (!Blocks::canGoThrough(bBottom, team)) {
|
||||
velocity.y = 0.f;
|
||||
y = position.y = (int)(position.y);
|
||||
onGround = 1;
|
||||
}
|
||||
bTop = G->SC->get(x, y+size.y, z);
|
||||
bBottom = G->SC->get(x, y-onGround, z);
|
||||
BlockType bNTop = G->SC->get(x+size.x, y+size.y, z),
|
||||
bNBottom = G->SC->get(x+size.x, y, z),
|
||||
|
||||
bSTop = G->SC->get(x-size.x, y+size.y, z),
|
||||
bSBottom = G->SC->get(x-size.x, y, z),
|
||||
|
||||
bETop = G->SC->get(x, y+size.y, z+size.z),
|
||||
bEBottom = G->SC->get(x, y, z+size.z),
|
||||
|
||||
bWTop = G->SC->get(x, y+size.y, z-size.z),
|
||||
bWBottom = G->SC->get(x, y, z-size.z);
|
||||
if (velocity.x > 0.f)
|
||||
if (!Blocks::canGoThrough(bNTop, team) || !Blocks::canGoThrough(bNBottom, team)) {
|
||||
if (bNTop == BlockType::Lava || bNBottom == BlockType::Lava) {
|
||||
kill(DeathReason::Lava);
|
||||
return;
|
||||
}
|
||||
velocity.x = 0.f;
|
||||
}
|
||||
if (velocity.x < 0.f)
|
||||
if (!Blocks::canGoThrough(bSTop, team) || !Blocks::canGoThrough(bSBottom, team)) {
|
||||
if (bSTop == BlockType::Lava || bSBottom == BlockType::Lava) {
|
||||
kill(DeathReason::Lava);
|
||||
return;
|
||||
}
|
||||
velocity.x = 0.f;
|
||||
}
|
||||
if (velocity.z > 0.f)
|
||||
if (!Blocks::canGoThrough(bETop, team) || !Blocks::canGoThrough(bEBottom, team)) {
|
||||
if (bETop == BlockType::Lava || bEBottom == BlockType::Lava) {
|
||||
kill(DeathReason::Lava);
|
||||
return;
|
||||
}
|
||||
velocity.z = 0.f;
|
||||
}
|
||||
if (velocity.z < 0.f)
|
||||
if (!Blocks::canGoThrough(bWTop, team) || !Blocks::canGoThrough(bWBottom, team)) {
|
||||
if (bWTop == BlockType::Lava || bWBottom == BlockType::Lava) {
|
||||
kill(DeathReason::Lava);
|
||||
return;
|
||||
}
|
||||
velocity.z = 0.f;
|
||||
}
|
||||
switch (bTop) {
|
||||
case BlockType::Lava:
|
||||
kill(DeathReason::Lava);
|
||||
return;
|
||||
case BlockType::Shock:
|
||||
kill(DeathReason::Shock);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (bBottom) {
|
||||
case BlockType::Jump:
|
||||
if (G->Time - lastJumpTime > 0.2) {
|
||||
velocity.y = JumpForce * 2;
|
||||
NetHelper::SendEvent(G, Net::EventType::PlayerJumpOnPad);
|
||||
lastJumpTime = G->Time;
|
||||
}
|
||||
break;
|
||||
|
||||
case BlockType::Lava:
|
||||
kill(DeathReason::Lava);
|
||||
return;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
position += velocity * delta;
|
||||
}
|
||||
|
||||
camera.setPosition(position + eyesPos);
|
||||
if (t != nullptr) {
|
||||
std::ostringstream oss;
|
||||
oss << G->LP->position.y << std::endl << i(G->LP->position.y) << std::endl <<
|
||||
G->LP->velocity.x << " " << G->LP->velocity.y << " " << G->LP->velocity.z;
|
||||
t->setText(oss.str());
|
||||
}
|
||||
|
||||
G->A->updatePos();
|
||||
}
|
||||
}
|
||||
|
||||
void LocalPlayer::forceCameraUpdate() {
|
||||
camera.setPosition(position + eyesPos);
|
||||
}
|
||||
|
||||
void LocalPlayer::kill(Player::DeathReason dr) {
|
||||
getDebugStream() << "Pretend you're dead" << std::endl;
|
||||
}
|
||||
|
||||
void LocalPlayer::goForward(bool enable) {
|
||||
goingForward = enable;
|
||||
}
|
||||
void LocalPlayer::goBackward(bool enable) {
|
||||
goingBackward = enable;
|
||||
}
|
||||
void LocalPlayer::goLeft(bool enable) {
|
||||
goingLeft = enable;
|
||||
}
|
||||
void LocalPlayer::goRight(bool enable) {
|
||||
goingRight = enable;
|
||||
}
|
||||
|
||||
void LocalPlayer::setHasGravity(bool fall) {
|
||||
hasGravity = fall;
|
||||
onGround = false;
|
||||
}
|
||||
|
||||
void LocalPlayer::setHasNoclip(bool fly) {
|
||||
hasNoclip = fly;
|
||||
setHasGravity(!fly);
|
||||
}
|
||||
|
||||
void LocalPlayer::jump() {
|
||||
if (!onGround)
|
||||
return;
|
||||
onGround = false;
|
||||
velocity.y += JumpForce;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
#ifndef LOCAL_PLAYER_HPP
|
||||
#define LOCAL_PLAYER_HPP
|
||||
#include "Camera.hpp"
|
||||
#include "Platform.hpp"
|
||||
#include "ui/Text.hpp"
|
||||
#include "Player.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class Game;
|
||||
|
||||
class LocalPlayer : public Player {
|
||||
private:
|
||||
bool goingForward, goingBackward, goingLeft, goingRight;
|
||||
bool hasGravity, hasNoclip, onGround, onRoad;
|
||||
|
||||
// Fixes
|
||||
double lastJumpTime = 0.0;
|
||||
|
||||
public:
|
||||
UI::Text *t;
|
||||
Camera camera;
|
||||
AABB bounds;
|
||||
glm::vec3 size, eyesPos;
|
||||
|
||||
LocalPlayer(Game *G);
|
||||
|
||||
inline glm::mat4 getPVMatrix() { return camera.getPVMatrix(); }
|
||||
void lookAt(const glm::vec3 &at);
|
||||
inline void setProjection(const glm::mat4 &p) { camera.setProjection(p); }
|
||||
void update(const float &delta);
|
||||
void forceCameraUpdate();
|
||||
void goForward(bool enable);
|
||||
void goBackward(bool enable);
|
||||
void goLeft(bool enable);
|
||||
void goRight(bool enable);
|
||||
void setHasGravity(bool fall);
|
||||
void setHasNoclip(bool fly);
|
||||
void jump();
|
||||
void kill(DeathReason dr);
|
||||
void special1();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,56 @@
|
|||
#include "MessageState.hpp"
|
||||
#include "_.hpp"
|
||||
#include "ui/Text.hpp"
|
||||
#include "Game.hpp"
|
||||
#include "GlobalProperties.hpp"
|
||||
#include "Sound.hpp"
|
||||
#include "Audio.hpp"
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
MessageState::MessageState(GameWindow *W, const std::string &msg, const std::string &submsg)
|
||||
: W(W), msg(msg), subMsg(submsg), txtMsg(nullptr), txtSubMsg(nullptr) {
|
||||
glfwSetInputMode(*W, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
||||
}
|
||||
|
||||
MessageState::~MessageState() {
|
||||
}
|
||||
|
||||
void MessageState::setupUI() {
|
||||
txtMsg = W->G->UIM->add<UI::Text>(W->G->F, msg, 2, 2);
|
||||
txtSubMsg = W->G->UIM->add<UI::Text>(W->G->F, subMsg);
|
||||
updateViewport();
|
||||
}
|
||||
|
||||
void MessageState::updateViewport() {
|
||||
glViewport(0, 0, W->getW(), W->getH());
|
||||
|
||||
UI::Text::Size txtMsgSize = txtMsg->getSize();
|
||||
txtMsg->setPos(W->getW()/2 - txtMsgSize.x/2, W->getH()/2);
|
||||
|
||||
UI::Text::Size txtSubMsgSize = txtSubMsg->getSize();
|
||||
txtSubMsg->setPos(W->getW()/2 - txtSubMsgSize.x / 2, W->getH()/2 - txtSubMsgSize.y);
|
||||
}
|
||||
|
||||
void MessageState::onResize(int w, int h) {
|
||||
updateViewport();
|
||||
}
|
||||
|
||||
void MessageState::run() {
|
||||
setupUI();
|
||||
if (GlobalProperties::IsSoundEnabled) {
|
||||
W->G->A->playSound("click-quiet");
|
||||
}
|
||||
while (!glfwWindowShouldClose(*W)) {
|
||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
W->G->UIM->render();
|
||||
|
||||
glfwSwapBuffers(*W);
|
||||
glfwPollEvents();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef MESSAGE_STATE_HPP
|
||||
#define MESSAGE_STATE_HPP
|
||||
#include "State.hpp"
|
||||
#include "GameWindow.hpp"
|
||||
#include "ui/Text.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class MessageState : public State {
|
||||
private:
|
||||
GameWindow *W;
|
||||
std::string msg, subMsg;
|
||||
UI::Text *txtMsg, *txtSubMsg;
|
||||
|
||||
void setupUI();
|
||||
|
||||
public:
|
||||
MessageState(GameWindow *W, const std::string &msg, const std::string &submsg = "");
|
||||
~MessageState();
|
||||
|
||||
//void onMouseButton(int key, int action, int mods);
|
||||
//void onCursorPos(double x, double y);
|
||||
void onResize(int w, int h);
|
||||
void run();
|
||||
|
||||
void updateViewport();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,29 @@
|
|||
#include "Mutex.hpp"
|
||||
#include <cstdlib>
|
||||
#include <pthread.h>
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
Mutex::Mutex() {
|
||||
data = (void*)std::malloc(sizeof(pthread_mutex_t));
|
||||
pthread_mutex_init((pthread_mutex_t*)data, nullptr);
|
||||
}
|
||||
|
||||
Mutex::~Mutex() {
|
||||
pthread_mutex_destroy((pthread_mutex_t*)data);
|
||||
free(data);
|
||||
}
|
||||
|
||||
void Mutex::lock() {
|
||||
pthread_mutex_lock((pthread_mutex_t*)data);
|
||||
}
|
||||
|
||||
bool Mutex::try_lock() {
|
||||
return pthread_mutex_trylock((pthread_mutex_t*)data) == 0;
|
||||
}
|
||||
|
||||
void Mutex::unlock() {
|
||||
pthread_mutex_unlock((pthread_mutex_t*)data);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
#ifndef MUTEX_HPP
|
||||
#define MUTEX_HPP
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class Mutex {
|
||||
private:
|
||||
void *data;
|
||||
|
||||
public:
|
||||
Mutex();
|
||||
~Mutex();
|
||||
Mutex& operator=(Mutex &m) = delete;
|
||||
|
||||
void lock();
|
||||
bool try_lock();
|
||||
void unlock();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,194 @@
|
|||
#include "Platform.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <functional>
|
||||
#include <climits>
|
||||
#include <cinttypes>
|
||||
#include <random>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
double (*Diggler::getTime)() = glfwGetTime;
|
||||
|
||||
|
||||
#ifdef BUILDINFO_PLATFORM_WINDOWS // Windows
|
||||
|
||||
// Put windows crap here
|
||||
|
||||
#elif defined(BUILDINFO_PLATFORM_LINUX) || defined(BUILDINFO_PLATFORM_ANDROID) // Linux
|
||||
|
||||
#include <sstream>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <cstdio>
|
||||
|
||||
std::string do_readlink(const char *path) throw(int) {
|
||||
char buff[PATH_MAX];
|
||||
ssize_t len = readlink(path, buff, sizeof(buff) - 1);
|
||||
if(len >= 0) {
|
||||
buff[len] = '\0';
|
||||
return std::string(buff);
|
||||
} else {
|
||||
throw len;
|
||||
}
|
||||
}
|
||||
std::string do_readlink(const std::string &path) throw(int) {
|
||||
if(path.length() > PATH_MAX)
|
||||
throw -1;
|
||||
return do_readlink(path.c_str());
|
||||
}
|
||||
|
||||
bool buildCachedExecutablePath = true;
|
||||
std::string cachedExecutablePath;
|
||||
|
||||
std::string Diggler::getExecutablePath() {
|
||||
if(buildCachedExecutablePath) {
|
||||
pid_t pid = getpid();
|
||||
// Assuming 32-bit pid -> max of 10 digits, we need only "/proc/xxxxxxxxxx/exe" space
|
||||
char path[22];
|
||||
std::sprintf(path, "/proc/%d/exe", pid);
|
||||
cachedExecutablePath = do_readlink(path);
|
||||
buildCachedExecutablePath = false;
|
||||
}
|
||||
return cachedExecutablePath;
|
||||
}
|
||||
|
||||
bool buildCachedExecutableDirectory = true;
|
||||
std::string cachedExecutableDirectory;
|
||||
|
||||
std::string Diggler::getExecutableDirectory() {
|
||||
if(buildCachedExecutableDirectory) {
|
||||
std::string filename(getExecutablePath());
|
||||
const size_t last_slash_idx = filename.rfind('/');
|
||||
if(last_slash_idx != std::string::npos) {
|
||||
cachedExecutableDirectory = filename.substr(0, last_slash_idx + 1);
|
||||
} else {
|
||||
getErrorStream() << "Ill-formed executable path: " << filename << std::endl;
|
||||
cachedExecutableDirectory = filename;
|
||||
}
|
||||
buildCachedExecutableDirectory = false;
|
||||
}
|
||||
return cachedExecutableDirectory;
|
||||
}
|
||||
|
||||
std::string Diggler::fs::pathCat(const std::string &first, const std::string &second) {
|
||||
if(first.at(first.length()-1) == '/')
|
||||
return first + second;
|
||||
return first + '/' + second;
|
||||
}
|
||||
|
||||
std::string Diggler::fs::getParent(const std::string &path) {
|
||||
return path.substr(path.find_last_of('/'));
|
||||
}
|
||||
|
||||
std::vector<std::string> Diggler::fs::getContents(const std::string &path) {
|
||||
DIR *dir = opendir(path.c_str());
|
||||
if(dir == 0)
|
||||
return std::vector<std::string>();
|
||||
std::vector<std::string> entitys = std::vector<std::string>();
|
||||
dirent *entity;
|
||||
while((entity = readdir(dir)) != nullptr) {
|
||||
if(entity->d_name[0] == '.' && (entity->d_name[1] == 0 || (entity->d_name[1] == '.' && entity->d_name[2] == 0)))
|
||||
continue;
|
||||
entitys.push_back(entity->d_name);
|
||||
}
|
||||
return entitys;
|
||||
}
|
||||
|
||||
std::vector<std::string> Diggler::fs::getDirs(const std::string &path) {
|
||||
DIR *dir = opendir(path.c_str());
|
||||
if(dir == 0)
|
||||
return std::vector<std::string>();
|
||||
std::vector<std::string> entitys = std::vector<std::string>();
|
||||
dirent *entity;
|
||||
while((entity = readdir(dir)) != nullptr) {
|
||||
if(entity->d_name[0] == '.' && (entity->d_name[1] == 0 || (entity->d_name[1] == '.' && entity->d_name[2] == 0)))
|
||||
continue;
|
||||
if(isDir(pathCat(path, entity->d_name)))
|
||||
entitys.push_back(entity->d_name);
|
||||
}
|
||||
return entitys;
|
||||
}
|
||||
|
||||
std::vector<std::string> Diggler::fs::getFiles(const std::string &path) {
|
||||
DIR *dir = opendir(path.c_str());
|
||||
if(dir == 0)
|
||||
return std::vector<std::string>();
|
||||
std::vector<std::string> entitys = std::vector<std::string>();
|
||||
dirent *entity;
|
||||
while((entity = readdir(dir)) != nullptr) {
|
||||
if(entity->d_name[0] == '.' && (entity->d_name[1] == 0 || (entity->d_name[1] == '.' && entity->d_name[2] == 0)))
|
||||
continue;
|
||||
if(!isDir(pathCat(path, entity->d_name)))
|
||||
entitys.push_back(entity->d_name);
|
||||
}
|
||||
return entitys;
|
||||
}
|
||||
|
||||
inline bool Diggler::fs::isDir(const std::string &path) {
|
||||
DIR *dir = opendir(path.c_str());
|
||||
if(dir)
|
||||
{
|
||||
closedir(dir);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#elif defined(BUILDINFO_PLATFORM_MAC) // Mac
|
||||
|
||||
// Put Mac crap here
|
||||
|
||||
#else // Any other
|
||||
|
||||
//Put other craps here
|
||||
|
||||
#endif
|
||||
|
||||
std::string Diggler::fs::readFile(const std::string &path) {
|
||||
std::ifstream in(path, std::ios::in | std::ios::binary);
|
||||
if (in) {
|
||||
std::string contents;
|
||||
in.seekg(0, std::ios::end);
|
||||
contents.resize(in.tellg());
|
||||
in.seekg(0, std::ios::beg);
|
||||
in.read(&contents[0], contents.size());
|
||||
in.close();
|
||||
return contents;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string Diggler::getAssetsDirectory() {
|
||||
return Diggler::getExecutableDirectory() + "assets/";
|
||||
}
|
||||
|
||||
std::string Diggler::getAssetsDirectory(const std::string &type) {
|
||||
return Diggler::getExecutableDirectory() + "assets/" + type + '/';
|
||||
}
|
||||
|
||||
std::string Diggler::getAssetPath(const std::string &name) {
|
||||
return Diggler::getExecutableDirectory() + "assets/" + name;
|
||||
}
|
||||
|
||||
std::string Diggler::getAssetPath(const std::string &type, const std::string &name) {
|
||||
return Diggler::getExecutableDirectory() + "assets/" + type + '/' + name;
|
||||
}
|
||||
|
||||
std::ostream& Diggler::getErrorStreamImpl() {
|
||||
return std::cerr;
|
||||
}
|
||||
|
||||
std::ostream& Diggler::getDebugStreamImpl() {
|
||||
return std::cout;
|
||||
}
|
||||
|
||||
std::ostream &Diggler::getOutputStreamImpl() {
|
||||
return std::cout;
|
||||
}
|
||||
|
||||
uint Diggler::FastRand_Seed = 0;
|
|
@ -0,0 +1,137 @@
|
|||
#ifndef PLATFORM_HPP
|
||||
#define PLATFORM_HPP
|
||||
|
||||
#define BUILDINFO_TIME __TIME__
|
||||
#define BUILDINFO_DATE __DATE__
|
||||
|
||||
#if defined(WINDOWS) || defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64) // Windows
|
||||
#define BUILDINFO_PLATFORM "Windows"
|
||||
#define BUILDINFO_PLATFORM_WINDOWS
|
||||
#elif defined(__ANDROID_API__)
|
||||
#define BUILDINFO_PLATFORM "Android"
|
||||
#define BUILDINFO_PLATFORM_ANDROID
|
||||
#elif defined(__linux__) || defined(linux) || defined(_linux) // Linux
|
||||
#define BUILDINFO_PLATFORM "Linux"
|
||||
#define BUILDINFO_PLATFORM_LINUX
|
||||
#elif defined(__APPLE__) || defined(__MACH__) // Mac
|
||||
#define BUILDINFO_PLATFORM "Mac"
|
||||
#define BUILDINFO_PLATFORM_MAC
|
||||
#else // Any other
|
||||
#define BUILDINFO_PLATFORM "Unknown"
|
||||
#endif
|
||||
|
||||
#if _WIN64 || __x86_64__ || __ppc64__
|
||||
#define HAS_NATIVE_64BIT 1
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
typedef int32_t int32;
|
||||
typedef uint32_t uint;
|
||||
typedef uint32_t uint32;
|
||||
typedef uint64_t uint64;
|
||||
typedef uint16_t uint16;
|
||||
typedef uint8_t uint8;
|
||||
typedef int64_t int64;
|
||||
typedef int16_t int16;
|
||||
typedef int8_t int8;
|
||||
typedef uint8_t byte;
|
||||
typedef char32_t char32;
|
||||
typedef char16_t char16;
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
extern double (*getTime)();
|
||||
|
||||
/// @returns The executable's absolute path
|
||||
std::string getExecutablePath();
|
||||
|
||||
/// @returns The executable's absolute path directory, including the end slash (/)
|
||||
std::string getExecutableDirectory();
|
||||
|
||||
#ifdef IN_IDE_PARSER
|
||||
/// @returns The system's error output stream
|
||||
std::ostream& getErrorStream();
|
||||
#else
|
||||
std::ostream& getErrorStreamImpl();
|
||||
#define getErrorStream() getErrorStreamImpl() << __FILENAME__ << ':' << __LINE__ << ' '
|
||||
#endif
|
||||
|
||||
#ifdef IN_IDE_PARSER
|
||||
/// @returns The system's debug output stream
|
||||
std::ostream& getDebugStream();
|
||||
#else
|
||||
std::ostream& getDebugStreamImpl();
|
||||
#define getDebugStream() getDebugStreamImpl() << __FILENAME__ << ':' << __LINE__ << ' '
|
||||
#endif
|
||||
|
||||
#ifdef IN_IDE_PARSER
|
||||
/// @returns The system's output stream
|
||||
std::ostream& getOutputStream();
|
||||
#else
|
||||
std::ostream& getOutputStreamImpl();
|
||||
#define getOutputStream() getOutputStreamImpl() << __FILENAME__ << ':' << __LINE__ << ' '
|
||||
#endif
|
||||
|
||||
|
||||
/// @returns The absolute specific assets directory path, including the end slash (/)
|
||||
std::string getAssetsDirectory(const std::string &type);
|
||||
|
||||
/// @returns The absolute assets directory path, including the end slash (/)
|
||||
std::string getAssetsDirectory();
|
||||
|
||||
/// @returns The absolute asset path
|
||||
std::string getAssetPath(const std::string &name);
|
||||
|
||||
/// @returns The absolute asset path
|
||||
std::string getAssetPath(const std::string &type, const std::string &name);
|
||||
|
||||
|
||||
extern uint FastRand_Seed;
|
||||
#define FastRandSeed(x) FastRand_Seed=x;
|
||||
///
|
||||
/// Fast pseudo-random number generator, very inaccurate
|
||||
/// @returns a random number between 0 and 2^31
|
||||
///
|
||||
inline int FastRand() {
|
||||
FastRand_Seed = (208413*((FastRand_Seed+4631018)>>1))^(0b01010001110101110101000101101001);
|
||||
return FastRand_Seed & 0x7FFFFFFF;
|
||||
}
|
||||
///
|
||||
/// Fast pseudo-random number generator, very inaccurate
|
||||
/// @returns a random number between 0 and max
|
||||
///
|
||||
inline int FastRand(int max) {
|
||||
return FastRand() % (max+1);
|
||||
}
|
||||
///
|
||||
/// Fast pseudo-random number generator, very inaccurate
|
||||
/// @returns a random number between min and max
|
||||
///
|
||||
inline int FastRand(int min, int max) {
|
||||
return min + (FastRand() % (max-min+1) );
|
||||
}
|
||||
///
|
||||
/// Fast pseudo-random number generator, very inaccurate
|
||||
/// @returns a random number between 0.f and 1.f
|
||||
///
|
||||
inline float FastRandF() {
|
||||
return (float)FastRand() / 0x7FFFFFFF;
|
||||
}
|
||||
|
||||
|
||||
namespace fs {
|
||||
bool isDir(const std::string &path);
|
||||
std::string pathCat(const std::string& first, const std::string &second);
|
||||
std::vector<std::string> getContents(const std::string &path);
|
||||
std::vector<std::string> getDirs(const std::string &path);
|
||||
std::vector<std::string> getFiles(const std::string &path);
|
||||
std::string readFile(const std::string &path);
|
||||
std::string getParent(const std::string &path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,157 @@
|
|||
#include "Texture.hpp"
|
||||
#include "Player.hpp"
|
||||
#include "Game.hpp"
|
||||
#include "GlobalProperties.hpp"
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
Texture ***Player::Textures = nullptr;
|
||||
const Program *Player::RenderProgram = nullptr;
|
||||
GLint Player::RenderProgram_attrib_texcoord = -1;
|
||||
GLint Player::RenderProgram_attrib_coord = -1;
|
||||
GLint Player::RenderProgram_uni_mvp = -1;
|
||||
|
||||
const char* Player::getTeamNameLowercase(Player::Team t) {
|
||||
switch (t) {
|
||||
case Team::Blue:
|
||||
return "blue";
|
||||
case Team::Red:
|
||||
return "red";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
const char* Player::getToolNameLowercase(Player::Tools t) {
|
||||
switch (t) {
|
||||
case Tools::ConstructionGun:
|
||||
case Tools::DeconstructionGun:
|
||||
return "construction";
|
||||
case Tools::Detonator:
|
||||
return "detonator";
|
||||
case Tools::Pickaxe:
|
||||
return "pickaxe";
|
||||
case Tools::ProspectingRadar:
|
||||
return "radar";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
int Player::getMaxOre(Class c) {
|
||||
switch (c) {
|
||||
case Class::Engineer:
|
||||
return 350;
|
||||
case Class::Miner:
|
||||
case Class::Prospector:
|
||||
case Class::Sapper:
|
||||
return 200;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Player::getMaxWeight(Class c) {
|
||||
switch (c) {
|
||||
case Class::Miner:
|
||||
return 8;
|
||||
case Class::Engineer:
|
||||
case Class::Prospector:
|
||||
case Class::Sapper:
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Player::Player(Game *G) : m_vbo(nullptr), team(Team::Red),
|
||||
playerclass(Class::Prospector), tool(Tools::Pickaxe), G(G) {
|
||||
if (GlobalProperties::IsClient) {
|
||||
m_vbo = new VBO();
|
||||
if (Textures == nullptr) {
|
||||
RenderProgram = G->PM->getProgram(PM_3D | PM_TEXTURED);
|
||||
RenderProgram_attrib_coord = RenderProgram->att("coord");
|
||||
RenderProgram_attrib_texcoord = RenderProgram->att("texcoord");
|
||||
RenderProgram_uni_mvp = RenderProgram->uni("mvp");
|
||||
|
||||
Textures = new Texture**[Team::LAST-1];
|
||||
for (uint8 t=1; t < (uint8)Team::LAST-1; t++) {
|
||||
Textures[t] = new Texture*[Tools::LAST];
|
||||
for (uint8 tool=0; tool < (uint8)Tools::LAST; tool++) {
|
||||
std::string aa = std::string("tex_sprite_") + getTeamNameLowercase(team) + '_' + getToolNameLowercase(this->tool) + ".png";
|
||||
Textures[t][tool] = new Texture(
|
||||
getAssetPath("sprites", std::string("tex_sprite_") + getTeamNameLowercase((Team)t) + '_' + getToolNameLowercase((Tools)tool) + ".png"),
|
||||
Texture::PixelFormat::RGBA);
|
||||
}
|
||||
}
|
||||
}
|
||||
float coords[6*5] = {
|
||||
-.5f, 0.0f, 0.0f, 0.0f, 1.0f,
|
||||
0.5f, 0.0f, 0.0f, 1.0f, 1.0f,
|
||||
0.5f, 1.0f, 0.0f, 1.0f, 0.0f,
|
||||
|
||||
-.5f, 1.0f, 0.0f, 0.0f, 0.0f,
|
||||
-.5f, 0.0f, 0.0f, 0.0f, 1.0f,
|
||||
0.5f, 1.0f, 0.0f, 1.0f, 0.0f,
|
||||
};
|
||||
m_vbo->setData(coords, 6*5);
|
||||
}
|
||||
}
|
||||
|
||||
using std::swap;
|
||||
Player::Player(Player &&p) : m_vbo(nullptr) {
|
||||
*this = std::move(p);
|
||||
}
|
||||
|
||||
Player& Player::operator=(Player &&p) {
|
||||
swap(m_vbo, p.m_vbo);
|
||||
swap(team, p.team);
|
||||
swap(playerclass, p.playerclass);
|
||||
swap(tool, p.tool);
|
||||
swap(direction, p.direction);
|
||||
swap(G, p.G);
|
||||
swap(position, p.position);
|
||||
swap(velocity, p.velocity);
|
||||
swap(accel, p.accel);
|
||||
swap(name, p.name);
|
||||
swap(id, p.id);
|
||||
swap(P, p.P);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Player::~Player() {
|
||||
if (GlobalProperties::IsClient) {
|
||||
delete m_vbo; m_vbo = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Player::setPosVel(const glm::vec3 &pos, const glm::vec3 &vel, const glm::vec3 &acc) {
|
||||
position = m_predictPos = pos;
|
||||
velocity = vel;
|
||||
accel = acc;
|
||||
}
|
||||
|
||||
void Player::update(const float &delta) {
|
||||
velocity += accel * delta;
|
||||
m_predictPos += velocity * delta;
|
||||
}
|
||||
|
||||
void Player::render(const glm::mat4 &transform) const {
|
||||
RenderProgram->bind();
|
||||
Textures[(uint8)team][(uint8)tool]->bind();
|
||||
m_vbo->bind();
|
||||
glEnableVertexAttribArray(RenderProgram_attrib_texcoord);
|
||||
glEnableVertexAttribArray(RenderProgram_attrib_coord);
|
||||
glm::vec3 &lpPos = G->LP->position;
|
||||
float angle = atan2(lpPos.x-m_predictPos.x, lpPos.z-m_predictPos.z);
|
||||
glUniformMatrix4fv(RenderProgram_uni_mvp, 1, GL_FALSE, glm::value_ptr(
|
||||
glm::translate(transform, m_predictPos) * glm::rotate(angle, glm::vec3(0.0, 1.0, 0.0))));
|
||||
glVertexAttribPointer(RenderProgram_attrib_coord, 3, GL_FLOAT, GL_FALSE, 5*sizeof(float), 0);
|
||||
glVertexAttribPointer(RenderProgram_attrib_texcoord, 2, GL_FLOAT, GL_FALSE, 5*sizeof(float), (GLvoid*)(3*sizeof(float)));
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
glDisableVertexAttribArray(RenderProgram_attrib_coord);
|
||||
glDisableVertexAttribArray(RenderProgram_attrib_texcoord);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
#ifndef PLAYER_HPP
|
||||
#define PLAYER_HPP
|
||||
#include "Platform.hpp"
|
||||
#include <glm/glm.hpp>
|
||||
#include "network/Network.hpp"
|
||||
#include <GL/glew.h>
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class Program;
|
||||
class VBO;
|
||||
class Game;
|
||||
class Texture;
|
||||
|
||||
class Player {
|
||||
private:
|
||||
static Texture ***Textures;
|
||||
static const Program *RenderProgram;
|
||||
static GLint RenderProgram_attrib_coord, RenderProgram_attrib_texcoord, RenderProgram_uni_mvp;
|
||||
VBO *m_vbo;
|
||||
glm::vec3 m_predictPos;
|
||||
|
||||
Player(const Player&) = delete;
|
||||
Player& operator=(const Player&) = delete;
|
||||
|
||||
public:
|
||||
enum Team : uint8 {
|
||||
None,
|
||||
Red,
|
||||
Blue,
|
||||
LAST
|
||||
} team;
|
||||
enum class Class : uint8 {
|
||||
Prospector,
|
||||
Miner,
|
||||
Engineer,
|
||||
Sapper
|
||||
} playerclass;
|
||||
enum class Tools : uint8 {
|
||||
Pickaxe,
|
||||
ConstructionGun,
|
||||
DeconstructionGun,
|
||||
ProspectingRadar,
|
||||
Detonator,
|
||||
LAST
|
||||
} tool;
|
||||
enum class Direction : uint8 {
|
||||
North, // To +Z
|
||||
East, // To +X
|
||||
South, // To -X
|
||||
West // To -Z
|
||||
} direction;
|
||||
enum class DeathReason : uint8 {
|
||||
Lava,
|
||||
Shock,
|
||||
Fall,
|
||||
Explosion,
|
||||
Void
|
||||
};
|
||||
Game *G;
|
||||
glm::vec3 position, velocity, accel;
|
||||
std::string name;
|
||||
uint32 id;
|
||||
Net::Peer P;
|
||||
|
||||
static const char* getTeamNameLowercase(Team t);
|
||||
static const char* getToolNameLowercase(Tools t);
|
||||
static int getMaxOre(Class c);
|
||||
static int getMaxWeight(Class c);
|
||||
|
||||
Player(Game *G = nullptr);
|
||||
Player(Player&&);
|
||||
Player& operator=(Player&&);
|
||||
~Player();
|
||||
void setPosVel(const glm::vec3 &pos, const glm::vec3 &vel, const glm::vec3 &acc = glm::vec3());
|
||||
void update(const float &delta);
|
||||
void render(const glm::mat4 &transform) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,68 @@
|
|||
#include "PlayerList.hpp"
|
||||
#include <stdexcept>
|
||||
#include "Game.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
PlayerList::PlayerList(Game *G) : G(G) {
|
||||
}
|
||||
|
||||
PlayerList::~PlayerList() {
|
||||
}
|
||||
|
||||
Player& PlayerList::add() {
|
||||
emplace_back(G);
|
||||
return back();
|
||||
}
|
||||
|
||||
void PlayerList::remove(int idx) {
|
||||
erase(begin() + idx);
|
||||
}
|
||||
|
||||
void PlayerList::remove(const Player &plr) {
|
||||
for (auto it = begin();
|
||||
it != end(); ++it) {
|
||||
if (&plr == &*it) {
|
||||
erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Actually bad
|
||||
throw std::out_of_range("Can't remove player: not in list");
|
||||
}
|
||||
|
||||
Player& PlayerList::getById(uint32 id) {
|
||||
if (G->LP && id == G->LP->id)
|
||||
return *G->LP;
|
||||
for (auto it = begin();
|
||||
it != end(); ++it) {
|
||||
if (it->id == id) {
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
throw std::out_of_range("Can't find player by ID");
|
||||
}
|
||||
|
||||
Player &PlayerList::getByName(const std::string &name) {
|
||||
if (G->LP && name == G->LP->name)
|
||||
return *G->LP;
|
||||
for (auto it = begin();
|
||||
it != end(); ++it) {
|
||||
if (it->name.compare(name) == 0) {
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
throw std::out_of_range("Can't find player by name");
|
||||
}
|
||||
|
||||
Player &PlayerList::getByPeer(const Net::Peer &peer) {
|
||||
for (auto it = begin();
|
||||
it != end(); ++it) {
|
||||
if (it->P == peer) {
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
throw std::out_of_range("Can't find player by Net::Peer");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
#ifndef PLAYER_LIST_HPP
|
||||
#define PLAYER_LIST_HPP
|
||||
#include "Platform.hpp"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "Player.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class Game;
|
||||
|
||||
class PlayerList : private std::vector<Player> {
|
||||
Game *G;
|
||||
PlayerList(const PlayerList&) = delete;
|
||||
PlayerList(PlayerList&) = delete;
|
||||
PlayerList& operator=(const PlayerList&) = delete;
|
||||
PlayerList& operator=(PlayerList&) = delete;
|
||||
|
||||
public:
|
||||
PlayerList(Game*);
|
||||
~PlayerList();
|
||||
|
||||
using std::vector<Player>::size;
|
||||
using std::vector<Player>::begin;
|
||||
using std::vector<Player>::end;
|
||||
|
||||
using std::vector<Player>::operator[];
|
||||
|
||||
Player& getById(uint32);
|
||||
Player& getByName(const std::string&);
|
||||
Player& getByPeer(const Net::Peer&);
|
||||
|
||||
Player& add();
|
||||
void remove(const Player&);
|
||||
void remove(int);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,94 @@
|
|||
#include "Program.hpp"
|
||||
#include "Platform.hpp"
|
||||
#include <thread>
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
Program::Program(Shader* vsh, Shader* fsh) : vsh(vsh), fsh(fsh), id(0) {
|
||||
|
||||
}
|
||||
|
||||
Program::Program(const std::string& vshPath, const std::string& fshPath) : id(0) {
|
||||
#if DEBUG
|
||||
this->fshPath = fshPath;
|
||||
this->vshPath = vshPath;
|
||||
#endif
|
||||
vsh = new Shader(Shader::Type::VERTEX, vshPath);
|
||||
fsh = new Shader(Shader::Type::FRAGMENT, fshPath);
|
||||
mustDestroy = true;
|
||||
|
||||
//getDebugStream() << id << ':' << vsh->getError() << fsh->getError()<< std::endl;
|
||||
}
|
||||
|
||||
bool Program::link() {
|
||||
id = glCreateProgram();
|
||||
glAttachShader(id, vsh->getId());
|
||||
glAttachShader(id, fsh->getId());
|
||||
glLinkProgram(id);
|
||||
glGetProgramiv(id, GL_LINK_STATUS, &linked);
|
||||
if (!linked) {
|
||||
getErrorStream() << id << ':' << getError() << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Program::getError() const {
|
||||
GLint log_length = 0;
|
||||
glGetProgramiv(id, GL_INFO_LOG_LENGTH, &log_length);
|
||||
char* log = (char*)malloc(log_length);
|
||||
glGetProgramInfoLog(id, log_length, NULL, log);
|
||||
std::string ret(log);
|
||||
free(log);
|
||||
return ret;
|
||||
}
|
||||
|
||||
GLuint Program::getId() const {
|
||||
return id;
|
||||
}
|
||||
|
||||
GLint Program::att(const std::string &name) const {
|
||||
if (!linked) {
|
||||
getErrorStream() << id << ":Not linked, failed attrib " << name << std::endl;
|
||||
return 0;
|
||||
}
|
||||
GLint loc = glGetAttribLocation(id, name.c_str());
|
||||
if (loc == -1) {
|
||||
#if DEBUG
|
||||
getErrorStream() << vshPath << ":Couldn't bind attrib " << name << std::endl;
|
||||
#else
|
||||
getErrorStream() << id << ":Couldn't bind attrib " << name << std::endl;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
return loc;
|
||||
}
|
||||
|
||||
GLint Program::uni(const std::string &name) const {
|
||||
if (!linked) {
|
||||
getErrorStream() << id << ":Not linked, failed uniform " << name << std::endl;
|
||||
return 0;
|
||||
}
|
||||
GLint loc = glGetUniformLocation(id, name.c_str());
|
||||
if (loc == -1) {
|
||||
#if DEBUG
|
||||
getErrorStream() << vshPath << ":Couldn't bind uniform " << name << std::endl;
|
||||
#else
|
||||
getErrorStream() << id << ":Couldn't bind uniform " << name << std::endl;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
return loc;
|
||||
}
|
||||
|
||||
void Program::bind() const {
|
||||
glUseProgram(id);
|
||||
}
|
||||
|
||||
Program::~Program() {
|
||||
if (mustDestroy) {
|
||||
delete vsh; delete fsh;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
#ifndef PROGRAM_HPP
|
||||
#define PROGRAM_HPP
|
||||
#include "Shader.hpp"
|
||||
|
||||
namespace std {
|
||||
class thread;
|
||||
}
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class Program {
|
||||
private:
|
||||
Shader *vsh, *fsh;
|
||||
GLuint id;
|
||||
GLint linked = GL_FALSE;
|
||||
bool mustDestroy = false;
|
||||
#if DEBUG
|
||||
std::string fshPath, vshPath;
|
||||
#endif
|
||||
|
||||
public:
|
||||
///
|
||||
/// Creates an OpenGL Program from two existent shaders
|
||||
/// (shaders dtors won't be called when ~Program is called)
|
||||
///
|
||||
Program(Shader* vsh, Shader* fsh);
|
||||
|
||||
///
|
||||
/// Creates an OpenGL Program shaders read from paths
|
||||
/// (shaders dtors will be called when ~Program is called)
|
||||
///
|
||||
Program(const std::string& vshPath, const std::string& fshPath);
|
||||
|
||||
///
|
||||
/// Links the shaders together
|
||||
/// @returns true on success, otherwise false
|
||||
/// @see getError() returns the error message
|
||||
///
|
||||
bool link();
|
||||
|
||||
///
|
||||
/// @returns The error message generated during link
|
||||
/// @see link()
|
||||
///
|
||||
std::string getError() const;
|
||||
|
||||
GLuint getId() const;
|
||||
operator GLuint() const { return getId(); }
|
||||
|
||||
///
|
||||
/// @param name Name of the attribute
|
||||
/// @returns OpenGL attribute ID
|
||||
///
|
||||
GLint att(const std::string& name) const;
|
||||
|
||||
///
|
||||
/// @param name Name of the uniform
|
||||
/// @returns OpenGL uniform ID
|
||||
///
|
||||
GLint uni(const std::string& name) const;
|
||||
|
||||
///
|
||||
/// Makes this Program active
|
||||
///
|
||||
void bind() const;
|
||||
|
||||
///
|
||||
/// Destroys the shader, freeing OpenGL resources and the subsequent Shaders (if needed)
|
||||
///
|
||||
~Program();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,63 @@
|
|||
#include "ProgramManager.hpp"
|
||||
#include <sstream>
|
||||
#include "Platform.hpp"
|
||||
|
||||
#define PROGRAM_MANAGER_DEBUG 0
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
std::string ProgramManager::getShadersName(int flags) const {
|
||||
std::ostringstream sstm;
|
||||
if (flags & PM_3D)
|
||||
sstm << "3d";
|
||||
else
|
||||
sstm << "2d";
|
||||
if (flags & PM_TEXTURED)
|
||||
sstm << "Textured";
|
||||
if (flags & PM_TEXSHIFT)
|
||||
sstm << "Texshift";
|
||||
if (flags & PM_COLORED)
|
||||
sstm << "Colored";
|
||||
if (flags & PM_FOG)
|
||||
sstm << "Fog";
|
||||
return sstm.str();
|
||||
}
|
||||
|
||||
ProgramManager::ProgramManager() {
|
||||
|
||||
}
|
||||
|
||||
const Program* ProgramManager::getProgram(int flags) {
|
||||
auto it = m_programs.find(flags);
|
||||
if (it != m_programs.end())
|
||||
return it->second;
|
||||
std::string shaderName = getShadersName(flags);
|
||||
Program* prog = new Program(getAssetPath(shaderName + ".v.glsl"), getAssetPath(shaderName + ".f.glsl"));
|
||||
prog->link();
|
||||
m_programs.insert(std::pair<int, Program*>(flags, prog));
|
||||
#if PROGRAM_MANAGER_DEBUG
|
||||
getDebugStream() << "Added " << shaderName << ':' << prog->getId() << std::endl;
|
||||
#endif
|
||||
return prog;
|
||||
}
|
||||
|
||||
const Program *ProgramManager::getSpecialProgram(const std::string &name) {
|
||||
Program* prog = new Program(getAssetPath(name + ".v.glsl"), getAssetPath(name + ".f.glsl"));
|
||||
prog->link();
|
||||
m_specialPrograms.push_back(prog);
|
||||
#if PROGRAM_MANAGER_DEBUG
|
||||
getDebugStream() << "AddSpecial " << name << ':' << prog->getId() << std::endl;
|
||||
#endif
|
||||
return prog;
|
||||
}
|
||||
|
||||
ProgramManager::~ProgramManager() {
|
||||
for (const std::pair<const int, Program*> pair : m_programs) {
|
||||
delete pair.second;
|
||||
}
|
||||
for (const Program* prog : m_specialPrograms) {
|
||||
delete prog;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef PROGRAM_MANAGER_HPP
|
||||
#define PROGRAM_MANAGER_HPP
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "Program.hpp"
|
||||
|
||||
#define PM_2D 0
|
||||
#define PM_3D 1
|
||||
#define PM_TEXTURED 2
|
||||
#define PM_COLORED 4
|
||||
#define PM_FOG 8
|
||||
#define PM_TEXSHIFT 16
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class ProgramManager {
|
||||
private:
|
||||
std::unordered_map<int, Program*> m_programs;
|
||||
std::vector<Program*> m_specialPrograms;
|
||||
std::string getShadersName(int flags) const;
|
||||
|
||||
public:
|
||||
ProgramManager();
|
||||
~ProgramManager();
|
||||
const Program* getProgram(int flags);
|
||||
const Program* getSpecialProgram(const std::string &name);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,330 @@
|
|||
#include "Server.hpp"
|
||||
#include "Game.hpp"
|
||||
#include "network/Network.hpp"
|
||||
#include "network/NetHelper.hpp"
|
||||
#include "VersionInfo.hpp"
|
||||
#include "CaveGenerator.hpp"
|
||||
#include "ChunkChangeHelper.hpp"
|
||||
#include <iterator>
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
#include <sstream>
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
using namespace Diggler::Net;
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
Player* Server::getPlayerByPeer(const Peer &peer) {
|
||||
try {
|
||||
return &G->players.getByPeer(peer);
|
||||
} catch (const std::out_of_range &e) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
Player* Server::getPlayerById(uint32 id) {
|
||||
try {
|
||||
return &G->players.getById(id);
|
||||
} catch (const std::out_of_range &e) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
Player *Server::getPlayerByName(const std::string &name) {
|
||||
try {
|
||||
return &G->players.getByName(name);
|
||||
} catch (const std::out_of_range &e) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Server::handlePlayerJoin(InMessage &msg, Peer &peer) {
|
||||
std::string name = msg.readString();
|
||||
getOutputStream() << peer.getHost() << " is joining as " << name << std::endl;
|
||||
|
||||
// TODO: ban list
|
||||
Player *possible = getPlayerByName(name);
|
||||
if (possible != nullptr) {
|
||||
// TODO: use kick() method
|
||||
OutMessage kick(MessageType::PlayerQuit, QuitReason::UsernameAlreadyUsed);
|
||||
kick.writeString("You are \faalready\f0 playing on this server");
|
||||
H.send(peer, kick, Tfer::Rel);
|
||||
getOutputStream() << peer.getHost() << " tried to connect as " << name << ": name is taken" << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
Player &plr = G->players.add();
|
||||
plr.name = name;
|
||||
plr.id = FastRand();
|
||||
plr.P = peer;
|
||||
|
||||
// Confirm successful join
|
||||
OutMessage join(MessageType::PlayerJoin);
|
||||
join.writeU32(plr.id);
|
||||
H.send(peer, join, Tfer::Rel);
|
||||
|
||||
// Send the player list
|
||||
for (Player &p : G->players) {
|
||||
if (p.id == plr.id)
|
||||
continue; // ok, he knows he's here
|
||||
OutMessage playerMsg(MessageType::PlayerJoin);
|
||||
playerMsg.writeU32(p.id);
|
||||
playerMsg.writeString(p.name);
|
||||
H.send(peer, playerMsg, Tfer::Rel);
|
||||
}
|
||||
|
||||
OutMessage map(MessageType::MapTransfer);
|
||||
G->SC->writeMsg(map);
|
||||
H.send(peer, map, Tfer::Rel);
|
||||
|
||||
// Broadcast player's join
|
||||
OutMessage broadcast(MessageType::PlayerJoin);
|
||||
broadcast.writeU32(plr.id);
|
||||
broadcast.writeString(plr.name);
|
||||
for (Player &p : G->players) {
|
||||
if (p.id == plr.id)
|
||||
continue; // dont send broadcast to the player
|
||||
H.send(p.P, broadcast, Tfer::Rel);
|
||||
}
|
||||
getOutputStream() << plr.name << " joined from " << peer.getHost() << endl;
|
||||
}
|
||||
|
||||
void Server::handlePlayerQuit(Peer &peer, QuitReason reason) {
|
||||
Player *plrPtr = getPlayerByPeer(peer);
|
||||
if (plrPtr) {
|
||||
Player &plr = *plrPtr;
|
||||
// Broadcast disconnection
|
||||
OutMessage broadcast(MessageType::PlayerQuit, reason);
|
||||
broadcast.writeU32(plr.id);
|
||||
for (Player &p : G->players) {
|
||||
if (p.id == plr.id)
|
||||
continue; // dont send broadcast to the player
|
||||
H.send(p.P, broadcast, Tfer::Rel);
|
||||
}
|
||||
getOutputStream() << plr.name << " disconnected" << endl;
|
||||
G->players.remove(plr);
|
||||
} else {
|
||||
getOutputStream() << peer.getHost() << " disconnected" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void Server::handleDisconnect(Peer &peer) {
|
||||
handlePlayerQuit(peer, QuitReason::Timeout);
|
||||
}
|
||||
|
||||
void Server::handleEvent(InMessage &msg, Peer &peer) {
|
||||
Player &plr = G->players.getByPeer(peer);
|
||||
switch (msg.getSubtype()) {
|
||||
case Net::EventType::PlayerJumpOnPad: {
|
||||
OutMessage out;
|
||||
NetHelper::MakeEvent(out, (Net::EventType)msg.getSubtype(), plr);
|
||||
NetHelper::Broadcast(G, out);
|
||||
} break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Server::handleChat(InMessage &msg, Peer &peer) {
|
||||
try {
|
||||
// TODO: implement codecvt_utf8<utf32> when libstdc++ supports it
|
||||
Player &plr = G->players.getByPeer(peer);
|
||||
std::string chatMsg = msg.readString();
|
||||
getOutputStream() << plr.name << ": " << chatMsg << endl;
|
||||
std::ostringstream contentFormatter;
|
||||
contentFormatter << plr.name << "> " << chatMsg;
|
||||
std::string content = contentFormatter.str();
|
||||
OutMessage newMsg(MessageType::Chat);
|
||||
newMsg.writeString(content);
|
||||
NetHelper::Broadcast(G, newMsg, Tfer::Rel);
|
||||
} catch (const std::out_of_range &e) {
|
||||
getErrorStream() << peer.getHost() << " sent chat message but is not connected" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void Server::handlePlayerUpdate(InMessage &msg, Peer &peer) {
|
||||
try {
|
||||
Player &plr = G->players.getByPeer(peer);
|
||||
|
||||
switch (msg.getSubtype()) {
|
||||
case PlayerUpdateType::Move: {
|
||||
// Broadcast movement
|
||||
OutMessage bcast(MessageType::PlayerUpdate, PlayerUpdateType::Move);
|
||||
bcast.writeU32(plr.id);
|
||||
glm::vec3 pos = msg.readVec3(),
|
||||
vel = msg.readVec3(),
|
||||
acc = msg.readVec3();
|
||||
bcast.writeVec3(pos);
|
||||
bcast.writeVec3(vel);
|
||||
bcast.writeVec3(acc);
|
||||
for (Player &p : G->players) {
|
||||
if (p.id == plr.id)
|
||||
continue; // dont send broadcast to the player
|
||||
// TODO: confirm position to player
|
||||
H.send(p.P, bcast, Tfer::Unrel);
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (std::out_of_range &e) {
|
||||
// TODO: log?
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Server::Server(Game *G) : G(G) {
|
||||
getOutputStream() << "Diggler v" << VersionString << " Server, port " << G->Port << ", "
|
||||
<< std::thread::hardware_concurrency() << " HW threads supported" << endl;
|
||||
|
||||
try {
|
||||
H.create(G->Port);
|
||||
} catch (Net::Exception &e) {
|
||||
getErrorStream() << "Couldn't open port " << G->Port << " for listening" << endl <<
|
||||
"Make sure no other server instance is running" << endl;
|
||||
throw "Server init failed";
|
||||
}
|
||||
|
||||
#if 1
|
||||
G->SC->setSize(4, 4, 4);
|
||||
|
||||
//for (int i=0; i < 8192; i++) G->SC->set(FastRand(CX*G->SC->getChunksX()), FastRand(CY*G->SC->getChunksY()), FastRand(CZ*G->SC->getChunksZ()), (BlockType)(FastRand((int)BlockType::LAST)));
|
||||
for(int x=0;x<CX*G->SC->getChunksX();x++) for(int z=0;z<(CZ*G->SC->getChunksZ())/2;z++) G->SC->set(x, 0, z, BlockType::Dirt);
|
||||
for(int x=0;x<CX*G->SC->getChunksX();x++) for(int z=0;z<(CZ*G->SC->getChunksZ())/2;z++) G->SC->set(x, 0, z+(CZ*G->SC->getChunksZ())/2, BlockType::Road);
|
||||
// for(int x=0;x<CX*G->SC->getChunksX();x++) for(int y=0;y<16;y++) for(int z=0;z<CZ*G->SC->getChunksZ();z++) G->SC->set(x,y,z,BlockType::Dirt);
|
||||
for(int x=0; x < (int)BlockType::LAST; x++) G->SC->set(x, 2, 0, (BlockType)(x));
|
||||
G->SC->set(4, 4, 4, BlockType::Shock);
|
||||
G->SC->set(4, 0, 4, BlockType::Jump);
|
||||
|
||||
G->SC->set(0, 1, 1, BlockType::Metal);
|
||||
G->SC->set(0, 2, 1, BlockType::Metal);
|
||||
G->SC->set(0, 3, 1, BlockType::Metal);
|
||||
|
||||
G->SC->set(1, 3, 1, BlockType::Metal);
|
||||
G->SC->set(2, 3, 1, BlockType::Metal);
|
||||
|
||||
G->SC->set(3, 1, 1, BlockType::Metal);
|
||||
G->SC->set(3, 2, 1, BlockType::Metal);
|
||||
G->SC->set(3, 3, 1, BlockType::Metal);
|
||||
|
||||
CaveGenerator::PaintAtPoint(*(G->SC), 8, 8, 8, 1, BlockType::Dirt);
|
||||
CaveGenerator::PaintAtPoint(*(G->SC), 16, 8, 8, 2, BlockType::Dirt);
|
||||
CaveGenerator::PaintAtPoint(*(G->SC), 24, 8, 8, 3, BlockType::Dirt);
|
||||
|
||||
for(int x=0;x<CX*G->SC->getChunksX();x++) for(int z=0;z<(CZ*G->SC->getChunksZ())/2;z++) G->SC->set(x, 64, z, BlockType::Dirt);
|
||||
G->SC->set(2*CX, 68, 2*CY, BlockType::Lava);
|
||||
#else
|
||||
G->SC->setSize(4, 4, 4);
|
||||
CaveGenerator::GenerateCaveSystem(*(G->SC), true, 15);
|
||||
#endif
|
||||
|
||||
//G->SC->save("/tmp/a");
|
||||
//G->SC->load("/tmp/a");
|
||||
|
||||
/*{
|
||||
Game *G = this->G;
|
||||
std::thread make([G]{CaveGenerator::GenerateCaveSystem(*(G->SC), true, 15);});
|
||||
make.detach();
|
||||
}*/
|
||||
}
|
||||
|
||||
void chunk_updater(Game *G, Superchunk *sc, Host &H) {
|
||||
while (true) {
|
||||
for (int x=0; x < CX; x++)
|
||||
for (int y=0; y < CY; y++)
|
||||
for (int z=0; z < CZ; z++) {
|
||||
Chunk* c = sc->getChunk(x, y, z);
|
||||
if (c)
|
||||
c->updateServerPrepare();
|
||||
}
|
||||
for (int x=0; x < CX; x++)
|
||||
for (int y=0; y < CY; y++)
|
||||
for (int z=0; z < CZ; z++) {
|
||||
Chunk* c = sc->getChunk(x, y, z);
|
||||
if (c)
|
||||
c->updateServer();
|
||||
}
|
||||
for (int x=0; x < CX; x++)
|
||||
for (int y=0; y < CY; y++)
|
||||
for (int z=0; z < CZ; z++) {
|
||||
Chunk* c = sc->getChunk(x, y, z);
|
||||
if (c)
|
||||
c->updateServerSwap();
|
||||
}
|
||||
if (!G->CCH->empty()) {
|
||||
OutMessage msg(MessageType::MapUpdate, G->CCH->count());
|
||||
// Message subtype = update count, trickery ;)
|
||||
G->CCH->flush(msg);
|
||||
for (Player &p : G->players) {
|
||||
H.send(p.P, msg, Tfer::Rel);
|
||||
}
|
||||
}
|
||||
OutMessage msg(MessageType::Event, EventType::ExplosivesBlow);
|
||||
msg.writeVec3(glm::vec3(0.f, 0.f, 0.f));
|
||||
for (Player &p : G->players) {
|
||||
H.send(p.P, msg, Tfer::Rel);
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
}
|
||||
}
|
||||
|
||||
void Server::run() {
|
||||
InMessage msg;
|
||||
Peer peer;
|
||||
std::thread upd(chunk_updater, G, G->SC.get(), std::ref(H));
|
||||
while (true) {
|
||||
if (H.recv(msg, peer, 100)) {
|
||||
switch (msg.getType()) {
|
||||
case MessageType::Connect:
|
||||
getOutputStream() << peer.getHost() << " NEWCONN" << std::endl;
|
||||
break;
|
||||
case MessageType::Disconnect:
|
||||
handleDisconnect(peer);
|
||||
break;
|
||||
|
||||
case MessageType::PlayerJoin:
|
||||
handlePlayerJoin(msg, peer);
|
||||
break;
|
||||
case MessageType::PlayerQuit:
|
||||
handlePlayerQuit(peer);
|
||||
break;
|
||||
|
||||
case MessageType::Chat:
|
||||
handleChat(msg, peer);
|
||||
break;
|
||||
case MessageType::PlayerUpdate:
|
||||
handlePlayerUpdate(msg, peer);
|
||||
break;
|
||||
case MessageType::Event:
|
||||
handleEvent(msg, peer);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Server::isPlayerOnline(const std::string &playername) const {
|
||||
for (const Player &p : G->players) {
|
||||
if (p.name == playername)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Server::kick(Player& p, Net::QuitReason r, const std::string& message) {
|
||||
OutMessage msg(MessageType::PlayerQuit, r);
|
||||
msg.writeU32(p.id);
|
||||
msg.writeString(message);
|
||||
H.send(p.P, msg, Tfer::Rel);
|
||||
p.P.disconnect();
|
||||
}
|
||||
|
||||
Server::~Server() {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef SERVER_HPP
|
||||
#define SERVER_HPP
|
||||
#include "network/Network.hpp"
|
||||
#include "Player.hpp"
|
||||
#include <memory>
|
||||
|
||||
using std::unique_ptr;
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class Game;
|
||||
|
||||
class Server {
|
||||
private:
|
||||
Game *G;
|
||||
|
||||
void handlePlayerJoin(Net::InMessage&, Net::Peer&);
|
||||
void handlePlayerQuit(Net::Peer&, Net::QuitReason reason = Net::QuitReason::Quit);
|
||||
void handleDisconnect(Net::Peer&);
|
||||
|
||||
void handleEvent(Net::InMessage&, Net::Peer&);
|
||||
void handleChat(Net::InMessage&, Net::Peer&);
|
||||
void handlePlayerUpdate(Net::InMessage&, Net::Peer&);
|
||||
|
||||
public:
|
||||
Net::Host H;
|
||||
|
||||
Server(Game *G);
|
||||
~Server();
|
||||
|
||||
void run();
|
||||
bool isPlayerOnline(const std::string &playername) const;
|
||||
bool isIPOnline(const std::string &ip) const;
|
||||
Player* getPlayerById(uint32 id);
|
||||
Player* getPlayerByPeer(const Net::Peer &peer);
|
||||
Player* getPlayerByName(const std::string &name);
|
||||
void kick(Player &p, Net::QuitReason r, const std::string& message = "");
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,49 @@
|
|||
#include "Shader.hpp"
|
||||
#include "Platform.hpp"
|
||||
|
||||
Diggler::Shader::Shader(Type type) : type(type) {
|
||||
id = glCreateShader((GLenum)type);
|
||||
}
|
||||
|
||||
Diggler::Shader::Shader(Diggler::Shader::Type type, const std::string& path) {
|
||||
id = glCreateShader((GLenum)type);
|
||||
compileFromFile(path);
|
||||
}
|
||||
|
||||
bool Diggler::Shader::compileFromFile(const std::string& path) {
|
||||
return compileFromString(fs::readFile(path));
|
||||
}
|
||||
|
||||
bool Diggler::Shader::compileFromString(const std::string& source) {
|
||||
if (source.size() == 0)
|
||||
return false;
|
||||
const char *src = source.c_str();
|
||||
glShaderSource(id, 1, &src, nullptr);
|
||||
glCompileShader(id);
|
||||
glGetShaderiv(id, GL_COMPILE_STATUS, &compiled);
|
||||
if (!compiled) {
|
||||
getErrorStream() << getError() << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Diggler::Shader::getError() const {
|
||||
GLint log_length = 0;
|
||||
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &log_length);
|
||||
if (log_length < 1)
|
||||
return "[empty error string]";
|
||||
char* log = (char*)malloc(log_length);
|
||||
glGetShaderInfoLog(id, log_length, NULL, log);
|
||||
std::string ret(log);
|
||||
free(log);
|
||||
return ret;
|
||||
}
|
||||
|
||||
GLuint Diggler::Shader::getId() const {
|
||||
return id;
|
||||
}
|
||||
|
||||
Diggler::Shader::~Shader() {
|
||||
glDeleteShader(id);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef SHADER_HPP
|
||||
#define SHADER_HPP
|
||||
#include <string>
|
||||
#include <GL/glew.h>
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class Shader {
|
||||
private:
|
||||
GLuint id;
|
||||
GLint compiled = GL_FALSE;
|
||||
|
||||
public:
|
||||
enum class Type : GLenum {
|
||||
FRAGMENT = GL_FRAGMENT_SHADER,
|
||||
VERTEX = GL_VERTEX_SHADER
|
||||
} type;
|
||||
Shader(Type type);
|
||||
Shader(Type type, const std::string& path);
|
||||
bool compileFromFile(const std::string& path);
|
||||
bool compileFromString(const std::string& source);
|
||||
std::string getError() const;
|
||||
GLuint getId() const;
|
||||
operator GLuint() const { return getId(); }
|
||||
~Shader();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,106 @@
|
|||
#include "Skybox.hpp"
|
||||
#include "Texture.hpp"
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
const Program *Skybox::RenderProgram = nullptr;
|
||||
GLint Skybox::RenderProgram_attrib_texcoord = -1;
|
||||
GLint Skybox::RenderProgram_attrib_coord = -1;
|
||||
GLint Skybox::RenderProgram_uni_mvp = -1;
|
||||
|
||||
Skybox::Skybox(Game *G, const std::string &skyName) : G(G) {
|
||||
if (RenderProgram == nullptr) {
|
||||
RenderProgram = G->PM->getProgram(PM_3D | PM_TEXTURED);
|
||||
RenderProgram_attrib_coord = RenderProgram->att("coord");
|
||||
RenderProgram_attrib_texcoord = RenderProgram->att("texcoord");
|
||||
RenderProgram_uni_mvp = RenderProgram->uni("mvp");
|
||||
}
|
||||
m_top = new Texture(skyName + "_up.png");
|
||||
m_bottom = new Texture(skyName + "_down.png");
|
||||
m_n = new Texture(skyName + "_north.png");
|
||||
m_n->setFiltering(Texture::Filter::Linear, Texture::Filter::Linear);
|
||||
m_e = new Texture(skyName + "_east.png");
|
||||
m_e->setFiltering(Texture::Filter::Linear, Texture::Filter::Linear);
|
||||
m_w = new Texture(skyName + "_west.png");
|
||||
m_w->setFiltering(Texture::Filter::Linear, Texture::Filter::Linear);
|
||||
m_s = new Texture(skyName + "_south.png");
|
||||
m_s->setFiltering(Texture::Filter::Linear, Texture::Filter::Linear);
|
||||
Coord coords[6*6] = {
|
||||
// Top
|
||||
{ -1, 1, -1, 0, 0 },
|
||||
{ 1, 1, -1, 1, 0 },
|
||||
{ -1, 1, 1, 0, 1 },
|
||||
{ -1, 1, 1, 0, 1 },
|
||||
{ 1, 1, -1, 1, 0 },
|
||||
{ 1, 1, 1, 1, 1 },
|
||||
// Bottom
|
||||
{ -1, -1, 1, 0, 0 },
|
||||
{ 1, -1, -1, 1, 1 },
|
||||
{ -1, -1, -1, 0, 1 },
|
||||
{ 1, -1, 1, 1, 0 },
|
||||
{ 1, -1, -1, 1, 1 },
|
||||
{ -1, -1, 1, 0, 0 },
|
||||
// East
|
||||
{ -1, -1, -1, 0, 1 },
|
||||
{ -1, 1, -1, 0, 0 },
|
||||
{ -1, -1, 1, 1, 1 },
|
||||
{ -1, 1, 1, 1, 0 },
|
||||
{ -1, -1, 1, 1, 1 },
|
||||
{ -1, 1, -1, 0, 0 },
|
||||
// West
|
||||
{ 1, 1, -1, 1, 0 },
|
||||
{ 1, -1, -1, 1, 1 },
|
||||
{ 1, -1, 1, 0, 1 },
|
||||
{ 1, -1, 1, 0, 1 },
|
||||
{ 1, 1, 1, 0, 0 },
|
||||
{ 1, 1, -1, 1, 0 },
|
||||
// North
|
||||
{ -1, 1, -1, 1, 0 },
|
||||
{ -1, -1, -1, 1, 1 },
|
||||
{ 1, -1, -1, 0, 1 },
|
||||
{ 1, -1, -1, 0, 1 },
|
||||
{ 1, 1, -1, 0, 0 },
|
||||
{ -1, 1, -1, 1, 0 },
|
||||
// South
|
||||
{ -1, -1, 1, 0, 1 },
|
||||
{ -1, 1, 1, 0, 0 },
|
||||
{ 1, -1, 1, 1, 1 },
|
||||
{ 1, 1, 1, 1, 0 },
|
||||
{ 1, -1, 1, 1, 1 },
|
||||
{ -1, 1, 1, 0, 0 },
|
||||
};
|
||||
m_vbo.setData(coords, 6*6);
|
||||
}
|
||||
|
||||
void Skybox::render(const glm::mat4 &transform) const {
|
||||
RenderProgram->bind();
|
||||
m_vbo.bind();
|
||||
glEnableVertexAttribArray(RenderProgram_attrib_coord);
|
||||
glEnableVertexAttribArray(RenderProgram_attrib_texcoord);
|
||||
glUniformMatrix4fv(RenderProgram_uni_mvp, 1, GL_FALSE, glm::value_ptr(transform));
|
||||
glVertexAttribPointer(RenderProgram_attrib_coord, 3, GL_BYTE, GL_FALSE, sizeof(Coord), 0);
|
||||
glVertexAttribPointer(RenderProgram_attrib_texcoord, 2, GL_BYTE, GL_FALSE, sizeof(Coord), (GLvoid*)offsetof(Coord, u));
|
||||
|
||||
m_top->bind();
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
m_bottom->bind();
|
||||
glDrawArrays(GL_TRIANGLES, 6, 6);
|
||||
m_e->bind();
|
||||
glDrawArrays(GL_TRIANGLES, 12, 6);
|
||||
m_w->bind();
|
||||
glDrawArrays(GL_TRIANGLES, 18, 6);
|
||||
m_n->bind();
|
||||
glDrawArrays(GL_TRIANGLES, 24, 6);
|
||||
m_s->bind();
|
||||
glDrawArrays(GL_TRIANGLES, 30, 6);
|
||||
|
||||
glDisableVertexAttribArray(RenderProgram_attrib_texcoord);
|
||||
glDisableVertexAttribArray(RenderProgram_attrib_coord);
|
||||
}
|
||||
|
||||
Skybox::~Skybox() {
|
||||
delete m_top;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef SKYBOX_HPP
|
||||
#define SKYBOX_HPP
|
||||
#include <glm/detail/type_mat.hpp>
|
||||
#include "VBO.hpp"
|
||||
#include "Game.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class Program;
|
||||
class Texture;
|
||||
|
||||
class Skybox {
|
||||
private:
|
||||
static const Program *RenderProgram;
|
||||
static GLint RenderProgram_attrib_coord, RenderProgram_attrib_texcoord, RenderProgram_uni_mvp;
|
||||
VBO m_vbo;
|
||||
Texture *m_top, *m_w, *m_e, *m_n, *m_s, *m_bottom;
|
||||
Game *G;
|
||||
struct Coord { int8 x, y, z, u, v; };
|
||||
|
||||
public:
|
||||
Skybox(Game *G, const std::string &skyName);
|
||||
~Skybox();
|
||||
void render(const glm::mat4 &transform) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,137 @@
|
|||
#include "Sound.hpp"
|
||||
#include "Platform.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
Sound::Sound(const SoundBuffer *buffer) : buffer(buffer), createdRelative(true) {
|
||||
alGenSources(1, &id);
|
||||
alSourcei(id, AL_BUFFER, buffer->getId());
|
||||
setRelative(true);
|
||||
setPosition(glm::vec3(0, 0, 0));
|
||||
}
|
||||
|
||||
Sound::Sound(const SoundBuffer *buffer, bool relative, const glm::vec3 &pos)
|
||||
: buffer(buffer), createdRelative(false) {
|
||||
alGenSources(1, &id);
|
||||
alSourcei(id, AL_BUFFER, buffer->getId());
|
||||
setRelative(relative);
|
||||
setPosition(pos);
|
||||
}
|
||||
|
||||
Sound::Sound(const Sound &s) {
|
||||
alGenSources(1, &id);
|
||||
buffer = s.buffer;
|
||||
createdRelative = s.createdRelative;
|
||||
alSourcei(id, AL_BUFFER, buffer->getId());
|
||||
setRelative(s.getRelative());
|
||||
setPosition(s.getPosition());
|
||||
setPitch(s.getPitch());
|
||||
setLooping(s.getLooping());
|
||||
setVelocity(s.getVelocity());
|
||||
setDirection(s.getDirection());
|
||||
setGain(s.getGain());
|
||||
}
|
||||
|
||||
Sound::Sound(Sound&& s) {
|
||||
id = s.id;
|
||||
buffer = s.buffer;
|
||||
s.buffer = nullptr;
|
||||
createdRelative = s.createdRelative;
|
||||
}
|
||||
|
||||
Sound::~Sound() {
|
||||
if (buffer == nullptr) // Got moved
|
||||
return;
|
||||
alDeleteSources(1, &id);
|
||||
}
|
||||
|
||||
void Sound::play() {
|
||||
alSourcePlay(id);
|
||||
}
|
||||
|
||||
void Sound::stop() {
|
||||
alSourceStop(id);
|
||||
alSourceRewind(id);
|
||||
}
|
||||
|
||||
bool Sound::isPlaying() const {
|
||||
ALint status;
|
||||
alGetSourcei(id, AL_SOURCE_STATE, &status);
|
||||
return status == AL_PLAYING;
|
||||
}
|
||||
|
||||
float Sound::getGain() const {
|
||||
ALfloat value = 1.f;
|
||||
alGetSourcef(id, AL_GAIN, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
void Sound::setGain(float value) {
|
||||
alSourcef(id, AL_GAIN, value);
|
||||
}
|
||||
|
||||
bool Sound::getRelative() const {
|
||||
ALint value = AL_FALSE;
|
||||
alGetSourcei(id, AL_SOURCE_RELATIVE, &value);
|
||||
return value == AL_TRUE ? true : false;
|
||||
}
|
||||
|
||||
void Sound::setRelative(bool value) {
|
||||
createdRelative = false;
|
||||
alSourcei(id, AL_SOURCE_RELATIVE, value ? AL_TRUE : AL_FALSE);
|
||||
}
|
||||
|
||||
float Sound::getPitch() const {
|
||||
ALfloat value = 1.f;
|
||||
alGetSourcef(id, AL_PITCH, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
void Sound::setPitch(float value) {
|
||||
alSourcef(id, AL_PITCH, value);
|
||||
}
|
||||
|
||||
bool Sound::getLooping() const {
|
||||
ALint value = AL_FALSE;
|
||||
alGetSourcei(id, AL_LOOPING, &value);
|
||||
return value == AL_TRUE ? true : false;
|
||||
}
|
||||
|
||||
void Sound::setLooping(bool value) {
|
||||
alSourcei(id, AL_SOURCE_RELATIVE, value ? AL_TRUE : AL_FALSE);
|
||||
}
|
||||
|
||||
glm::vec3 Sound::getDirection() const {
|
||||
ALfloat x = 0.f, y = 0.f, z = 0.f;
|
||||
alGetSource3f(id, AL_DIRECTION, &x, &y, &z);
|
||||
return glm::vec3(x, y, z);
|
||||
}
|
||||
|
||||
void Sound::setDirection(const glm::vec3 &value) {
|
||||
if (createdRelative) setRelative(createdRelative = false);
|
||||
alSource3f(id, AL_DIRECTION, value.x, value.y, value.z);
|
||||
}
|
||||
|
||||
glm::vec3 Sound::getPosition() const {
|
||||
ALfloat x = 0.f, y = 0.f, z = 0.f;
|
||||
alGetSource3f(id, AL_POSITION, &x, &y, &z);
|
||||
return glm::vec3(x, y, z);
|
||||
}
|
||||
|
||||
void Sound::setPosition(const glm::vec3 &value) {
|
||||
if (createdRelative) setRelative(createdRelative = false);
|
||||
alSource3f(id, AL_POSITION, value.x, value.y, value.z);
|
||||
}
|
||||
|
||||
glm::vec3 Sound::getVelocity() const {
|
||||
ALfloat x = 0.f, y = 0.f, z = 0.f;
|
||||
alGetSource3f(id, AL_VELOCITY, &x, &y, &z);
|
||||
return glm::vec3(x, y, z);
|
||||
}
|
||||
|
||||
void Sound::setVelocity(const glm::vec3 &value) {
|
||||
if (createdRelative) setRelative(createdRelative = false);
|
||||
alSource3f(id, AL_VELOCITY, value.x, value.y, value.z);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
#ifndef SOUND_HPP
|
||||
#define SOUND_HPP
|
||||
#include <string>
|
||||
#include "SoundBuffer.hpp"
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class Sound {
|
||||
private:
|
||||
const SoundBuffer *buffer;
|
||||
ALuint id; bool createdRelative;
|
||||
|
||||
public:
|
||||
Sound(const SoundBuffer *buffer);
|
||||
Sound(const SoundBuffer *buffer, bool relative, const glm::vec3 &pos = glm::vec3());
|
||||
// Copy
|
||||
Sound(const Sound&);
|
||||
// Move
|
||||
Sound(Sound&&);
|
||||
~Sound();
|
||||
void play();
|
||||
void stop();
|
||||
bool isPlaying() const;
|
||||
inline ALuint getId() const { return id; }
|
||||
|
||||
float getGain() const;
|
||||
void setGain(float value);
|
||||
|
||||
bool getLooping() const;
|
||||
void setLooping(bool value);
|
||||
|
||||
float getPitch() const;
|
||||
void setPitch(float value);
|
||||
|
||||
bool getRelative() const;
|
||||
void setRelative(bool value);
|
||||
|
||||
glm::vec3 getPosition() const;
|
||||
void setPosition(const glm::vec3 &value);
|
||||
|
||||
glm::vec3 getVelocity() const;
|
||||
void setVelocity(const glm::vec3 &value);
|
||||
|
||||
glm::vec3 getDirection() const;
|
||||
void setDirection(const glm::vec3 &value);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,87 @@
|
|||
#include "SoundBuffer.hpp"
|
||||
#include "Platform.hpp"
|
||||
#include "stb_vorbis.h"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
inline ALenum alGetFormat(short channels, short samples) {
|
||||
bool stereo = (channels > 1);
|
||||
switch (samples) {
|
||||
case 16:
|
||||
if (stereo)
|
||||
return AL_FORMAT_STEREO16;
|
||||
else
|
||||
return AL_FORMAT_MONO16;
|
||||
case 8:
|
||||
if (stereo)
|
||||
return AL_FORMAT_STEREO8;
|
||||
else
|
||||
return AL_FORMAT_MONO8;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
const char* alGetErrorString(ALenum error) {
|
||||
switch (error) {
|
||||
case AL_NO_ERROR:
|
||||
return "AL_NO_ERROR";
|
||||
case AL_INVALID_NAME:
|
||||
return "AL_INVALID_NAME";
|
||||
case AL_INVALID_ENUM:
|
||||
return "AL_INVALID_ENUM";
|
||||
case AL_INVALID_VALUE:
|
||||
return "AL_INVALID_VALUE";
|
||||
case AL_INVALID_OPERATION:
|
||||
return "AL_INVALID_OPERATION";
|
||||
case AL_OUT_OF_MEMORY:
|
||||
return "AL_OUT_OF_MEMORY";
|
||||
default:
|
||||
return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
SoundBuffer::SoundBuffer() : moved(false) {
|
||||
alGenBuffers(1, &id);
|
||||
}
|
||||
|
||||
SoundBuffer::SoundBuffer(SoundBuffer &&b) {
|
||||
id = b.id;
|
||||
b.moved = true;
|
||||
}
|
||||
|
||||
SoundBuffer::~SoundBuffer() {
|
||||
if (moved)
|
||||
return;
|
||||
alDeleteBuffers(1, &id);
|
||||
}
|
||||
|
||||
void SoundBuffer::loadOgg(const std::string &path) {
|
||||
int error = 0;
|
||||
stb_vorbis* stream = stb_vorbis_open_filename(const_cast<char*>(path.c_str()), &error, nullptr);
|
||||
if (stream == nullptr) {
|
||||
getDebugStream() << "Could not load " << path << " : " << error << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get file info
|
||||
stb_vorbis_info info = stb_vorbis_get_info(stream);
|
||||
ALenum format = alGetFormat(info.channels, 16); // stb_vorbis always 16-bit samples
|
||||
uint bufferSize = stb_vorbis_stream_length_in_samples(stream); //4096*8;
|
||||
|
||||
// Create buffer
|
||||
ALshort *bufferData = new ALshort[bufferSize];
|
||||
|
||||
// Fill the buffer
|
||||
stb_vorbis_get_samples_short_interleaved(stream, info.channels, bufferData, bufferSize);
|
||||
|
||||
// Send the buffer data
|
||||
alBufferData(id, format, bufferData, stb_vorbis_stream_length_in_samples(stream)*sizeof(ALshort), info.sample_rate);
|
||||
//getDebugStream() << path << ' ' << info.sample_rate << "Hz" << std::endl;
|
||||
|
||||
// avoid memory leaks: delete the buffer and stb_vorbis instance
|
||||
delete[] bufferData;
|
||||
stb_vorbis_close(stream);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef SOUND_BUFFER_HPP
|
||||
#define SOUND_BUFFER_HPP
|
||||
#include <AL/al.h>
|
||||
#include <string>
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class SoundBuffer {
|
||||
private:
|
||||
bool moved;
|
||||
ALuint id;
|
||||
|
||||
public:
|
||||
SoundBuffer();
|
||||
// No copy
|
||||
SoundBuffer(const SoundBuffer&) = delete;
|
||||
// Move
|
||||
SoundBuffer(SoundBuffer&&);
|
||||
~SoundBuffer();
|
||||
|
||||
void loadOgg(const std::string &path);
|
||||
|
||||
operator ALint() const { return id; }
|
||||
ALint getId() const { return id; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef STATE_HPP
|
||||
#define STATE_HPP
|
||||
#include "Platform.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class State {
|
||||
public:
|
||||
virtual void onMouseButton(int key, int action, int mods) {}
|
||||
virtual void onCursorPos(double x, double y) {}
|
||||
virtual void onMouseScroll(double x, double y) {}
|
||||
virtual void onKey(int key, int scancode, int action, int mods) {}
|
||||
virtual void onChar(char32 unichar) {}
|
||||
virtual void onResize(int w, int h) {}
|
||||
virtual void run() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,301 @@
|
|||
#include "Superchunk.hpp"
|
||||
#include "Game.hpp"
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include "lzfx/lzfx.h"
|
||||
#include "network/Network.hpp"
|
||||
|
||||
using std::fopen; using std::fwrite; using std::fread; using std::fclose;
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
Superchunk::Superchunk(Diggler::Game *G) : G(G), c(nullptr),
|
||||
chunksX(0), chunksY(0), chunksZ(0) {
|
||||
}
|
||||
|
||||
void Superchunk::free() {
|
||||
if (c == nullptr)
|
||||
return;
|
||||
for(int x = 0; x < chunksX; x++) {
|
||||
for(int y = 0; y < chunksY; y++) {
|
||||
for(int z = 0; z < chunksZ; z++) {
|
||||
if (c[x][y][z]) {
|
||||
delete c[x][y][z];
|
||||
}
|
||||
}
|
||||
delete[] c[x][y];
|
||||
}
|
||||
delete[] c[x];
|
||||
}
|
||||
delete[] c;
|
||||
}
|
||||
|
||||
void Superchunk::setSize(int x, int y, int z) {
|
||||
free();
|
||||
chunksX = x; chunksY = y; chunksZ = z;
|
||||
c = new Chunk***[chunksX];
|
||||
for (int x = 0; x < chunksX; x++) {
|
||||
c[x] = new Chunk**[chunksY];
|
||||
for (int y = 0; y < chunksY; y++) {
|
||||
c[x][y] = new Chunk*[chunksZ];
|
||||
for (int z = 0; z < chunksZ; z++) {
|
||||
c[x][y][z] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Superchunk::~Superchunk() {
|
||||
free();
|
||||
}
|
||||
|
||||
BlockType Superchunk::get(int x, int y, int z) {
|
||||
if (x < 0 || y < 0 || z < 0 || x >= chunksX*CX || y >= chunksY*CY || z >= chunksZ*CZ)
|
||||
return BlockType::Air;
|
||||
|
||||
int cx = x / CX;
|
||||
int cy = y / CY;
|
||||
int cz = z / CZ;
|
||||
|
||||
x %= CX;
|
||||
y %= CY;
|
||||
z %= CZ;
|
||||
|
||||
if (cx > chunksX || cy > chunksY || cz > chunksZ || !c[cx][cy][cz])
|
||||
return BlockType::Air;
|
||||
else
|
||||
return c[cx][cy][cz]->get(x, y, z);
|
||||
}
|
||||
|
||||
void Superchunk::set(int x, int y, int z, BlockType type) {
|
||||
if (x < 0 || y < 0 || z < 0 || x >= chunksX*CX || y >= chunksY*CY || z >= chunksZ*CZ)
|
||||
return;
|
||||
int cx = x / CX;
|
||||
int cy = y / CY;
|
||||
int cz = z / CZ;
|
||||
|
||||
x %= CX;
|
||||
y %= CY;
|
||||
z %= CZ;
|
||||
|
||||
if(!c[cx][cy][cz])
|
||||
c[cx][cy][cz] = new Chunk(false, cx, cy, cz, G);
|
||||
|
||||
c[cx][cy][cz]->set(x, y, z, type);
|
||||
}
|
||||
|
||||
void Superchunk::set2(int x, int y, int z, BlockType type) {
|
||||
if (x < 0 || y < 0 || z < 0 || x >= chunksX*CX || y >= chunksY*CY || z >= chunksZ*CZ)
|
||||
return;
|
||||
int cx = x / CX;
|
||||
int cy = y / CY;
|
||||
int cz = z / CZ;
|
||||
|
||||
x %= CX;
|
||||
y %= CY;
|
||||
z %= CZ;
|
||||
|
||||
if(!c[cx][cy][cz])
|
||||
c[cx][cy][cz] = new Chunk(false, cx, cy, cz, G);
|
||||
|
||||
c[cx][cy][cz]->set2(x, y, z, type);
|
||||
}
|
||||
|
||||
static int i(const float &f) {
|
||||
if (f >= 0)
|
||||
return (int)f;
|
||||
return ((int)f)-1;
|
||||
}
|
||||
|
||||
BlockType Superchunk::get(float x, float y, float z) {
|
||||
return get(i(x), i(y), i(z));
|
||||
}
|
||||
|
||||
Chunk* Superchunk::getChunk(int cx, int cy, int cz) {
|
||||
if (cx >= chunksX || cy >= chunksY || cz >= chunksZ)
|
||||
return nullptr;
|
||||
if(!c[cx][cy][cz])
|
||||
c[cx][cy][cz] = new Chunk(false, cx, cy, cz, G);
|
||||
return c[cx][cy][cz];
|
||||
}
|
||||
|
||||
void Superchunk::render(const glm::mat4& transform) {
|
||||
if (Chunk::RenderProgram == nullptr)
|
||||
return;
|
||||
Chunk::RenderProgram->bind();
|
||||
glEnableVertexAttribArray(Chunk::RenderProgram_attrib_coord);
|
||||
glEnableVertexAttribArray(Chunk::RenderProgram_attrib_texcoord);
|
||||
glEnableVertexAttribArray(Chunk::RenderProgram_attrib_color);
|
||||
Chunk::TextureAtlas->bind();
|
||||
|
||||
const static glm::vec3 cShift(Chunk::MidX, Chunk::MidY, Chunk::MidZ);
|
||||
glm::mat4 chunkTransform;
|
||||
for (int x = 0; x < chunksX; x++)
|
||||
for (int y = 0; y < chunksY; y++)
|
||||
for (int z = 0; z < chunksZ; z++)
|
||||
if (c[x][y][z]) {
|
||||
glm::vec3 translate = glm::vec3(x * CX, y * CY, z * CZ);
|
||||
if (G->LP->camera.frustum.sphereInFrustum(translate + cShift, Chunk::CullSphereRadius)) {
|
||||
chunkTransform = glm::translate(transform, translate);
|
||||
c[x][y][z]->renderBatched(chunkTransform);
|
||||
}
|
||||
}
|
||||
|
||||
glDisableVertexAttribArray(Chunk::RenderProgram_attrib_color);
|
||||
glDisableVertexAttribArray(Chunk::RenderProgram_attrib_texcoord);
|
||||
glDisableVertexAttribArray(Chunk::RenderProgram_attrib_coord);
|
||||
}
|
||||
|
||||
int Superchunk::getChunksX() const {
|
||||
return chunksX;
|
||||
}
|
||||
|
||||
int Superchunk::getChunksY() const {
|
||||
return chunksY;
|
||||
}
|
||||
|
||||
int Superchunk::getChunksZ() const {
|
||||
return chunksZ;
|
||||
}
|
||||
|
||||
void Superchunk::save(const std::string &path) const {
|
||||
FILE *f = fopen(path.c_str(), "w");
|
||||
int32 chunkSize[3] = {CX, CY, CZ};
|
||||
int32 superChunkSize[3] = {getChunksX(), getChunksY(), getChunksZ()};
|
||||
fwrite(chunkSize, sizeof(int32), 3, f);
|
||||
fwrite(superChunkSize, sizeof(int32), 3, f);
|
||||
const BlockType *chunkData = nullptr;
|
||||
uint compressedSize = CX * CY * CZ;
|
||||
byte *compressed = (byte*)malloc(compressedSize);
|
||||
for (int sx=0; sx < superChunkSize[0]; sx++) {
|
||||
for (int sy=0; sy < superChunkSize[1]; sy++) {
|
||||
for (int sz=0; sz < superChunkSize[2]; sz++) {
|
||||
// Chunk may be uninitialized
|
||||
if (c[sx][sy][sz] == nullptr) {
|
||||
// Chunk is empty (not initialized), mark as missing
|
||||
int16 size = -1;
|
||||
fwrite(&size, sizeof(int16), 1, f);
|
||||
} else {
|
||||
chunkData = c[sx][sy][sz]->blk;
|
||||
compressedSize = CX * CY * CZ;
|
||||
lzfx_compress(chunkData, CX*CY*CZ, compressed, &compressedSize);
|
||||
int16 size = (int16)compressedSize;
|
||||
fwrite(&size, sizeof(int16), 1, f);
|
||||
fwrite(compressed, compressedSize, 1, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
::free(compressed);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void Superchunk::load(const std::string &path) {
|
||||
FILE *f = fopen(path.c_str(), "r");
|
||||
int32 chunkSize[3];
|
||||
int32 superChunkSize[3];
|
||||
fread(chunkSize, sizeof(int32), 3, f);
|
||||
fread(superChunkSize, sizeof(int32), 3, f);
|
||||
setSize(superChunkSize[0], superChunkSize[1], superChunkSize[1]);
|
||||
uint uncompressedDataSize = CX * CY * CZ; // Should not change
|
||||
BlockType *uncompressedData = (BlockType*)malloc(uncompressedDataSize);
|
||||
for (int sx=0; sx < superChunkSize[0]; sx++) {
|
||||
for (int sy=0; sy < superChunkSize[1]; sy++) {
|
||||
for (int sz=0; sz < superChunkSize[2]; sz++) {
|
||||
int16 size; fread(&size, sizeof(int16), 1, f);
|
||||
if (c[sx][sy][sz] != nullptr) {
|
||||
delete c[sx][sy][sz]; // Bash out the old chunk
|
||||
}
|
||||
if (size == -1) { // Chunk is empty
|
||||
c[sx][sy][sz] = nullptr;
|
||||
} else {
|
||||
c[sx][sy][sz] = new Chunk(false, sx, sy, sz, G);
|
||||
byte *compressedData = (byte*)malloc(size);
|
||||
fread(compressedData, size, 1, f);
|
||||
uncompressedDataSize = CX * CY * CZ;
|
||||
lzfx_decompress(compressedData, size, uncompressedData, &uncompressedDataSize);
|
||||
for (int i=0; i < CX*CY*CZ; ++i) {
|
||||
c[sx][sy][sz]->blk[i] = uncompressedData[i];
|
||||
}
|
||||
::free(compressedData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
::free(uncompressedData);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
struct MapTransferHeader {
|
||||
struct {
|
||||
int32 x, y, z;
|
||||
} ChunkSize;
|
||||
struct {
|
||||
int32 x, y, z;
|
||||
} Chunks;
|
||||
};
|
||||
|
||||
void Superchunk::writeMsg(Net::OutMessage &msg) const {
|
||||
MapTransferHeader mth {
|
||||
{CX, CY, CZ},
|
||||
{getChunksX(), getChunksY(), getChunksZ()}
|
||||
};
|
||||
msg.writeData(&mth, sizeof(MapTransferHeader));
|
||||
const BlockType *chunkData = nullptr;
|
||||
uint compressedSize = CX * CY * CZ;
|
||||
byte *compressed = (byte*)malloc(compressedSize);
|
||||
for (int sx=0; sx < getChunksX(); sx++) {
|
||||
for (int sy=0; sy < getChunksY(); sy++) {
|
||||
for (int sz=0; sz < getChunksZ(); sz++) {
|
||||
// Chunk may be uninitialized
|
||||
if (c[sx][sy][sz] == nullptr) {
|
||||
// Chunk is empty (not initialized), mark as missing
|
||||
msg.writeI16(-1);
|
||||
} else {
|
||||
chunkData = c[sx][sy][sz]->blk;
|
||||
compressedSize = CX * CY * CZ;
|
||||
lzfx_compress(chunkData, CX*CY*CZ, compressed, &compressedSize);
|
||||
msg.writeI16(compressedSize);
|
||||
msg.writeData(compressed, compressedSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
::free(compressed);
|
||||
}
|
||||
|
||||
void Superchunk::readMsg(Net::InMessage &M) {
|
||||
MapTransferHeader mth;
|
||||
M.readData(&mth, sizeof(mth));
|
||||
setSize(mth.Chunks.x, mth.Chunks.y, mth.Chunks.z);
|
||||
int bytesRead = 0;
|
||||
uint uncompressedDataSize = CX * CY * CZ; // Should not change
|
||||
BlockType *uncompressedData = (BlockType*)malloc(uncompressedDataSize);
|
||||
for (int sx=0; sx < mth.Chunks.x; sx++) {
|
||||
for (int sy=0; sy < mth.Chunks.y; sy++) {
|
||||
for (int sz=0; sz < mth.Chunks.z; sz++) {
|
||||
int16 size = M.readI16();
|
||||
if (c[sx][sy][sz] != nullptr) {
|
||||
delete c[sx][sy][sz]; // Bash out the old chunk
|
||||
}
|
||||
if (size == -1) { // Chunk is empty
|
||||
c[sx][sy][sz] = nullptr; // Keep out
|
||||
} else {
|
||||
c[sx][sy][sz] = new Chunk(false, sx, sy, sz, G);
|
||||
byte *compressedData = (byte*)malloc(size);
|
||||
M.readData(compressedData, size);
|
||||
bytesRead += size;
|
||||
uncompressedDataSize = CX * CY * CZ;
|
||||
lzfx_decompress(compressedData, size, uncompressedData, &uncompressedDataSize);
|
||||
for (int i=0; i < CX*CY*CZ; ++i) {
|
||||
c[sx][sy][sz]->blk[i] = uncompressedData[i];
|
||||
}
|
||||
::free(compressedData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
::free(uncompressedData);
|
||||
getDebugStream() << "MapTransfer: read " << bytesRead << " b, MsgSize: " << M.getSize() << std::endl;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef SUPERCHUNK_HPP
|
||||
#define SUPERCHUNK_HPP
|
||||
#include "Chunk.hpp"
|
||||
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class Game;
|
||||
namespace Net {
|
||||
class InMessage;
|
||||
class OutMessage;
|
||||
}
|
||||
|
||||
class Superchunk {
|
||||
private:
|
||||
friend class Chunk;
|
||||
|
||||
Game *G;
|
||||
Chunk ****c;
|
||||
int chunksX, chunksY, chunksZ;
|
||||
|
||||
void set2(int x, int y, int z, BlockType type);
|
||||
void free();
|
||||
|
||||
public:
|
||||
Superchunk(Game *G = nullptr);
|
||||
~Superchunk();
|
||||
|
||||
void save(const std::string &path) const;
|
||||
void load(const std::string &path);
|
||||
void writeMsg(Net::OutMessage&) const;
|
||||
void readMsg(Net::InMessage&);
|
||||
|
||||
void setSize(int x, int y, int z);
|
||||
int getChunksX() const;
|
||||
int getChunksY() const;
|
||||
int getChunksZ() const;
|
||||
|
||||
BlockType get(int x, int y, int z);
|
||||
BlockType get(float x, float y, float z);
|
||||
void set(int x, int y, int z, BlockType type);
|
||||
Chunk* getChunk(int cx, int cy, int cz);
|
||||
|
||||
void render(const glm::mat4 &transform);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,242 @@
|
|||
#include "Texture.hpp"
|
||||
#include "Platform.hpp"
|
||||
#include "stb_image.h"
|
||||
#include <cstdio>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#define PushBoundTex() GLint currentBoundTex; glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤tBoundTex);
|
||||
#define PopBoundTex() glBindTexture(GL_TEXTURE_2D, currentBoundTex);
|
||||
|
||||
#define TEXTURE_LOAD_DEBUG 0
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
Texture::Texture(int w, int h, Texture::PixelFormat format, bool makeXor) : w(w), h(h), m_format(format) {
|
||||
PushBoundTex();
|
||||
create();
|
||||
//if (makeXor) { // Actually it's damn expensive to do this
|
||||
setPlaceholder(makeXor);
|
||||
//}
|
||||
PopBoundTex();
|
||||
}
|
||||
|
||||
Texture::Texture(int w, int h, uint8_t* data, Texture::PixelFormat format) {
|
||||
PushBoundTex();
|
||||
create();
|
||||
setTexture(w, h, data, format);
|
||||
PopBoundTex();
|
||||
}
|
||||
|
||||
Texture::Texture(const std::string& path, Texture::PixelFormat format) {
|
||||
PushBoundTex();
|
||||
create();
|
||||
int stbiFormat;
|
||||
switch (format) {
|
||||
case PixelFormat::RGB:
|
||||
stbiFormat = STBI_rgb;
|
||||
break;
|
||||
case PixelFormat::RGBA:
|
||||
stbiFormat = STBI_rgb_alpha;
|
||||
break;
|
||||
case PixelFormat::Monochrome8:
|
||||
stbiFormat = STBI_grey;
|
||||
break;
|
||||
}
|
||||
int width, height, channels;
|
||||
unsigned char *ptr = nullptr;
|
||||
FILE *fp = fopen(path.c_str(), "rb");
|
||||
if (fp != nullptr) {
|
||||
ptr = stbi_load_from_file(fp, &width, &height, &channels, stbiFormat);
|
||||
w = width; h = height;
|
||||
}
|
||||
if (ptr && width && height) {
|
||||
setTexture(w, h, ptr, format);
|
||||
stbi_image_free(ptr);
|
||||
#if TEXTURE_LOAD_DEBUG
|
||||
getDebugStream() << "Loaded image " << path << std::endl;
|
||||
#endif
|
||||
} else {
|
||||
w = 64; h = 64;
|
||||
if (fp == nullptr)
|
||||
getErrorStream() << "Failed to open \"" << path << "\" : " << std::strerror(errno) << std::endl;
|
||||
else
|
||||
getErrorStream() << "Failed to load image \"" << path << "\" : " << stbi_failure_reason() << std::endl;
|
||||
setPlaceholder(true);
|
||||
}
|
||||
PopBoundTex();
|
||||
}
|
||||
|
||||
void Texture::create() {
|
||||
glGenTextures(1, &id);
|
||||
glBindTexture(GL_TEXTURE_2D, id);
|
||||
setFiltering(Filter::Nearest, Filter::Nearest);
|
||||
}
|
||||
|
||||
GLenum getFilterGlConstant(Texture::Filter filter) {
|
||||
switch (filter) {
|
||||
case Texture::Filter::Linear:
|
||||
return GL_LINEAR;
|
||||
case Texture::Filter::Nearest:
|
||||
return GL_NEAREST;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Texture::setFiltering(Filter min, Filter mag) {
|
||||
PushBoundTex();
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, getFilterGlConstant(min));
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, getFilterGlConstant(mag));
|
||||
PopBoundTex();
|
||||
}
|
||||
|
||||
GLenum getWrapGlConstant(Texture::Wrapping wrap) {
|
||||
switch (wrap) {
|
||||
case Texture::Wrapping::Clamp:
|
||||
return GL_CLAMP;
|
||||
case Texture::Wrapping::Repeat:
|
||||
return GL_REPEAT;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Texture::setWrapping(Wrapping s, Wrapping t) {
|
||||
PushBoundTex();
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, getWrapGlConstant(s));
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, getWrapGlConstant(t));
|
||||
PopBoundTex();
|
||||
}
|
||||
|
||||
void Texture::setPlaceholder(bool makeXor) {
|
||||
// VALVe checkerboard, anyone?
|
||||
int pxLength;
|
||||
switch (m_format) {
|
||||
case PixelFormat::RGB:
|
||||
pxLength = 3;
|
||||
break;
|
||||
case PixelFormat::RGBA:
|
||||
pxLength = 4;
|
||||
break;
|
||||
case PixelFormat::Monochrome8:
|
||||
pxLength = 1;
|
||||
break;
|
||||
}
|
||||
unsigned char* white = new unsigned char[w * h * pxLength];
|
||||
if (makeXor) {
|
||||
for(int x = 0; x < w; x++) // XOR texture
|
||||
for(int y = 0; y < h; y++)
|
||||
for(int i = 0; i < pxLength; i++)
|
||||
white[i+x*pxLength+y*w*pxLength] = x ^ y;
|
||||
} else {
|
||||
memset(white, 255, w * h * pxLength);
|
||||
}
|
||||
setTexture(w, h, white, m_format);
|
||||
delete[] white;
|
||||
}
|
||||
|
||||
void Texture::setTexture(int w, int h, uint8_t *data, Texture::PixelFormat format) {
|
||||
this->w = w; this->h = h;
|
||||
this->m_format = format;
|
||||
GLenum glFormat;
|
||||
switch (format) {
|
||||
case PixelFormat::RGB:
|
||||
glFormat = GL_RGB;
|
||||
break;
|
||||
case PixelFormat::RGBA:
|
||||
glFormat = GL_RGBA;
|
||||
break;
|
||||
case PixelFormat::Monochrome8:
|
||||
glFormat = GL_LUMINANCE8;
|
||||
break;
|
||||
}
|
||||
glTexImage2D(GL_TEXTURE_2D, // target
|
||||
0, // level, 0 = base, no minimap
|
||||
glFormat, // internalformat
|
||||
w, // width
|
||||
h, // height
|
||||
0, // border
|
||||
glFormat, // format
|
||||
GL_UNSIGNED_BYTE, // type
|
||||
data);
|
||||
}
|
||||
|
||||
GLuint Texture::getId() const {
|
||||
return id;
|
||||
}
|
||||
|
||||
void Texture::resize(int w, int h) {
|
||||
PushBoundTex();
|
||||
//glGenTextures(1, &id);
|
||||
bind();
|
||||
GLenum glFormat;
|
||||
switch (m_format) {
|
||||
case PixelFormat::RGB:
|
||||
glFormat = GL_RGB;
|
||||
break;
|
||||
case PixelFormat::RGBA:
|
||||
glFormat = GL_RGBA;
|
||||
break;
|
||||
case PixelFormat::Monochrome8:
|
||||
glFormat = GL_LUMINANCE8;
|
||||
break;
|
||||
}
|
||||
glTexImage2D(GL_TEXTURE_2D, // target
|
||||
0, // level, 0 = base, no minimap,
|
||||
glFormat, // internalformat
|
||||
w, // width
|
||||
h, // height
|
||||
0, // border
|
||||
glFormat, // format
|
||||
GL_UNSIGNED_BYTE, // type
|
||||
nullptr);
|
||||
PopBoundTex();
|
||||
}
|
||||
|
||||
int Texture::getW() {
|
||||
return w;
|
||||
}
|
||||
|
||||
int Texture::getH() {
|
||||
return h;
|
||||
}
|
||||
|
||||
Texture::PixelFormat Texture::getPixelFormat() {
|
||||
return m_format;
|
||||
}
|
||||
|
||||
int Texture::getRequiredBufferSize() {
|
||||
int texelSize;
|
||||
switch (m_format) {
|
||||
case PixelFormat::RGB:
|
||||
texelSize = 3;
|
||||
break;
|
||||
case PixelFormat::RGBA:
|
||||
texelSize = 4;
|
||||
break;
|
||||
case PixelFormat::Monochrome8:
|
||||
texelSize = 1;
|
||||
break;
|
||||
}
|
||||
return w * h * texelSize;
|
||||
}
|
||||
|
||||
void Texture::getTexture(uint8_t* data) {
|
||||
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||
}
|
||||
|
||||
void Texture::bind() const {
|
||||
glBindTexture(GL_TEXTURE_2D, id);
|
||||
}
|
||||
|
||||
void Texture::bind(int number) const {
|
||||
glActiveTexture(GL_TEXTURE0+number);
|
||||
glBindTexture(GL_TEXTURE_2D, id);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
|
||||
Texture::~Texture() {
|
||||
glDeleteTextures(1, &id);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef TEXTURE_HPP
|
||||
#define TEXTURE_HPP
|
||||
#include <GL/glew.h>
|
||||
#include <string>
|
||||
|
||||
namespace Diggler {
|
||||
class Texture {
|
||||
public:
|
||||
enum class PixelFormat {
|
||||
RGB,
|
||||
RGBA,
|
||||
Monochrome8
|
||||
};
|
||||
enum class Filter {
|
||||
Nearest,
|
||||
Linear
|
||||
};
|
||||
enum class Wrapping {
|
||||
Repeat,
|
||||
Clamp
|
||||
};
|
||||
private:
|
||||
GLuint id;
|
||||
int w, h;
|
||||
PixelFormat m_format;
|
||||
void create();
|
||||
void setPlaceholder(bool makeXor);
|
||||
|
||||
public:
|
||||
Texture(int w, int h, PixelFormat format = PixelFormat::RGB, bool makeXor = false);
|
||||
Texture(int w, int h, uint8_t *data, PixelFormat format = PixelFormat::RGB);
|
||||
Texture(const std::string& path, PixelFormat format = PixelFormat::RGB);
|
||||
GLuint getId() const;
|
||||
operator GLuint() const { return getId(); }
|
||||
void resize(int w, int h);
|
||||
int getW();
|
||||
int getH();
|
||||
PixelFormat getPixelFormat();
|
||||
int getRequiredBufferSize();
|
||||
void getTexture(uint8_t *data);
|
||||
void setTexture(int w, int h, uint8_t *data, PixelFormat format = PixelFormat::RGB);
|
||||
void setFiltering(Filter min, Filter mag);
|
||||
void setWrapping(Wrapping s, Wrapping t);
|
||||
void bind() const;
|
||||
void bind(int number) const;
|
||||
~Texture();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,30 @@
|
|||
#include "UITestState.hpp"
|
||||
#include "GameWindow.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
UITestState::UITestState(GameWindow *W) : W(W) {
|
||||
}
|
||||
|
||||
UITestState::~UITestState() {
|
||||
}
|
||||
|
||||
void UITestState::run() {
|
||||
}
|
||||
|
||||
void UITestState::updateViewport() {
|
||||
}
|
||||
|
||||
void UITestState::onMouseScroll(double x, double y) {
|
||||
}
|
||||
|
||||
void UITestState::onResize(int w, int h) {
|
||||
}
|
||||
|
||||
void UITestState::onMouseButton(int key, int action, int mods) {
|
||||
}
|
||||
|
||||
void UITestState::onCursorPos(double x, double y) {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef UI_TEST_STATE_HPP
|
||||
#define UI_TEST_STATE_HPP
|
||||
#include "State.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
namespace UI {
|
||||
class Text;
|
||||
}
|
||||
|
||||
class UITestState : public State {
|
||||
private:
|
||||
class GameWindow *W;
|
||||
UI::Text *txt;
|
||||
|
||||
public:
|
||||
UITestState(GameWindow*);
|
||||
~UITestState();
|
||||
|
||||
void onMouseButton(int key, int action, int mods);
|
||||
void onCursorPos(double x, double y);
|
||||
void onResize(int w, int h);
|
||||
void onMouseScroll(double x, double y);
|
||||
void run();
|
||||
|
||||
void updateViewport();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,55 @@
|
|||
#include "VBO.hpp"
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
static void unref(int *rc, GLuint id) {
|
||||
if (--(*rc) == 0) {
|
||||
glDeleteBuffers(1, &id);
|
||||
delete rc;
|
||||
}
|
||||
}
|
||||
|
||||
VBO::VBO() {
|
||||
*(m_refcount = new int[1]) = 1;
|
||||
glGenBuffers(1, &id);
|
||||
}
|
||||
|
||||
VBO::VBO(const VBO &other) {
|
||||
(*this) = other;
|
||||
}
|
||||
VBO& VBO::operator=(const VBO &other) {
|
||||
unref(m_refcount, id);
|
||||
id = other.id;
|
||||
m_refcount = other.m_refcount;
|
||||
(*m_refcount)++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
VBO::VBO(VBO &&other) {
|
||||
(*this) = other;
|
||||
}
|
||||
VBO& VBO::operator=(VBO &&other) {
|
||||
unref(m_refcount, id);
|
||||
id = other.id;
|
||||
m_refcount = other.m_refcount;
|
||||
return *this;
|
||||
}
|
||||
|
||||
VBO::~VBO() {
|
||||
unref(m_refcount, id);
|
||||
}
|
||||
|
||||
void VBO::bind() const {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, id);
|
||||
}
|
||||
|
||||
int VBO::getSize() const {
|
||||
GLint currentBoundArray; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, ¤tBoundArray);
|
||||
GLint data;
|
||||
bind();
|
||||
glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &data);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, currentBoundArray);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
#ifndef VBO_HPP
|
||||
#define VBO_HPP
|
||||
#include <vector>
|
||||
#include <GL/glew.h>
|
||||
#include <typeinfo>
|
||||
#include "Platform.hpp"
|
||||
#include <cstring>
|
||||
|
||||
namespace Diggler {
|
||||
|
||||
class VBO {
|
||||
private:
|
||||
int *m_refcount;
|
||||
|
||||
public:
|
||||
GLuint id;
|
||||
|
||||
// Ctor / dtor
|
||||
VBO();
|
||||
~VBO();
|
||||
// Copy
|
||||
VBO(const VBO&);
|
||||
VBO& operator=(const VBO&);
|
||||
// Move
|
||||
VBO(VBO&&);
|
||||
VBO& operator=(VBO&&);
|
||||
|
||||
operator GLuint() const { return id; }
|
||||
template <typename T> void setData(const std::vector<T>& data, GLenum usage = GL_STATIC_DRAW) {
|
||||
GLint currentBoundArray; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, ¤tBoundArray);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, id);
|
||||
glBufferData(GL_ARRAY_BUFFER, data.size()*sizeof(T), data.data(), usage);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, currentBoundArray);
|
||||
//getDebugStream() << "Set buffer " << typeid(T).name() << ' ' << data.size() << std::endl;
|
||||
}
|
||||
template <typename T> void setData(const std::initializer_list<T>& data, GLenum usage = GL_STATIC_DRAW) {
|
||||
// TODO: Fix this, it's not working: data seems to always be empty
|
||||
GLint currentBoundArray; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, ¤tBoundArray);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, id);
|
||||
glBufferData(GL_ARRAY_BUFFER, data.size()*sizeof(T), data.begin(), usage);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, currentBoundArray);
|
||||
//getDebugStream() << "Set buffer " << typeid(T).name() << ' ' << data.size() << std::endl;
|
||||
}
|
||||
template <typename T> void setData(const T *data, uint count, GLenum usage = GL_STATIC_DRAW) {
|
||||
GLint currentBoundArray; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, ¤tBoundArray);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, id);
|
||||
glBufferData(GL_ARRAY_BUFFER, count*sizeof(*data), data, usage);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, currentBoundArray);
|
||||
}
|
||||
void bind() const;
|
||||
int getSize() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef VERSION_INFO_HPP
|
||||
#define VERSION_INFO_HPP
|
||||
|
||||
namespace Diggler {
|
||||
const char* VersionString = "0.1.0";
|
||||
int VersionMajor = 0;
|
||||
int VersionMinor = 1;
|
||||
int VersionRevision = 0;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,87 @@
|
|||
#ifndef UNDERSCORE_HPP
|
||||
#define UNDERSCORE_HPP
|
||||
#include <utility>
|
||||
|
||||
template<class T> struct _ {
|
||||
T *ptr;
|
||||
|
||||
// Construct
|
||||
_() : ptr(nullptr) {}
|
||||
_(decltype(nullptr)) : ptr(nullptr) {}
|
||||
_(T *t) : ptr(t) {}
|
||||
template<typename... Args> _(Args&&... args) {
|
||||
ptr = new T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// No copy
|
||||
_(const _&) = delete;
|
||||
_& operator=(const _&) = delete;
|
||||
|
||||
// Move
|
||||
_(_ &&o) {
|
||||
delete ptr;
|
||||
ptr = o.ptr;
|
||||
o.ptr = nullptr;
|
||||
}
|
||||
_& operator=(_ &&o) {
|
||||
delete ptr;
|
||||
ptr = o.ptr;
|
||||
o.ptr = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Assign
|
||||
_& operator=(T *t) {
|
||||
delete ptr;
|
||||
ptr = t;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Comparison
|
||||
bool operator==(T *t) const {
|
||||
return t == ptr;
|
||||
}
|
||||
bool operator!=(T *t) const {
|
||||
return t != ptr;
|
||||
}
|
||||
|
||||
// Get
|
||||
T* get() const {
|
||||
return ptr;
|
||||
}
|
||||
T& operator[](int i) const {
|
||||
return ptr[i];
|
||||
}
|
||||
T& operator*() const {
|
||||
return *ptr;
|
||||
}
|
||||
T* operator->() const {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// Addressof
|
||||
T** operator&() const {
|
||||
return &ptr;
|
||||
}
|
||||
|
||||
// Cast
|
||||
operator T*() const {
|
||||
return ptr;
|
||||
}
|
||||
operator const T*() const {
|
||||
return ptr;
|
||||
}
|
||||
template<typename R> operator R*() const {
|
||||
return (R*)ptr;
|
||||
}
|
||||
operator bool() const {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// Destruct
|
||||
~_() {
|
||||
delete ptr;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
Binary file not shown.
After Width: | Height: | Size: 416 B |
|
@ -0,0 +1 @@
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
#version 120
|
||||
|
||||
varying vec4 v_color;
|
||||
|
||||
void main(void) {
|
||||
gl_FragColor = v_color;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#version 120
|
||||
|
||||
attribute vec2 coord;
|
||||
uniform mat4 mvp;
|
||||
attribute vec4 color;
|
||||
varying vec4 v_color;
|
||||
|
||||
void main(void) {
|
||||
gl_Position = mvp * vec4(coord, 0.0, 1.0);
|
||||
v_color = color;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#version 120
|
||||
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D mytexture;
|
||||
void main(void) {
|
||||
gl_FragColor = texture2D(mytexture, v_texcoord);
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#version 120
|
||||
|
||||
attribute vec2 coord;
|
||||
uniform mat4 mvp;
|
||||
attribute vec2 texcoord;
|
||||
varying vec2 v_texcoord;
|
||||
|
||||
void main(void) {
|
||||
gl_Position = mvp * vec4(coord, 0.0, 1.0);
|
||||
v_texcoord = texcoord;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#version 120
|
||||
|
||||
varying vec2 v_texcoord;
|
||||
varying vec4 v_color;
|
||||
uniform sampler2D mytexture;
|
||||
void main(void) {
|
||||
gl_FragColor = texture2D(mytexture, v_texcoord) * v_color;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#version 120
|
||||
|
||||
attribute vec2 coord;
|
||||
uniform mat4 mvp;
|
||||
attribute vec2 texcoord;
|
||||
attribute vec4 color;
|
||||
varying vec4 v_color;
|
||||
varying vec2 v_texcoord;
|
||||
|
||||
void main(void) {
|
||||
gl_Position = mvp * vec4(coord, 0.0, 1.0);
|
||||
v_texcoord = texcoord;
|
||||
v_color = color;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#version 120
|
||||
|
||||
varying vec4 v_color;
|
||||
|
||||
void main(void) {
|
||||
gl_FragColor = v_color;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#version 120
|
||||
|
||||
uniform mat4 mvp;
|
||||
attribute vec4 coord;
|
||||
attribute vec4 color;
|
||||
varying vec4 v_color;
|
||||
|
||||
void main(void) {
|
||||
v_color = color;
|
||||
gl_Position = mvp * vec4(coord.xyz, 1);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#version 120
|
||||
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D texture;
|
||||
|
||||
void main(void) {
|
||||
gl_FragColor = texture2D(texture, v_texcoord);
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#version 120
|
||||
|
||||
uniform mat4 mvp;
|
||||
attribute vec4 coord;
|
||||
attribute vec2 texcoord;
|
||||
varying vec2 v_texcoord;
|
||||
|
||||
void main(void) {
|
||||
v_texcoord = texcoord;
|
||||
gl_Position = mvp * vec4(coord.xyz, 1);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#version 120
|
||||
|
||||
varying vec2 v_texcoord;
|
||||
varying vec4 v_color;
|
||||
uniform sampler2D texture;
|
||||
|
||||
void main(void) {
|
||||
gl_FragColor = texture2D(texture, v_texcoord) * v_color;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#version 120
|
||||
|
||||
uniform mat4 mvp;
|
||||
attribute vec4 coord;
|
||||
attribute vec4 color;
|
||||
attribute vec2 texcoord;
|
||||
varying vec2 v_texcoord;
|
||||
varying vec4 v_color;
|
||||
|
||||
void main(void) {
|
||||
v_texcoord = texcoord;
|
||||
v_color = color;
|
||||
gl_Position = mvp * vec4(coord.xyz, 1);
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#version 120
|
||||
|
||||
varying vec2 v_texcoord;
|
||||
varying vec4 v_color;
|
||||
varying vec4 v_coord;
|
||||
uniform sampler2D texture;
|
||||
uniform float fogEnd = 32.0;
|
||||
uniform float fogStart = 16.0;
|
||||
|
||||
void main(void) {
|
||||
float fogCoord = (gl_FragCoord.z/gl_FragCoord.w);
|
||||
gl_FragColor = mix(texture2D(texture, v_texcoord) * v_color, vec4(0.0, 0.0, 0.0, 1.0), 1.0-clamp((fogEnd-fogCoord)/(fogEnd-fogStart), 0.0, 1.0));
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#version 120
|
||||
|
||||
uniform mat4 mvp;
|
||||
attribute vec4 coord;
|
||||
attribute vec2 texcoord;
|
||||
attribute vec4 color;
|
||||
varying vec2 v_texcoord;
|
||||
varying vec4 v_color;
|
||||
|
||||
void main(void) {
|
||||
v_texcoord = texcoord;
|
||||
v_color = color;
|
||||
gl_Position = mvp * vec4(coord.xyz, 1);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#version 120
|
||||
|
||||
varying vec4 v_coord;
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D texture;
|
||||
uniform float fogEnd = 32.0;
|
||||
uniform float fogStart = 16.0;
|
||||
|
||||
void main(void) {
|
||||
float fogCoord = (gl_FragCoord.z/gl_FragCoord.w);
|
||||
gl_FragColor = mix(texture2D(texture, v_texcoord), vec4(0.0, 0.0, 0.0, 1.0), 1.0-clamp((fogEnd-fogCoord)/(fogEnd-fogStart), 0.0, 1.0));
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#version 120
|
||||
|
||||
uniform mat4 mvp;
|
||||
attribute vec4 coord;
|
||||
attribute vec2 texcoord;
|
||||
varying vec4 v_coord;
|
||||
varying vec2 v_texcoord;
|
||||
|
||||
void main(void) {
|
||||
v_coord = coord;
|
||||
v_texcoord = texcoord;
|
||||
gl_Position = mvp * vec4(coord.xyz, 1);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue