This commit is contained in:
j3d1 2016-10-05 17:34:44 +02:00
commit 756e98fe9f
55 changed files with 123227 additions and 437 deletions

View file

@ -8,7 +8,7 @@ function(setup_target NAME)
set_property(TARGET ${NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
target_compile_options(${NAME} PRIVATE -Wall -Wextra)
target_compile_options(${NAME} PRIVATE -fdiagnostics-color=always)
target_compile_options(${NAME} PRIVATE $<$<CONFIG:DEBUG>:-ggdb -O2>)
target_compile_options(${NAME} PRIVATE $<$<CONFIG:DEBUG>:-ggdb -O0>)
target_compile_options(${NAME} PRIVATE $<$<CONFIG:RELEASE>:-O3 -NDEBUG>)
endfunction(setup_target)

BIN
data/img/planet_mars.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
data/img/test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7 KiB

114662
data/mesh/planet_128.stl Normal file

File diff suppressed because it is too large Load diff

18
data/mesh/rocket.scad Normal file
View file

@ -0,0 +1,18 @@
$fn = 16;
translate([0, 0, -0.2]) {
cylinder(r=0.05, h=0.4);
translate([0, 0, 0.25]) cylinder(r=0.065, h=0.1);
translate([0, 0, 0.35]) sphere(r=0.07);
translate([0, 0, 0.35]) cylinder(r=0.01, h=0.2);
for (h=[0.07, 0.2]) {
translate([0, 0, h]) {
for (i=[0,1,2,3]) {
rotate([0, 0, 90*i]) {
cube([0.2, 0.01, 0.07], center=true);
}
}
}
}
}

5126
data/mesh/rocket.stl Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,13 @@
#version 120
uniform sampler2D texture;
varying vec3 vertex;
varying vec2 uv;
void main()
{
vec3 color = texture2D(texture, uv).rgb;
gl_FragColor = vec4(color, 1.0);
}

View file

@ -0,0 +1,12 @@
#version 120
uniform vec2 uvScale;
varying vec3 vertex;
varying vec2 uv;
void main()
{
gl_Position = gl_Vertex;
vertex = gl_Position.xyz;
uv = uvScale * (0.5 + vertex.xy / 2.0);
}

View file

@ -10,6 +10,10 @@ uniform vec3 materialColor;
uniform int materialKind;
uniform int materialSeed;
uniform int explLightsNum;
uniform vec3 explLightsPos[10];
uniform float explLightsIntensities[10];
void main()
{
vec3 Eye = normalize(-vertex);
@ -27,7 +31,31 @@ void main()
vec3 color = materialColor;
color = max(vec3(0.0), min(vec3(1.0), color));
vec3 IDiffuse = vec3(color) * lightColor * max(dot(normal, lightDirection), 0.0);
vec3 light = lightColor * max(dot(normal, lightDirection), 0.0);
//vec3 light = vec3(0.0);
int i;
for (i=0; i<explLightsNum; i++) {
vec3 explLightColor = vec3(1.0, 0.5, 0.2);
vec3 diff = vertex - explLightsPos[i];
float l = 10.0*length(diff);
float dir = max(0.0, -dot(normal, diff));
float dp = max(0.0, 1.0-l);
float intensity = 10.0;
if (dp == 0.0) {
intensity *= dir;
} else {
intensity *= dp;
}
intensity /= 1.0 + 0.5*l*l;
light += intensity * pow(explLightsIntensities[i], 2.0) * explLightColor;
}
light = max(vec3(0.0), light);
vec3 IDiffuse = vec3(color) * light;
// TODO make instensity/exponent as parameter
//vec3 ISpecular = lightColor * 5.0 * pow(max(dot(Reflected, Eye), 0.0), 2.0);

View file

@ -9,11 +9,12 @@ varying vec3 lightDirection;
uniform mat4 model;
uniform vec3 lightPosition;
uniform float aspectRatio;
void main()
{
// TODO: this becomes invalid when projection matrices are used
vec3 p = (model*vec4(in_vertex, 1.0)).xyz;
vec3 p = (model*vec4(in_vertex/vec3(aspectRatio, 1.0, 1.0), 1.0)).xyz;
lightDirection = normalize(lightPosition - p);
vertex = p.xyz;

View file

@ -4,7 +4,8 @@ varying vec3 velocity;
varying float decay;
varying float explCenterDist;
uniform vec3 explCenter;
float pi = 3.14159;
vec3 hsv2rgb(vec3 c)
{
@ -13,15 +14,28 @@ vec3 hsv2rgb(vec3 c)
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
float hash(float n)
{
return fract(sin(n)*43758.5453);
}
void main()
{
float r = length(vertex);
if (r > 1.0) {
// normalize radius from 0..1
vec2 vn = 2.0*vertex;
float rn = length(vn);
float angle = atan(vn.y, vn.x);
float hs = 20.0;
float removeCorners = 0.5 * (hash(fract(hs*vertex.x)) + hash(fract(hs*vertex.y)));
if (rn+removeCorners > 1.0) {
discard;
}
//float rf = 0.5-0.5*rn;
float rf = 1.0;
float d = decay/(1.0+max(0.0, 1.0-2.0*explCenterDist));
float h = (1.0-d)/6.0;
float h = max(0.0, (1.0-d)/6.0) * rf;
float v = max(0.0, (1.0-log(d)));
float s = max(0.0, min(30.0 * sqrt(decay) * explCenterDist, 1.0));
@ -31,4 +45,5 @@ void main()
gl_FragColor = vec4(hsv2rgb(vec3(h, s, v)), 1.0);
//gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
//gl_FragColor.rgb = vec3(0.5+0.5*(a/pi));
}

View file

@ -1,7 +1,8 @@
#version 120
attribute vec2 in_vertex;
attribute vec2 in_geometry;
attribute vec3 in_position;
attribute vec3 in_velocity;
attribute float in_maxDist;
uniform float age;
uniform float size;
@ -14,6 +15,8 @@ varying vec3 velocity;
varying vec2 vertex;
varying float explCenterDist;
uniform float aspectRatio;
// TODO: rotate to face the user!
void main()
{
@ -26,11 +29,17 @@ void main()
decay = ageMod / halfAge;
// faster particles are smaller
// TODO: scale by time too! scale down fast after 3 halfAges
float scaleBySpeed = (1.0 - 0.95*length(in_velocity)/maxVelocity);
float finalSize = size * scaleBySpeed;
vec2 base = in_vertex;
vec3 p = finalSize*vec3(base, 0.0);
vec3 offset = (0.2*age + log(1.0+age*5.0)) * in_velocity + in_position;
float finalSize = size * scaleBySpeed * (1.0-max(0.0, decay-3.0*halfAge)/2.0);
vec2 base = in_geometry;
vec3 p = vec3(finalSize*base, 0.0);
vec3 move = (0.2*age + log(1.0+age*5.0)) * in_velocity;
float md = length(move);
if (md > in_maxDist) {
move *= in_maxDist / md;
}
vec3 offset = move + in_position;
p += offset;
gl_Position = vec4(p, 1.0);
@ -38,5 +47,5 @@ void main()
vertex = base.xy;
velocity = in_velocity;
explCenterDist = length(explCenter-offset);
explCenterDist = length(explCenter - offset);
}

View file

@ -4,6 +4,7 @@ find_package(OpenGL REQUIRED)
find_package(epoxy REQUIRED)
find_package(X11 REQUIRED)
find_package(assimp REQUIRED)
find_package(PNG REQUIRED)
set(GAME_SRC
main.cpp
@ -11,14 +12,22 @@ set(GAME_SRC
glclasses.cpp
game_window.cpp
renderer.cpp
developer_console.cpp
renderer_polygon_2d/renderer_polygon_2d.cpp
renderer_polygon_3d/renderer_polygon_3d.cpp
renderer_polygon_3d/polygon_model.cpp
renderer_polygon_3d/particle_batch.cpp
renderer_polygon_3d/image_texture.cpp
renderer_ray_tracer/renderer_ray_tracer.cpp
network/session.cpp
sound/sound_effects.cpp
util.cpp
game.cpp
@ -33,9 +42,17 @@ set(GAME_SRC
state/state.cpp
)
set(SOUND_LIBRARIES "")
# TODO: make optional!
#set(GAME_SRC ${GAME_SRC} sound/sound.cpp)
#set(SOUND_LIBRARIES -lportaudio -lsndfile)
set(GAME_SRC ${GAME_SRC} sound/dummy_sound.cpp)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${OPENGL_INCLUDE_DIR})
include_directories(${PNG_INCLUDE_DIR})
include_directories(${CMAKE_SOURCE_DIR}/libs/glm/)
include_directories(${CMAKE_SOURCE_DIR}/libs/asio/asio/include/)
include_directories(${assimp_INCLUDE_DIRS})
@ -43,4 +60,5 @@ include_directories(${assimp_INCLUDE_DIRS})
add_executable(game ${GAME_SRC})
setup_target(game)
target_link_libraries(game X11 epoxy pthread ${assimp_LIBRARIES} assimp)
target_link_libraries(game X11 epoxy pthread ${assimp_LIBRARIES} assimp
${SOUND_LIBRARIES} ${PNG_LIBRARIES})

View file

@ -0,0 +1,74 @@
#include "developer_console.hpp"
#include <iostream>
#include "util.hpp"
namespace developer {
DeveloperConsole s_instance;
DeveloperConsole::CallbackResult DeveloperConsole::CallbackResult::createError(const std::string &answer)
{
return CallbackResult(false, answer);
}
DeveloperConsole::CallbackResult DeveloperConsole::CallbackResult::createOkay(const std::string &answer)
{
return CallbackResult(true, answer);
}
DeveloperConsole::CallbackResult::CallbackResult(bool okay, const std::string &answer)
: okay(okay), answer(answer)
{
}
DeveloperConsole::DeveloperConsole()
{
}
// static thing.
DeveloperConsole &DeveloperConsole::instance() {
return s_instance;
}
bool DeveloperConsole::addCallback(const std::string &token, callback_t cb)
{
bool hasWhitespace = false;
for (size_t i=0; i<token.size(); i++) {
if (isspace(token[i])) {
hasWhitespace = true;
break;
}
}
bool cbIsNull = (cb == nullptr);
bool tokenEmpty = token.empty();
if (hasWhitespace || cbIsNull || tokenEmpty) {
std::cout << "not registering bad token/cb comibnation for: " << token << std::endl;
return false;
}
std::cout<<"[developer console] adding developer "
"callback for token '" << token << "'" << std::endl;
m_callbacks[token] = cb;
return true;
}
DeveloperConsole::CallbackResult DeveloperConsole::dispatchInput(const std::string &token, const std::string &payload)
{
std::map<std::string, DeveloperConsole::callback_t>::iterator it = m_callbacks.find(token);
if (it == m_callbacks.end()) {
return DeveloperConsole::CallbackResult::createError("unknown command with token: '" + token + "'");
}
return it->second(payload);
}
DeveloperConsole::CallbackResult resultOkay()
{
return DeveloperConsole::CallbackResult::createOkay();
}
}

View file

@ -0,0 +1,49 @@
#pragma once
#include <string>
#include <map>
#include <functional>
// TODO
// stuff in namespace "developer" can be disabled and the game must function in
// exact the same way just without cool debug features.
namespace developer {
class DeveloperConsole {
public:
class CallbackResult {
private:
CallbackResult(bool okay, const std::string &answer);
public:
static CallbackResult createError(const std::string &answer="");
static CallbackResult createOkay(const std::string &answer="");
public:
// read-only interface to result data
const bool okay;
const std::string answer;
};
typedef std::function<CallbackResult(const std::string &cmd)> callback_t;
DeveloperConsole();
// access functionality over this static singleton attribute to make
// it really easy and fast to use
static DeveloperConsole &instance();
// register callback for a token.
// token must not have whitespace in it and not be empty.
// old callback will be overridden if any
// return true on succes, false if not saved.
bool addCallback(const std::string &token, callback_t cb);
// dispatch input received over a communication channel.
CallbackResult dispatchInput(const std::string &token, const std::string &payload);
private:
std::map<std::string, callback_t> m_callbacks;
};
DeveloperConsole::CallbackResult resultOkay();
}

View file

@ -80,3 +80,7 @@ bool Game::cycle(float dt)
return true;
}
void Game::resize(int width, int height)
{
m_state->setPlayingFieldSize(width, height);
}

View file

@ -13,6 +13,9 @@ class Game {
// for rendering
game::State *state() const { return m_state; }
// resize the playing field
void resize(int width, int height);
private:
game::State *m_state;

View file

@ -45,7 +45,21 @@ class GameWindow : public endofthejedi::GLWindow {
m_renderer.render(m_game->state());
}
void resize() override { glViewport(0, 0, getwidth(), getheight()); }
void resize() override
{
std::cout<<"resize()" << std::endl;
glViewport(0, 0, getwidth(), getheight());
// resize the game
m_game->resize(getwidth(), getheight());
// TODO: mark it and let the reinit() happen in the next so it happnens just once. while doing the resize
m_game->state()->init();
// resize the renderer
m_renderer.setWindowSize(getwidth(), getheight());
}
public:
GameWindow(unsigned int width, unsigned int height, Game *ptr)

View file

@ -6,28 +6,27 @@
#include <sstream>
namespace endofthejedi {
VAO::VAO() { glGenVertexArrays(1, &m_name); }
VAO::~VAO() { glDeleteVertexArrays(1, &m_name); }
VAO::VAO() {}
VAO::~VAO() { glDeleteVertexArrays(1, &m_name); }
void VAO::bind() { glBindVertexArray(m_name); }
void VAO::unbind() { glBindVertexArray(0); }
void VAO::init() { glGenVertexArrays(1, &m_name); }
void VAO::bind() { glBindVertexArray(m_name); }
void VAO::unbind() { glBindVertexArray(0); }
void VAO::fill(GLuint index, GLint size, GLenum type,
GLboolean normalized, GLsizei stride,
const GLvoid *pointer)
{
void VAO::fill(GLuint index, GLint size, GLenum type, GLboolean normalized,
GLsizei stride, const GLvoid *pointer) {
glEnableVertexAttribArray(index);
glVertexAttribPointer(index, size, type, normalized, stride, pointer);
}
}
Shader::Shader() : m_program(0) { }
Shader::Shader() : m_program(0) {}
Shader::~Shader() {}
Shader::~Shader() {}
void Shader::init() { m_program = glCreateProgram(); }
void Shader::init() { m_program = glCreateProgram(); }
bool Shader::check() {
bool Shader::check() {
GLint len = 0;
GLint result = 0;
@ -35,18 +34,18 @@ namespace endofthejedi {
glGetProgramiv(m_program, GL_INFO_LOG_LENGTH, &len);
if (result == GL_FALSE) {
std::cout<<"getting error log:" << std::endl;
char *error = (char *)malloc(len+1);
std::cout << "getting error log:" << std::endl;
char *error = (char *)malloc(len + 1);
glGetProgramInfoLog(m_program, len, NULL, error);
std::string str(error);
std::cout << str << std::endl;
}
//std::cout << "checked program" << std::endl;
// std::cout << "checked program" << std::endl;
return (bool)result;
}
}
bool Shader::checkShader(GLuint shader) {
bool Shader::checkShader(GLuint shader) {
GLint len = 0;
GLint result = 0;
@ -54,19 +53,18 @@ namespace endofthejedi {
if (result == GL_FALSE) {
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
char *error = (char *)malloc(len+1);
char *error = (char *)malloc(len + 1);
glGetShaderInfoLog(shader, len, NULL, error);
std::string str(error, error + len);
std::cout << str << std::endl;
}
//std::cout << "checked shader" << std::endl;
// std::cout << "checked shader" << std::endl;
return result != GL_FALSE;
}
}
void Shader::bind()
{
void Shader::bind() {
if (m_program == 0) {
std::cerr << "error: invalid to bind invalid program (0)! "
"use unbind() if that was your purpose!" << std::endl;
@ -76,15 +74,16 @@ namespace endofthejedi {
}
glUseProgram(m_program);
}
}
GLuint Shader::program() { return m_program; }
GLuint Shader::program() { return m_program; }
void Shader::unbind() { glUseProgram(0); }
void Shader::unbind() { glUseProgram(0); }
void Shader::load(const std::string &data, GLenum shadertype) {
void Shader::load(const std::string &data, GLenum shadertype) {
if (m_program == 0) {
std::cout<<"[shader] error: shader program is invalid (0)!" << std::endl;
std::cout << "[shader] error: shader program is invalid (0)!"
<< std::endl;
exit(-1);
return;
}
@ -101,41 +100,92 @@ namespace endofthejedi {
check();
glDeleteShader(shader);
}
}
void Shader::loadFile(const std::string &path, GLenum shadertype) {
void Shader::loadFile(const std::string &path, GLenum shadertype) {
std::string content;
std::ifstream fileStream(path, std::ios::in);
if(!fileStream.is_open()) {
std::cerr << "Could not read file " << path << ". File does not exist." << std::endl;
if (!fileStream.is_open()) {
std::cerr << "Could not read file " << path << ". File does not exist."
<< std::endl;
return;
}
std::string line = "";
while(!fileStream.eof()) {
while (!fileStream.eof()) {
std::getline(fileStream, line);
content.append(line + "\n");
}
fileStream.close();
load(content, shadertype);
}
GLuint Shader::location(const std::string &name) {
return glGetUniformLocation(m_program, name.c_str());
}
}
void printGlError(GLenum err)
{
GLuint Shader::location(const std::string &name) {
return glGetUniformLocation(m_program, name.c_str());
}
Texture::Texture() {}
Texture::~Texture() { glDeleteTextures(1, &m_name); }
void Texture::init() { glGenTextures(1, &m_name); }
void Texture::bind() { glBindTexture(GL_TEXTURE_2D, m_name); }
void Texture::unbind() { glBindTexture(GL_TEXTURE_2D, 0); }
void Texture::fill(GLint level, GLint internalFormat, GLsizei width,
GLsizei height, GLint border, GLenum format, GLenum type,
const GLvoid *data) {
bind();
glTexImage2D(GL_TEXTURE_2D, level, internalFormat, width, height, border,
format, type, data);
}
GLuint Texture::getName() { return m_name; }
Framebuffer::Framebuffer() {}
Framebuffer::~Framebuffer() { glDeleteFramebuffers(1, &m_name); }
void Framebuffer::init() { glGenFramebuffers(1, &m_name); }
void Framebuffer::bind() { glBindFramebuffer(GL_FRAMEBUFFER, m_name); }
void Framebuffer::unbind() { glBindFramebuffer(GL_FRAMEBUFFER, 0); }
void Framebuffer::attachRenderbuffer(GLenum attachment, GLuint rbo) {
bind();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, rbo);
}
void Framebuffer::attachTexture(GLenum attachment, GLuint tex) {
bind();
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex, 0);
}
Renderbuffer::Renderbuffer() {}
Renderbuffer::~Renderbuffer() { glDeleteRenderbuffers(1, &m_name); }
void Renderbuffer::init() { glGenRenderbuffers(1, &m_name); }
void Renderbuffer::bind() { glBindRenderbuffer(GL_RENDERBUFFER, m_name); }
void Renderbuffer::unbind() { glBindRenderbuffer(GL_RENDERBUFFER, 0); }
void Renderbuffer::create(GLenum internalformat, GLsizei width,
GLsizei height) {
bind();
glRenderbufferStorage(GL_RENDERBUFFER, internalformat, width, height);
}
}
void printGlError(GLenum err) {
if (err != GL_NO_ERROR) {
std::cout << "opengl error is: " << stringFromGlError(err) << std::endl;
}
}
void discardLastGlError(bool print)
{
void discardLastGlError(bool print) {
GLenum err = glGetError();
if (print) {
printGlError(err);
@ -143,8 +193,7 @@ void discardLastGlError(bool print)
}
// return false if there's an error
bool checkAndPrintGlError()
{
bool checkAndPrintGlError() {
GLenum err = glGetError();
printGlError(err);
@ -152,15 +201,25 @@ bool checkAndPrintGlError()
return true;
}
const char *stringFromGlError(GLenum err)
{
switch(err) {
case GL_INVALID_ENUM: return "GL_INVALID_ENUM"; break;
case GL_INVALID_VALUE: return "GL_INVALID_VALUE"; break;
case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION"; break;
case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY"; break;
case GL_INVALID_FRAMEBUFFER_OPERATION: return "GL_INVALID_FRAMEBUFFER_OPERATION"; break;
default: return "<unknown>"; break;
const char *stringFromGlError(GLenum err) {
switch (err) {
case GL_INVALID_ENUM:
return "GL_INVALID_ENUM";
break;
case GL_INVALID_VALUE:
return "GL_INVALID_VALUE";
break;
case GL_INVALID_OPERATION:
return "GL_INVALID_OPERATION";
break;
case GL_OUT_OF_MEMORY:
return "GL_OUT_OF_MEMORY";
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
return "GL_INVALID_FRAMEBUFFER_OPERATION";
break;
default:
return "<unknown>";
break;
}
}

View file

@ -16,6 +16,7 @@ template <GLenum T> class BufferObject {
public:
BufferObject();
~BufferObject();
void init();
void bind();
void bind(GLuint index, GLintptr offset = 0, GLsizeiptr size = 0);
void fill(GLenum usage, GLsizei size = 0, GLvoid *data = NULL);
@ -35,6 +36,7 @@ class VAO {
public:
VAO();
~VAO();
void init();
void bind();
void unbind();
void fill(GLuint index, GLint size, GLenum type, GLboolean normalized,
@ -57,20 +59,67 @@ class Shader {
void bind();
void unbind();
void load(const std::string &data, GLenum shadertype);
void loadFile(const std::string& data, GLenum shadertype);
void loadFile(const std::string &data, GLenum shadertype);
GLuint location(const std::string &name);
GLuint program();
};
class Texture {
private:
GLuint m_name;
protected:
public:
Texture();
~Texture();
void init();
void bind();
void unbind();
void fill(GLint level, GLint internalFormat, GLsizei width, GLsizei height,
GLint border, GLenum format, GLenum type, const GLvoid *data);
GLuint getName();
};
class Framebuffer {
private:
GLuint m_name;
protected:
public:
Framebuffer();
~Framebuffer();
void init();
void bind();
void unbind();
void attachRenderbuffer(GLenum attachment, GLuint rbo);
void attachTexture(GLenum attachment, GLuint tex);
};
class Renderbuffer {
private:
GLuint m_name;
protected:
public:
Renderbuffer();
~Renderbuffer();
void init();
void bind();
void unbind();
void create(GLenum internalformat, GLsizei width, GLsizei height);
};
}
#define TBufferObject_(pre, post) \
template <GLenum T> pre endofthejedi::BufferObject<T>::post
#define TBufferObject(...) TBufferObject_(__VA_ARGS__)
TBufferObject(, BufferObject)() { glGenBuffers(1, &m_name); }
TBufferObject(, BufferObject)() {}
TBufferObject(, ~BufferObject)() { glDeleteBuffers(1, &m_name); }
TBufferObject(void, init)() { glGenBuffers(1, &m_name); }
TBufferObject(void, bind)() { glBindBuffer(T, m_name); }
TBufferObject(void, bind)(GLuint index, GLintptr offset, GLsizeiptr size) {
@ -94,7 +143,7 @@ TBufferObject(void, unmap)() {
// todo
}
void discardLastGlError(bool print=false);
void discardLastGlError(bool print = false);
// return false if there's an error
bool checkAndPrintGlError();
@ -102,4 +151,3 @@ bool checkAndPrintGlError();
void printGlError(GLenum err);
const char *stringFromGlError(GLenum err);

View file

@ -10,6 +10,11 @@
#include "network/server.hpp"
#include "options.hpp"
#include "sound/sound.hpp"
#include "sound/sound_effects.hpp"
#include "state/state_update_event.hpp"
#include <sys/time.h>
uint64_t optionsFlags;
@ -19,6 +24,7 @@ using asio::ip::tcp;
int main(int argc, char *argv[])
{
bool devMode = false;
bool soundEnabled = false;
char port[]="3490";
static struct option long_options[] =
@ -33,6 +39,7 @@ int main(int argc, char *argv[])
// {"delete", required_argument, 0, 'd'},
{"autorun", required_argument, 0, 'a'},
{"port", required_argument, 0, 'p'},
{"sound", no_argument, 0, 's'},
{"fps", no_argument, 0, 'f'},
{"dev", no_argument, 0, 'd'},
{0, 0, 0, 0}
@ -41,7 +48,7 @@ int main(int argc, char *argv[])
int option_index = 0;
while(1){
char c = getopt_long (argc, argv, "p:fad",
char c = getopt_long (argc, argv, "p:fads",
long_options, &option_index);
if (c == -1)
break;
@ -51,6 +58,7 @@ int main(int argc, char *argv[])
case 'f':
SET_FLAG(SHOW_FPS,true);
break;
case 'd':
std::cout<<"enabling developer mode" << std::endl;
devMode = true;
@ -60,6 +68,10 @@ int main(int argc, char *argv[])
strcpy(port,optarg);
break;
case 's':
soundEnabled = true;
break;
case 'a':
SET_FLAG(TEST_AUTORUN, true);
break;
@ -75,6 +87,13 @@ int main(int argc, char *argv[])
srand(time(NULL));
sound::SoundEffects *sounds = nullptr;
if (soundEnabled) {
if (sound::initSound()) {
sounds = new sound::SoundEffects();
}
}
Game game;
game.state()->setDeveloperMode(devMode);
@ -89,6 +108,23 @@ int main(int argc, char *argv[])
while(window.running()){
window.poll();
io_service.poll();
//size_t numEvents = game.state()->currentStateUpdateEvents().size();
//if (numEvents != 0) {
// std::cout<<"game state update events: " << numEvents << std::endl;
// for (game::StateUpdateEvent *evt : game.state()->currentStateUpdateEvents()) {
// std::cout<< evt->description() << std::endl;
// }
//}
if (sounds != nullptr) {
// TODO: get time diff too
sounds->advance(1/50.0f, game.state()->currentStateUpdateEvents());
// TODO: use flag to now when to do this.
sound::deleteOldSounds();
}
game.state()->applyAndClearAllOldStateUpdates();
}
return 0;

View file

@ -3,11 +3,10 @@
#include "session.hpp"
#include "game.hpp"
#include "state/commands.hpp"
#include "util.hpp"
#include <iostream>
#include <sstream>
void trim(std::string &str)
{
const std::string ws = " \t\r\n";
@ -28,13 +27,10 @@ bool Session::parse(std::string s)
// strip leading/trailing whitespace/newlines as they are always unintional
trim(s);
std::istringstream iss(s);
std::string token;
iss >> token;
std::string rest;
// skip token + next whitespace
std::string rest = s.substr(std::min(s.size(), token.size()+1));
util::splitIntoTokenAndRest(s, token, rest);
//std::cout<<"[session] token='" << token << "' rest='" << rest << "'" << std::endl;

View file

@ -33,5 +33,5 @@ private:
char m_rcv_data[max_length];
game::State* m_state;
bool m_started = false;
int m_pid;
size_t m_pid;
};

View file

@ -99,8 +99,9 @@ namespace endofthejedi {
m_width = attribs.width;
m_height = attribs.height;
resize();
} else if (event.type == ClientMessage) {
if (event.xclient.data.l[0] == m_atomWmDeleteWindow) {
if (event.xclient.data.l[0] == (int) m_atomWmDeleteWindow) {
stop();
}
} else if (event.type == DestroyNotify) {

View file

@ -5,6 +5,8 @@
#include <epoxy/gl.h>
#include <epoxy/glx.h>
#include <glm/gtc/matrix_transform.hpp>
#include "game.hpp"
namespace endofthejedi {
@ -21,6 +23,9 @@ namespace endofthejedi {
public:
virtual void setup() { }
virtual void render(const game::State *state) = 0;
virtual void setWindowSize(int px, int py) { (void) px; (void) py; }
virtual void setCameraMatrix(const glm::mat4 &cam) { (void) cam; }
};
}

View file

@ -0,0 +1,159 @@
#include "image_texture.hpp"
#include <png.h>
#include <cmath>
namespace endofthejedi {
bool ImageTexture::loadPng()
{
m_valid = false;
//header for testing if it is a png
png_byte header[8];
//open file as binary
FILE *fp = fopen(m_path.c_str(), "rb");
if (!fp) {
printf("[image_texture] warning: could not open png file to load: %s\n", m_path.c_str());
return false;
}
//read the header
fread(header, 1, 8, fp);
//test if png
int is_png = !png_sig_cmp(header, 0, 8);
if (!is_png) {
puts("warning: image loader: file is not PNG!");
fclose(fp);
return false;
}
//create png struct
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr) {
fclose(fp);
return false;
}
//create png info struct
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
fclose(fp);
return false;
}
//create png info struct
png_infop end_info = png_create_info_struct(png_ptr);
if (!end_info) {
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
fclose(fp);
return false;
}
//png error stuff, not sure libpng man suggests this.
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
fclose(fp);
return false;
}
//init png reading
png_init_io(png_ptr, fp);
//let libpng know you already read the first 8 bytes
png_set_sig_bytes(png_ptr, 8);
// read all the info up to the image data
png_read_info(png_ptr, info_ptr);
//variables to pass to get info
int bit_depth, color_type;
png_uint_32 twidth, theight;
// get info about png
png_get_IHDR(png_ptr, info_ptr, &twidth, &theight, &bit_depth, &color_type,
NULL, NULL, NULL);
//update width and height based on png info
m_size = glm::vec2(twidth, theight);
int pot_width = (int) pow(2, ceilf(log2f(m_size.x)));
int pot_height = (int) pow(2, ceilf(log2f(m_size.y)));
m_uvScale = m_size / glm::vec2(pot_width, pot_height);
/*puts("#########################################");*/
/*printf("# PNG: width=%d, height=%d\n", width, height);*/
/*printf("# powers of two: w=%d, h=%d\n", pot_width, pot_height);*/
/*puts("#########################################");*/
// Update the png info struct.
png_read_update_info(png_ptr, info_ptr);
// Row size in bytes.
int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
int pot_rowbytes = 4 * (int) pow(2, ceilf(log2f(rowbytes/4)));
/*printf("rowbytes: %d, pot_rowbytes=%d\n", rowbytes, pot_rowbytes);*/
// Allocate the image_data as a big block, to be given to opengl
png_byte *image_data = (png_byte *) calloc(pot_rowbytes * pot_height, sizeof(png_byte));
if (!image_data) {
//clean up memory and close stuff
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
fclose(fp);
return false;
}
//row_pointers is for pointing to image_data for reading the png with libpng
png_bytep *row_pointers = (png_bytep *) malloc(sizeof(png_bytep) * theight);
if (!row_pointers) {
//clean up memory and close stuff
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
free(image_data);
fclose(fp);
return false;
}
// set the individual row_pointers to point at the correct offsets of image_data
for (uint32_t i = 0; i < theight; ++i) {
row_pointers[theight- 1 - i] = image_data + i * pot_rowbytes;
}
//read the png into image_data through row_pointers
png_read_image(png_ptr, row_pointers);
//Now generate the OpenGL texture object
glGenTextures(1, &m_texture_id);
glBindTexture(GL_TEXTURE_2D, m_texture_id);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGBA,
pot_width,
pot_height,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
(GLvoid*) image_data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
// TODO: make selectable via switch!
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
//clean up memory and close stuff
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
free(image_data);
free(row_pointers);
fclose(fp);
m_valid = true;
return true;
}
}

View file

@ -0,0 +1,63 @@
#pragma once
#include "glclasses.hpp"
#include <glm/vec2.hpp>
// based on sources of knoc/chaqu
namespace endofthejedi {
/**
* @brief This sprite struct stores all information needed to render an
* arbitrary PNG texture to a quad.
*
* If the loaded texture dimensions have no powers of a bit more space is
* allocated and extra padding is added.
*/
class ImageTexture {
public:
ImageTexture(const std::string &filename) : m_path(filename), m_valid(false)
{
}
~ImageTexture()
{
}
/**
* @brief Try to load a PNG image from file into an opengl texture.
*
* @param path Path of PNG file
* @param texture The used texture id will be saved there.
* @param width Width of the loaded texture in pixels.
* @param height Height of the loaded texture in pixels.
* @param uv_scale_x Horizontal portion of the screen which is padded to get
* power-of-two texture.
* @param uv_scale_y Vertical portion of the screen which is padded to get
* power-of-two texture.
*
* Note: The current active texture unit should be bound (GL_TEXTURE0 etc...)
* before calling this
*
* @return true if loading suceeded, false on errors.
*/
bool loadPng();
bool valid() const { return m_valid; }
glm::vec2 size() const { return m_size; }
glm::vec2 uvScale() const { return m_uvScale; }
GLuint textureId() const { return m_texture_id; }
public:
std::string m_path; // path for the loaded texture is saved here
glm::vec2 m_size; // used width/height of the pixels of the texture
glm::vec2 m_uvScale; // factor to scale uv-coordinates to get rid of non-power-of-two padding
bool m_valid;
GLuint m_texture_id; // texture binding id
};
}

View file

@ -4,7 +4,7 @@
// TODO: use VAO's as soon as this is working
int getDivisorForIndex(int index)
int getDivisorForIndex(size_t index)
{
// 0 or 1?
return (index == 0) ? 0 : 3;
@ -15,52 +15,63 @@ namespace endofthejedi {
: m_id(id)
, m_numParticles(numParticles)
, m_particleRadius(particleSize)
, m_halfAge(halfAge)
, m_age(0.0)
, m_halfAge(halfAge)
// XXX this is used in some places in the shader.
// set it via uniform too
, m_maxNumHalfAges(5.0)
, m_maxVelocity(1.0)
, m_center(glm::vec3(0.0f, 0.0f, 0.0f))
// 2d quad drawn as a triangle fan
, m_data_quad({
1.0f, -1.0f,
1.0f, 1.0f,
-1.0f, 1.0f,
-1.0f, -1.0f})
// 2d quad drawn as a triangle fan.
//
// TODO
// it is transformed before uploading so it looks at the camera
, m_data_geometry({
0.5f, -0.5f,
0.5f, 0.5f,
-0.5f, 0.5f,
-0.5f, -0.5f})
{
m_num_vertex_buffers = 4;
//std::cout<<"[ParticleBatch] create for " << numParticles << " num particles" << std::endl;
m_attr_locations.resize(m_num_vertex_buffers);
m_data_vbos.resize(m_num_vertex_buffers);
m_data_position.resize(m_numParticles);
m_data_velocity.resize(m_numParticles);
m_data_kind.resize(m_numParticles);
m_data_max_age.resize(m_numParticles);
m_data_max_distance.resize(m_numParticles);
std::string vss_particles = "../data/shader/particle.vert";
std::string fss_particles = "../data/shader/particle.frag";
m_shader.init();
m_shader.loadFile(vss_particles, GL_VERTEX_SHADER);
m_shader.loadFile(fss_particles, GL_FRAGMENT_SHADER);
const char *names[] = {
"in_vertex",
"in_position",
"in_velocity",
"XXXunusedXXX",
"XXXunusedXXX"
};
for (int i=0; i<5; i++) {
const char *name = names[i];
GLint loc = glGetAttribLocation(m_shader.program(), name);
m_attr_locations[i] = loc;
//std::cout<<"attr location " << i << " " << loc << " " << name << std::endl;
for (size_t i=0; i<m_num_vertex_buffers; i++) {
m_attr_locations[i] = -1;
m_data_vbos[i] = 0;
}
}
ParticleBatch::~ParticleBatch()
{
// TODO: find out if stuff must be deallocated
glDeleteBuffers(5, m_data_vbos);
glDeleteBuffers(m_num_vertex_buffers, m_data_vbos.data());
}
void ParticleBatch::setup(Shader *shader)
{
static const char *names[] = {
"in_geometry",
"in_position",
"in_velocity",
"in_maxDist"
};
for (size_t i=0; i<m_num_vertex_buffers; i++) {
const char *name = names[i];
GLint loc = glGetAttribLocation(shader->program(), name);
m_attr_locations[i] = loc;
//std::cout<<"attr location for " << name << " (#" << i << ") is " << loc << std::endl;
}
}
void ParticleBatch::setCenter(const glm::vec3 &center)
@ -73,7 +84,7 @@ namespace endofthejedi {
m_maxVelocity = maxVelocity;
}
void ParticleBatch::setParticle(size_t index, const glm::vec3 &p, const glm::vec3 &v)
void ParticleBatch::setParticle(size_t index, const glm::vec3 &p, const glm::vec3 &v, float maxDist)
{
if (index >= m_numParticles) {
return;
@ -83,15 +94,14 @@ namespace endofthejedi {
m_data_position[index] = p;
m_data_velocity[index] = v;
m_data_kind[index] = 0.0;
m_data_max_age[index] = 0.0;
m_data_max_distance[index] = maxDist;
}
void ParticleBatch::bind()
{
//std::cout<<"[ParticleBatch] bind" << std::endl;
for (size_t i=0; i<5; i++) {
for (size_t i=0; i<m_num_vertex_buffers; i++) {
//std::cout<<"vbo #" << i << ": " << m_data_vbos[i] << std::endl;
glEnableVertexAttribArray(m_attr_locations[i]);
glBindBuffer(GL_ARRAY_BUFFER, m_data_vbos[i]);
@ -113,9 +123,9 @@ namespace endofthejedi {
{
//std::cout<<"[ParticleBatch] upload to vbo's " << std::endl;
glGenBuffers(5, m_data_vbos); // Generate buffer
glGenBuffers(m_num_vertex_buffers, m_data_vbos.data()); // Generate buffer
for (size_t i=0; i<5; i++) {
for (size_t i=0; i<m_num_vertex_buffers; i++) {
size_t bufferDataSize = dataSizeForIndex(i) * m_numParticles * sizeof(float);
glEnableVertexAttribArray(i);
@ -130,16 +140,14 @@ namespace endofthejedi {
}
}
void ParticleBatch::render()
void ParticleBatch::render(Shader *shader)
{
//std::cout<<"[ParticleBatch] render " << std::endl;
m_shader.bind();
glUniform1f(m_shader.location("age"), m_age);
glUniform1f(m_shader.location("maxVelocity"), m_maxVelocity);
glUniform1f(m_shader.location("halfAge"), m_halfAge);
glUniform1f(m_shader.location("size"), m_particleRadius);
glUniform3f(m_shader.location("explCenter"), m_center.x, m_center.y, m_center.z);
glUniform1f(shader->location("age"), m_age);
glUniform1f(shader->location("maxVelocity"), m_maxVelocity);
glUniform1f(shader->location("halfAge"), m_halfAge);
glUniform1f(shader->location("size"), m_particleRadius);
glUniform3f(shader->location("explCenter"), m_center.x, m_center.y, m_center.z);
bind();
@ -154,7 +162,7 @@ namespace endofthejedi {
#if 0
glBegin(GL_QUADS);
for (size_t index=0; index<m_numParticles; index++) {
for (int i=0; i<4; i++) {
for (size_t i=0; i<4; i++) {
glm::vec2 p = m_data_position[index] + triangles[i];
//glColor3f(1.0, 0.0, 0.0);
glVertex2f(p.x, p.y);
@ -165,9 +173,9 @@ namespace endofthejedi {
}
size_t ParticleBatch::dataSizeForIndex(int i)
size_t ParticleBatch::dataSizeForIndex(size_t index)
{
switch(i) {
switch(index) {
case 0:
return 2;
@ -176,25 +184,23 @@ namespace endofthejedi {
return 3;
case 3:
case 4:
return 1;
default:
std::cerr << "bad" << std::endl;
std::cerr << "dataSizeForIndex() bad" << std::endl;
exit(-1);
}
}
void *ParticleBatch::dataSourceForIndex(int i)
void *ParticleBatch::dataSourceForIndex(size_t index)
{
switch(i) {
case 0: return (void *) m_data_quad.data();
switch(index) {
case 0: return (void *) m_data_geometry.data();
case 1: return (void *) m_data_position.data();
case 2: return (void *) m_data_velocity.data();
case 3: return (void *) m_data_kind.data();
case 4: return (void *) m_data_max_age.data();
case 3: return (void *) m_data_max_distance.data();
default:
std::cerr << "bad" << std::endl;
std::cerr << "dataSourceForIndex() bad" << std::endl;
exit(-1);
}
}
@ -206,6 +212,6 @@ namespace endofthejedi {
bool ParticleBatch::done() const
{
return m_age >= 5.0*m_halfAge;
return m_age >= m_maxNumHalfAges*m_halfAge;
}
}

View file

@ -8,6 +8,39 @@
#include "glclasses.hpp"
namespace endofthejedi {
#if 0
class VertexAttribute {
public:
class Data {
public:
enum class Type {
Integer, Float, Float2, Float3, Float4, Matrix3, Matrix4
};
void setDataInteger(int index, int v)
{
}
Data(Type type, int size) : m_type(type), m_size(size)
{
if (type ==
}
private:
int m_size;
Type m_type;
std::vector<int> m_sourceInteger;
std::vector<float> m_sourceFloat;
};
public:
Data data;
std::string variableName;
int divisor;
GLint index;
};
#endif
class ParticleBatch {
public:
ParticleBatch(size_t id, size_t numParticles, float particleRadius, float halfAge);
@ -15,47 +48,58 @@ namespace endofthejedi {
// deallocate opengl stuff on destroy
~ParticleBatch();
// set particle data
void setParticle(size_t index, const glm::vec3 &p, const glm::vec3 &v, float maxDistance);
// stuff for setup/rendering usage
void setup(Shader *shader);
void bind();
void upload();
void render(Shader *shader);
// advance internal state (mostly age)
void tick(float dt);
// check if the lifetime is over and whether it can be deleted.
bool done() const;
// access attributes
size_t numParticles() const { return m_numParticles; }
float ageNormalized() const { return m_age / (m_maxNumHalfAges*m_halfAge); }
float age() const { return m_age; }
size_t id() const { return m_id; }
const glm::vec3 &explosionCenter() const { return m_center; }
void setParticle(size_t index, const glm::vec3 &p, const glm::vec3 &v);
// set attributes
void setCenter(const glm::vec3 &center);
void setMaxVelocity(float maxVelocity);
void bind();
void upload();
void render();
void tick(float dt);
bool done() const;
Shader *shader() { return &m_shader; }
size_t id() const { return m_id; }
private:
size_t dataSizeForIndex(int i);
void *dataSourceForIndex(int i);
private:
size_t dataSizeForIndex(size_t index);
void *dataSourceForIndex(size_t index);
private:
// id of explosion
size_t m_id;
// uniforms for the shader
size_t m_numParticles;
float m_particleRadius;
const float m_halfAge;
float m_age;
const float m_halfAge;
const float m_maxNumHalfAges;
float m_maxVelocity;
glm::vec3 m_center;
GLuint m_data_vbos[5];
// meta data
size_t m_num_vertex_buffers;
std::vector<GLuint> m_data_vbos;
std::vector<GLuint> m_attr_locations;
std::vector<float> m_data_quad;
// vertex attributes
std::vector<float> m_data_geometry;
std::vector<glm::vec3> m_data_position;
std::vector<glm::vec3> m_data_velocity;
std::vector<float> m_data_kind;
std::vector<float> m_data_max_age;
GLuint m_attr_locations[5];
Shader m_shader;
std::vector<float> m_data_max_distance;
};
}

View file

@ -2,10 +2,11 @@
#include <iostream>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/euler_angles.hpp>
#include <glm/gtc/random.hpp>
#include "state/events/explosion_event.hpp"
#include "state/events/missile_event.hpp"
#include "state/events/ship_event.hpp"
#include "developer_console.hpp"
namespace endofthejedi {
void RendererPolygon3d::setup()
@ -14,23 +15,96 @@ namespace endofthejedi {
std::cout<<"setup polygon 3d" << std::endl;
m_shader.init();
#if 0
std::string vss_simple = "../data/shader/simple.vert";
std::string fss_simple = "../data/shader/simple.frag";
m_shader.loadFile(vss_simple, GL_VERTEX_SHADER);
m_shader.loadFile(fss_simple, GL_FRAGMENT_SHADER);
#else
std::string vss_game_objects = "../data/shader/gameobjects.vert";
std::string fss_game_objects = "../data/shader/gameobjects.frag";
m_shader.loadFile(vss_game_objects, GL_VERTEX_SHADER);
m_shader.loadFile(fss_game_objects, GL_FRAGMENT_SHADER);
#endif
addModel("../data/mesh/small_atomic_bomb.stl", &m_missileModel);
addModel("../data/mesh/planet_12.stl", &m_planetModel);
m_shader_game_objects.init();
m_shader_game_objects.loadFile(vss_game_objects, GL_VERTEX_SHADER);
m_shader_game_objects.loadFile(fss_game_objects, GL_FRAGMENT_SHADER);
std::string vss_particles = "../data/shader/particle.vert";
std::string fss_particles = "../data/shader/particle.frag";
m_shader_particles.init();
m_shader_particles.loadFile(vss_particles, GL_VERTEX_SHADER);
m_shader_particles.loadFile(fss_particles, GL_FRAGMENT_SHADER);
std::string vss_background = "../data/shader/background.vert";
std::string fss_background = "../data/shader/background.frag";
m_shader_background.init();
m_shader_background.loadFile(vss_background, GL_VERTEX_SHADER);
m_shader_background.loadFile(fss_background, GL_FRAGMENT_SHADER);
//addModel("../data/mesh/small_atomic_bomb.stl", &m_missileModel);
addModel("../data/mesh/rocket.stl", &m_missileModel);
addModel("../data/mesh/planet_128.stl", &m_planetModel);
addModel("../data/mesh/ship_ufo.stl", &m_shipModel);
m_texture = nullptr;
m_backgroundTexturePath = "../data/img/test.png";
loadBackgroundTexture();
developer::DeveloperConsole::instance().addCallback("reload_bg", [=](const std::string &)
{
loadBackgroundTexture();
return developer::resultOkay();
});
developer::DeveloperConsole::instance().addCallback("set_bg", [=](const std::string &str)
{
std::string token;
std::string rest;
util::splitIntoTokenAndRest(str, token, rest);
m_backgroundTexturePath = token;
loadBackgroundTexture();
return developer::resultOkay();
});
}
void RendererPolygon3d::loadBackgroundTexture()
{
//m_texture = new ImageTexture("../data/img/test.png");
if (m_texture != nullptr) {
delete(m_texture);
}
//"../data/img/stars_nebular.png");
m_texture = new ImageTexture(m_backgroundTexturePath);
glActiveTexture(GL_TEXTURE0);
m_texture->loadPng();
std::cout<<"texture loading: " << m_texture->valid() << std::endl;
if (!m_texture->valid()) {
std::cout<<"loading failed!";
return;
//exit(-1);
}
glm::vec2 s = m_texture->size();
std::cout<<"texture size is " << s.x << " X " << s.y << std::endl;
}
void RendererPolygon3d::renderBackgroundImage()
{
m_shader_background.bind();
glUniform2fv(m_shader_background.location("uvScale"), 1, glm::value_ptr(m_texture->uvScale()));
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, m_texture->textureId());
//drawNdcQuad();
glColor3f(1.0, 0.0, 0.0);
glBegin(GL_QUADS);
glVertex2f(-1.0f, -1.0f);
glVertex2f(1.0f, -1.0f);
glVertex2f(1.0f, 1.0f);
glVertex2f(-1.0f, 1.0f);
glEnd();
}
void RendererPolygon3d::render(const game::State *state)
@ -52,20 +126,28 @@ namespace endofthejedi {
// TODO: add little rocks flying around
glClearColor(0.0, 0.0, 0.0, 1.0);
//float s = 0.1;
//glClearColor(s, s, s, 1.0);
m_shader.bind();
renderBackgroundImage();
//float s = 0.1;
//glClearColor(s, s, 1.2*s, 1.0);
m_shader_game_objects.bind();
// TODO: add ONE sun planet
// TODO: add lights for explosions
configureLightningInShader();
configureLightningInShader(&m_shader_game_objects);
//std::cout<<"setting aspect ratio: " << m_aspectRatio << std::endl;
glUniform1f(m_shader_game_objects.location("aspectRatio"), m_aspectRatio);
renderPlanets();
renderShips();
renderMissiles();
renderParticles();
renderTraces();
//glColor3f(1.0, 0.0, 0.0);
@ -81,17 +163,24 @@ namespace endofthejedi {
void RendererPolygon3d::renderParticles()
{
m_shader_particles.bind();
glUniform1f(m_shader_particles.location("aspectRatio"), m_aspectRatio);
for (ParticleBatch *batch : m_particles) {
batch->bind();
batch->render();
batch->render(&m_shader_particles);
}
}
void RendererPolygon3d::addExplosionEffect(size_t id, const glm::vec2 &pos, size_t n, float duration)
void RendererPolygon3d::addExplosionEffect(
size_t id, const glm::vec2 &explCenter, const glm::vec2 &missileVelocity,
bool isPlanetHit,
size_t n, float duration)
{
//float particleRadius = 0.005;
//float particleRadius = 0.003;
float particleRadius = 0.005;
float particleRadius = 0.02;
// TODO: use this as shader input too and make the area 2x around this
// so that it stays hot/yellow for 2/3 of the time
@ -99,7 +188,8 @@ namespace endofthejedi {
float maxVelocity = 0.4f;
ParticleBatch *batch = new ParticleBatch(id, n, particleRadius, duration);
batch->setCenter(glm::vec3(pos, 0.0));
batch->setup(&m_shader_particles);
batch->setCenter(glm::vec3(explCenter, 0.0));
batch->setMaxVelocity(maxVelocity);
for (size_t i=0; i<n; i++) {
@ -113,10 +203,123 @@ namespace endofthejedi {
// especially in 3d this would look bad without 3d velocity vector.
//glm::vec3 v = 0.5f*glm::vec3(sin(t), cos(t), util::randf_m1_1());
glm::vec3 v = glm::ballRand(maxVelocity);
v *= util::randf_0_1() * util::randf_0_1() * util::randf_0_1();
glm::vec3 pos = glm::vec3(explCenter, 0.0) + glm::ballRand(explCoreSize);
batch->setParticle(i, glm::vec3(pos, 0.0) + glm::ballRand(explCoreSize), v);
glm::vec3 v = glm::ballRand(maxVelocity);
// TODO: is that good?
if (isPlanetHit) {
v *= util::randf_0_1() * util::randf_0_1() * util::randf_0_1();
} else {
v *= util::randf_0_1();
}
// find collisions with planetns and limit max distance so particles
// won't fly through planets
//float maxDist = 0.1;
//float maxDist = 0.1*util::randf_0_1();
bool isInsidePlanet = false;
float maxParticleDist = INFINITY;
const game::Planet *nearestPlanet = nullptr;
for (const game::Planet *planet : m_state->planets) {
const glm::vec3 ppos3 = glm::vec3(planet->position, 0.0f);
// TODO: that's slightly wrong. use intersection for this.
float dist = glm::distance(ppos3, pos);
if (dist <= planet->radius) {
isInsidePlanet = true;
nearestPlanet = planet;
}
if (isInsidePlanet) {
// skip searching for nearer planets once we are inside some
// planet as the position/velocity will be changed to start
// at the surface of the planet we were in with
// reflected or planet-normal velocity.
continue;
}
// TODO: if inside, move position so that it looks like
// reflecting the particle from the planet
bool fliesInPlanetDirection = glm::dot(v, ppos3-pos) > 0.0f;
if (dist < maxParticleDist && fliesInPlanetDirection) {
nearestPlanet = planet;
maxParticleDist = dist;
}
}
bool makeStationary = false;
if (isInsidePlanet && isPlanetHit) {
util::IntersectionTest intersect;
if (!intersect.raySphere(
glm::vec3(explCenter, 0.0f), v,
glm::vec3(nearestPlanet->position, 0.0f), nearestPlanet->radius))
{
makeStationary = true;
//std::cout<<"warning: intersection should be valid!" << std::endl;
// TODO: must be as they lie on a plane and the dist is < as
// the radius.
// handle if this is wrong.
} else {
// simple reflection, ignoring the missile velocity: this looks good enough
(void) missileVelocity;
glm::vec3 planetNormal = glm::normalize(pos - glm::vec3(nearestPlanet->position, 0.0f));
v = glm::length(v) * planetNormal;
// TODO
// considering the missile velocity is not yet working:
// set position to the intersection point between explosion
// center and planet surface
//pos = intersect.pointAtDistance(intersect.distance());
//v = glm::vec3(missileVelocity, 0.0f);
//v = v - 2.0f*glm::dot(v, planetNormal) * planetNormal;
//v *= 4.0;
//maxParticleDist = 100.0;
//v = -v;
//pos = glm::vec3(nearestPlanet->position, 0.0f) + nearestPlanet->radius*planetNormal;
// set position to the intersection point between explosion
// center and planet surface
//const glm::vec3 planetNormal = glm::vec3(glm::normalize(explCenter - nearestPlanet->position), 0.0f);
//pos = glm::vec3(nearestPlanet->position, 0.0f) + nearestPlanet->radius*planetNormal;
// build new velocity by reflecting the old velocity on the
// planet normal
// TODO: add a bit random
// TODO: add reflection
// TODO: distribute particles around main reflection angle and
// TODO: add material exhaust that is specific for the planet.
// TODO: spawn waves on water planet
// TODO: start fire on gas planet
//v = glm::length(v) * planetNormal;
//v = v - 2.0f*glm::dot(v, planetNormal) * planetNormal;
//v = glm::length(v) * planetNormal;
//glm::vec3 r = v - 2.0f*glm::dot(v, planetNormal) * planetNormal;
//glm::vec3 vn = glm::length(v) * planetNormal;
//v = (r+vn) / 2.0f;
//glm::vec3 vc = glm::vec3(nearestPlanet->position-explCenter, 0.0f);
//glm::vec3 r = vc - 2.0f*glm::dot(vc, planetNormal) * planetNormal;
//v = r;
}
} else if (isInsidePlanet && !isPlanetHit) {
// if a planet is just hit by explosions particles but not the
// missile itself, don't reflect the particles in the planet.
// just set them as stationary at place of explosion
makeStationary = true;
}
if (makeStationary) {
v = glm::vec3(0.0f, 0.0f, 0.0f);
pos = glm::vec3(explCenter, 0.0f);;
}
batch->setParticle(i, pos, v, maxParticleDist);
}
batch->upload();
@ -126,6 +329,7 @@ namespace endofthejedi {
void RendererPolygon3d::advanceGraphicObjects(float dt)
{
#if 0
for (const game::Explosion *expl : m_state->explosions) {
bool gotIt = false;
for (ParticleBatch *batch : m_particles) {
@ -136,12 +340,61 @@ namespace endofthejedi {
}
if (!gotIt) {
addExplosionEffect(expl->id, expl->position, 1000, 1.0);
addExplosionEffect(
expl->id, expl->position,
expl->missileVelocity,
(expl->hit == game::Hit::Planet),
1000, 1.0);
}
}
#endif
for (auto *evt : m_state->currentStateUpdateEvents()) {
auto type = evt->eventType();
auto cycle = evt->lifeCycle();
if (type == game::EventType::Explosion) {
if (cycle == game::LifeCycle::Create) {
game::ExplosionEvent *ee = static_cast<game::ExplosionEvent*>(evt);
game::Explosion *expl = static_cast<game::Explosion*>(ee->object());
addExplosionEffect(
expl->id, expl->position,
expl->missileVelocity,
(expl->hit == game::Hit::Planet),
1000, 1.0);
}
} else if (type == game::EventType::Ship) {
game::ShipEvent *me = static_cast<game::ShipEvent*>(evt);
game::Ship *ship = static_cast<game::Ship*>(me->object());
// is always modificated
if (cycle == game::LifeCycle::Create) {
//std::cout<<"[renderer] adding missile #" << missile->id << std::endl;
m_ships.push_back(ship);
} else if (cycle == game::LifeCycle::Destroy) {
//std::cout<<"[renderer] removing missile #" << missile->id << std::endl;
m_ships.remove(ship);
}
} else if (type == game::EventType::Missile) {
game::MissileEvent *me = static_cast<game::MissileEvent*>(evt);
game::Missile *missile = static_cast<game::Missile*>(me->object());
// is always modificated
if (cycle == game::LifeCycle::Create) {
//std::cout<<"[renderer] adding missile #" << missile->id << std::endl;
m_missiles.push_back(missile);
} else if (cycle == game::LifeCycle::Destroy) {
//std::cout<<"[renderer] removing missile #" << missile->id << std::endl;
m_missiles.remove(missile);
}
}
}
//if (m_particles.size() == 0) {
// addExplosionEffect(0, glm::vec2(0.0, 0.0), 1000, 2.0);
// addExplosionEffect(0, glm::vec2(0.0, 0.0), glm::vec2(0.0, 0.0), false, 10000, 2.0);
//}
std::vector<ParticleBatch*> rm;
@ -167,12 +420,12 @@ namespace endofthejedi {
// too (same for missiles)
for (const game::Planet *planet : m_state->planets) {
glm::mat4 model = computeModelMatrix(planet);
glUniformMatrix4fv(m_shader.location("model"), 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(m_shader_game_objects.location("model"), 1, GL_FALSE, glm::value_ptr(model));
glm::vec3 c = planet->getColor();
glUniform3f(m_shader.location("materialColor"), c.x, c.y, c.z);
glUniform1i(m_shader.location("materialSeed"), planet->seed);
glUniform1i(m_shader.location("materialKind"), (int) planet->material);
glUniform3f(m_shader_game_objects.location("materialColor"), c.x, c.y, c.z);
glUniform1i(m_shader_game_objects.location("materialSeed"), planet->seed);
glUniform1i(m_shader_game_objects.location("materialKind"), (int) planet->material);
m_planetModel->render();
}
@ -180,31 +433,33 @@ namespace endofthejedi {
void RendererPolygon3d::renderMissiles()
{
// TODO: add fire trail for missiles near the sun
m_missileModel->bind();
for (const game::Player *player : m_state->players) {
for (const game::Missile *missile : player->missiles) {
for (const game::Missile *missile : m_missiles) {
glm::vec3 c = glm::vec3(1.0, 1.0, 0.3);
glUniform3f(m_shader.location("materialColor"), c.x, c.y, c.z);
glUniform3f(m_shader_game_objects.location("materialColor"), c.x, c.y, c.z);
// TODO: rename functions so their name represents what args they
// take
glm::mat4 model = computeModelMatrix(missile);
glUniformMatrix4fv(m_shader.location("model"), 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(m_shader_game_objects.location("model"), 1, GL_FALSE, glm::value_ptr(model));
m_missileModel->render();
}
}
}
void RendererPolygon3d::renderShips()
{
m_shipModel->bind();
for (const game::Ship *ship : m_state->ships) {
for (const game::Ship *ship : m_ships) {
glm::mat4 model = computeModelMatrix(ship);
glUniformMatrix4fv(m_shader.location("model"), 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(m_shader_game_objects.location("model"), 1, GL_FALSE, glm::value_ptr(model));
glm::vec3 c = glm::vec3(0.1, 1.0, 0.2);
glUniform3f(m_shader.location("materialColor"), c.x, c.y, c.z);
glUniform3f(m_shader_game_objects.location("materialColor"), c.x, c.y, c.z);
m_shipModel->render();
}
@ -220,7 +475,7 @@ namespace endofthejedi {
exit(-1);
}
(*dest)->setup(&m_shader);
(*dest)->setup(&m_shader_game_objects);
(*dest)->uploadToOpenGl();
m_models.push_back(*dest);
@ -236,9 +491,17 @@ namespace endofthejedi {
glm::vec2 vn = glm::normalize(missile->velocity);
float a = std::atan2(vn.y, vn.x);
glm::mat4 mat = computeModelMatrix(missile->position, 0.1f, a);
// TODO: which visual size has the rocket? in game its just a point with
// no size because all others have size.
return computeModelMatrix(missile->position, 0.03f, a);
//for atomic bomb
//return computeModelMatrix(missile->position, 0.03f, a);
// flipped too
mat = glm::rotate(mat, (float) M_PI/2.0f, glm::vec3(0.0f, 1.0f, 0.0f));
return mat;
}
glm::mat4 RendererPolygon3d::computeModelMatrix(const game::Ship *ship)
@ -293,7 +556,7 @@ namespace endofthejedi {
glPolygonMode(GL_FRONT, GL_FILL);
}
void RendererPolygon3d::configureLightningInShader()
void RendererPolygon3d::configureLightningInShader(Shader *shader) const
{
// TODO: add a few small lights for explosions so they lit the
// surroundsings
@ -310,7 +573,49 @@ namespace endofthejedi {
}
}
glUniform3f(m_shader.location("lightPosition"), p.x, p.y, p.z);
glUniform3f(m_shader.location("lightColor"), c.x, c.y, c.z);
glUniform3f(shader->location("lightPosition"), p.x, p.y, p.z);
glUniform3f(shader->location("lightColor"), c.x, c.y, c.z);
std::vector<glm::vec3> positions;
std::vector<float> intensities;
size_t numExplLights = 0;
for (ParticleBatch *batch : m_particles) {
float age = batch->ageNormalized();
// TODO: use function with a peak for this:
//
// /\__
// _/ \--____
float intensity = 1.0-age;
glm::vec3 p = batch->explosionCenter();
intensities.push_back(intensity);
positions.push_back(p);
numExplLights++;
if (numExplLights == 10) {
break;
}
}
glUniform1i( shader->location("explLightsNum"), numExplLights);
if (numExplLights != 0) {
glUniform3fv(shader->location("explLightsPos"), numExplLights, glm::value_ptr(positions[0]));
glUniform1fv(shader->location("explLightsIntensities"), numExplLights, intensities.data());
}
}
void RendererPolygon3d::setWindowSize(int px, int py)
{
m_aspectRatio = (float) px / (float) py;
}
void RendererPolygon3d::setCameraMatrix(const glm::mat4 &cam)
{
(void) cam;
}
}

View file

@ -2,6 +2,13 @@
#include <list>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/euler_angles.hpp>
#include <glm/gtc/random.hpp>
#include <glm/vec3.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "glclasses.hpp"
#include "game.hpp"
@ -16,9 +23,7 @@
#include "particle_batch.hpp"
#include "polygon_model.hpp"
#include <glm/vec3.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "image_texture.hpp"
namespace endofthejedi {
@ -27,6 +32,9 @@ namespace endofthejedi {
void setup();
void render(const game::State *state) override;
void setWindowSize(int px, int py);
void setCameraMatrix(const glm::mat4 &cam);
private:
void renderPlanets();
void renderMissiles();
@ -35,7 +43,10 @@ namespace endofthejedi {
void addModel(const std::string &filename, PolygonModel **dest);
void addExplosionEffect(size_t id, const glm::vec2 &pos, size_t n, float duration);
void addExplosionEffect(
size_t id, const glm::vec2 &pos, const glm::vec2 &missileVelocity,
bool hitPlanet,
size_t n, float duration);
void advanceGraphicObjects(float dt);
@ -49,7 +60,11 @@ namespace endofthejedi {
void renderTraces();
void configureLightningInShader();
void configureLightningInShader(Shader *shader) const;
void renderBackgroundImage();
void loadBackgroundTexture();
private:
// all models are also here (for easy reloading etc.)
@ -61,7 +76,9 @@ namespace endofthejedi {
PolygonModel *m_shipModel;
// for rendering everything
Shader m_shader;
Shader m_shader_background;
Shader m_shader_game_objects;
Shader m_shader_particles;
// for accessing
const game::State *m_state;
@ -70,5 +87,14 @@ namespace endofthejedi {
// time value for last rendering cycle (-1 after setup/startup)
float m_lastTime;
float m_aspectRatio;
// TODO: put representation specialized for rendering in here
std::list<const game::Missile*> m_missiles;
std::list<const game::Ship*> m_ships;
std::string m_backgroundTexturePath;
ImageTexture *m_texture;
};
}

131
game/sound/dummy_sound.cpp Normal file
View file

@ -0,0 +1,131 @@
#include "sound.hpp"
namespace sound {
bool initSound(void)
{
return false;
}
void deleteOldSounds()
{
}
SoundHandle *playSound(enum Sound type, float amplitude)
{
(void) type;
(void) amplitude;
return NULL;
}
SoundHandle *playFrequency(float freq, float amplitude)
{
(void) freq;
(void) amplitude;
return NULL;
}
bool configureEnvelope(SoundHandle *handle, float rise, float hold, float decay)
{
(void) handle;
(void) rise;
(void) hold;
(void) decay;
return false;
}
bool configureOvershoot(SoundHandle *handle, float relativeOvershootTime)
{
(void) handle;
(void) relativeOvershootTime;
return false;
}
bool configureEnvelopeWait(SoundHandle *handle, float rise, float hold, float decay, float wait)
{
(void) handle;
(void) rise;
(void) hold;
(void) decay;
(void) wait;
return false;
}
bool configureEnvelopeLooping(SoundHandle *handle, float rise, float hold, float decay, float wait)
{
(void) handle;
(void) rise;
(void) hold;
(void) decay;
(void) wait;
return false;
}
float getRiseDuration(SoundHandle *handle)
{
(void) handle;
return 0.0f;
}
float getHoldDuration(SoundHandle *handle)
{
(void) handle;
return 0.0f;
}
float getDecayDuration(SoundHandle *handle)
{
(void) handle;
return 0.0f;
}
float getWaitTime(SoundHandle *handle)
{
(void) handle;
return 0.0f;
}
void configureWaitTime(SoundHandle *handle, float waitTime)
{
(void) handle;
(void) waitTime;
}
void setLoopCount(SoundHandle *handle, int numRepetitions)
{
(void) handle;
(void) numRepetitions;
}
bool stopSound(SoundHandle *handle)
{
(void) handle;
return true;
}
void stopAllSounds(void)
{
}
void teardownSound(void)
{
}
bool startDumpingWav(const char *filename)
{
(void) filename;
return false;
}
void stopDumpingWav(void)
{
}
int numActiveSounds(void)
{
return 0;
}
void deleteSound(SoundHandle *handle)
{
(void) handle;
}
}

1071
game/sound/sound.cpp Normal file

File diff suppressed because it is too large Load diff

196
game/sound/sound.hpp Normal file
View file

@ -0,0 +1,196 @@
#ifndef _SOUND_HPP_
#define _SOUND_HPP_
#include <stdlib.h>
#include <stdbool.h>
namespace sound {
// type of sound.
// the envelope settings modify each sound.
enum Sound {
SoundFrequency=0, // one frequency
SoundWhiteNoise=1, // all frequencies are equal
SoundBrownianNoise=2, // 1/f^2 noise
SoundPinkNoise=3, // 1/f noise
};
enum EnvelopeState {
New=0, // newly spawned
Wait, // ready but not yet started (waiting before starting or before repeating)
Rise, // increasing
BeginOvershoot,
EndOvershoot,
Hold, // staying at normal level
Decay, // deacying
Done, // done
};
enum SoundDecayType {
SoundDecayLinear = 0,
SoundDecayExp = 1
};
struct SoundHandle {
// just for debugging/logging. don't rely on use
const char *name;
// unique ID for accessing them via lookups etc. if sound is dead, this will be never reused
size_t uid;
// marked for deletion by the internal thread.
bool deleteFlag;
// is now deleted. when a new sound should be used, this can be reused
// and at the end deleteFlag and isDeleted should be cleared.
bool isDeleted;
// if true, don't deallocate once this is done.
bool keep_when_done;
// these are public and can be changed by the user.
enum EnvelopeState envelope;
/****************************************************************/
/* Special Parameters for specific types */
/****************************************************************/
/******************** SoundFrequency ****************************/
float freq; // base frequency for type: SoundFrequency
/******************** White / Brownian / Pink Noise ****************/
float leakage; // influence of previous state on current value (0..1)
float scaling; // influence of white noise on next value
// If true and the value goes outside (-1..1), do the step in the other
// direction to stay inside the valid range.
bool skipDirectionIfClamping;
/******************** SoundFrequency: ***************************/
/****************************************************************/
/* Common sound state / parameters */
/****************************************************************/
// progress of the sound which is used for the envelope but can be used for
// other things too
float time;
// scalar state that can be used arbitrarily to derive the waveform from it
float state;
// time when the sound is done
// if != 0.0, the sound changes with that
// TODO: make attack gain decay stuff as envelope
// TODO: add amplitude noise
float _riseTime;
float _relativeOvershootTime; // percentage of end of rise time that is used for overshoot (max amplitude at t_overshoot/2)
float _holdTime;
float _decayTime;
float _waitTime;
// waveform of type of rise. default: linear
enum SoundDecayType riseType;
// waveform of type of decay. default: linear
enum SoundDecayType decayType;
// if true, play infinitely
bool looping;
// base amplitude of the sound
float amplitude;
// amplitude when signal is at minimum (before rise, after release and in wait time)
float minAmplitude;
// these are al private!
// never acces them
enum Sound _type; // which type of sound
int _loopCount;
// if true, will get removed
bool _done;
bool _useEnvelope;
float *history; // if != 0, history is a pointer to 2*numHistorySamples samples.
size_t numHistorySamples; // if != 0, save history for this sound.
size_t lastHistorySample; // index/2 of last history sample. used
};
// initialize sound subsystem. return true if working, false on errors.
bool initSound(void);
// free sounds that are not used anymore.
// must be called regularily by the application
void deleteOldSounds();
// start playing a sound.
// args:
// type - type of sound.
// amplitude : 0..1
// handle: if != NULL will point to the internal sound handle to change the
// manipulate playing of the sound.
// TODO: add sound from file to play too
SoundHandle *playSound(enum Sound type, float amplitude);
// start playing a looping frequency.
// args:
// freq: frequency in Hz
SoundHandle *playFrequency(float freq, float amplitude);
// set them.
// args:
// time of phases
bool configureEnvelope(SoundHandle *handle, float rise, float hold, float decay);
bool configureOvershoot(SoundHandle *handle, float relativeOvershootTime);
// same as above but set to looping and set wait time between intervals
bool configureEnvelopeWait(SoundHandle *handle, float rise, float hold, float decay, float wait);
// without looping but with wait
bool configureEnvelopeLooping(SoundHandle *handle, float rise, float hold, float decay, float wait);
// getter for attributes. if 0.0, unused.
float getRiseDuration(SoundHandle *handle);
float getHoldDuration(SoundHandle *handle);
float getDecayDuration(SoundHandle *handle);
float getWaitTime(SoundHandle *handle);
void configureWaitTime(SoundHandle *handle, float waitTime);
// Set looping behaviour of the sound.
// args:
// / > 0, sound is looping for this number of repetitions
// numRepetitions { == 0, sound is stopped when it is over
// \ == -1, loop the sound forever
//
void setLoopCount(SoundHandle *handle, int numRepetitions);
// stop playing a sound. the handle is invalid now.
bool stopSound(SoundHandle *handle);
// stop and remove all playing sounds. all handle become invalid after calling
// this.
void stopAllSounds(void);
void teardownSound(void);
// for debugging
bool startDumpingWav(const char *filename);
// is deletede when 'keep' not set after stopping
void stopDumpingWav(void);
int numActiveSounds(void);
void deleteSound(SoundHandle *handle);
//SoundHandle *playFreqEnvelope(float freq, float rise, float hold, float decay);
// TODO: add fade out/fade in overlay stuff for stopping sounds smoothly
}
#endif

View file

@ -0,0 +1,83 @@
#include "sound_effects.hpp"
#include "sound.hpp"
#include "util.hpp"
namespace sound {
void SoundEffects::addSoundHandleForObject(game::Object *obj, SoundHandle *handle)
{
std::cout<<"add sound for object: " << obj->id << std::endl;
auto pair = std::pair<size_t, SoundHandle*>(obj->id, handle);
m_mapObjectToSoundHandle.insert(pair);
}
void SoundEffects::fadeOutSoundHandlesForObject(game::Object *obj, float fadeOutTime)
{
std::cout<<"deleting sounds for object: " << obj->id << std::endl;
std::multimap<size_t, SoundHandle*>::iterator it;
for (it = m_mapObjectToSoundHandle.find(obj->id); it != m_mapObjectToSoundHandle.end(); it++) {
std::cout<<"sound #" << it->second->uid << " / " << it->second->name << std::endl;
fadeOut(it->second, fadeOutTime);
}
size_t numRemoved = m_mapObjectToSoundHandle.erase(obj->id);
std::cout<<"removed total sounds: " << numRemoved << std::endl;
}
void SoundEffects::advance(float dt, const std::list<game::StateUpdateEvent*> &updates)
{
(void) dt;
for (const game::StateUpdateEvent *evt : updates) {
auto type = evt->eventType();
auto cycle = evt->lifeCycle();
if (type == game::EventType::Missile) {
if (cycle == game::LifeCycle::Create) {
SoundHandle *handle = playFrequency(880.0, 0.1);
if (handle == nullptr) {
continue;
}
fadeIn(handle);
handle->name = "missile-fly-sound_fade-in";
addSoundHandleForObject(evt->object(), handle);
} else if (cycle == game::LifeCycle::Destroy) {
fadeOutSoundHandlesForObject(evt->object());
}
} else if (type == game::EventType::Explosion) {
#if 1
if (cycle == game::LifeCycle::Create) {
SoundHandle *handle = playSound(SoundBrownianNoise, 0.4);
if (handle == nullptr) {
continue;
}
configureEnvelope(handle, 0.2, 0.0, 2.0);
// add big *boom* sound too
handle = playFrequency(200.0, 0.5);
if (handle == nullptr) {
continue;
}
configureEnvelope(handle, 0.1, 0.0, 1.0);
}
#endif
}
}
}
void SoundEffects::fadeIn(SoundHandle *handle, float fadeInTime)
{
// assume sound has just started
configureEnvelope(handle, fadeInTime, 1000000.0, 0.0);
}
void SoundEffects::fadeOut(SoundHandle *handle, float fadeOutTime)
{
// reset time so it imediately starts to fade out.
configureEnvelope(handle, 0.0, 0.0, fadeOutTime);
handle->time = 0.0;
}
}

View file

@ -0,0 +1,43 @@
#pragma once
#include <list>
#include <map>
#include "state/state.hpp"
namespace sound {
struct SoundHandle;
#if 0
class SoundEffect {
public:
virtual ~SoundEffects()
{
}
// return false if it can be deleted
virtual bool advance(float dt) = 0;
};
#endif
class SoundEffects {
public:
SoundEffects()
{
}
void advance(float dt, const std::list<game::StateUpdateEvent*> &updates);
private:
void fadeIn(SoundHandle *handle, float fadeInTime=0.3f);
void fadeOut(SoundHandle *handle, float fadeOutTime=0.3f);
void addSoundHandleForObject(game::Object *obj, SoundHandle *handle);
void fadeOutSoundHandlesForObject(game::Object *obj, float fadeOutTime=0.3f);
private:
std::list<SoundHandle*> m_soundHandles;
std::multimap<size_t, SoundHandle*> m_mapObjectToSoundHandle;
//std::list<SoundEffect*> m_effects;
};
}

View file

@ -5,6 +5,8 @@
#include "trace.hpp"
#include "util.hpp"
#include "developer_console.hpp"
namespace game {
void ShootCommand::apply(Player *player, State *state) const
{
@ -14,25 +16,24 @@ namespace game {
// angles are supplied in degrees and are CCW
Missile *missile = new Missile(
state->generateId(),
player,
player->ship->position,
-util::deg2rad(m_angle),
0.005*player->speed);
Trace *trace = new Trace(missile);
missile->trace = trace;
player->energy -= player->speed;
player->missiles.push_back(missile);
state->addTrace(trace);
state->addMissile(missile);
}
bool ShootCommand::ready(const Player *player, const State *state) const
{
(void) state;
return player->alive && player->energy >= player->speed;
//return player->alive && player->energy >= player->speed;
return player->alive;
}
void ChangeNameCommand::apply(Player *player, State *state) const
@ -78,6 +79,8 @@ namespace game {
void DeveloperCommand::apply(Player *player, State *state) const
{
(void) player;
if (!state->developerMode()) {
std::cout<<"ignoring dev command while not in developer mode: '"
<< m_payload << "'" << std::endl;
@ -90,14 +93,26 @@ namespace game {
std::cout << "got developer command: '" << m_payload << "'" << std::endl;
if (m_payload == "reload shader") {
// TODO: really do that ;)
} else if (m_payload == "reload models") {
std::string nextToken;
std::string nextPayload;
if (util::splitIntoTokenAndRest(m_payload, nextToken, nextPayload)) {
developer::DeveloperConsole::CallbackResult result =
developer::DeveloperConsole::instance().dispatchInput(
nextToken,
nextPayload);
if (result.okay) {
std::cout<<"[commands] developer callback success: " << result.answer << std::endl;
} else {
std::cout<<"[commands] developer callback failure: " << result.answer << std::endl;
}
}
}
void SetDeveloperModeCommand::apply(Player *player, State *state) const
{
(void) player;
// TODO: check if player is admin
state->setDeveloperMode(m_enable);

View file

@ -0,0 +1,14 @@
#pragma once
#include "state/explosion.hpp"
#include "state/state_update_event.hpp"
namespace game {
class ExplosionEvent : public StateUpdateEvent {
public:
ExplosionEvent(LifeCycle lifeCycle, Explosion *explosion)
: StateUpdateEvent(lifeCycle, EventType::Explosion, explosion)
{
}
};
}

View file

@ -0,0 +1,14 @@
#pragma once
#include "state/missile.hpp"
#include "state/state_update_event.hpp"
namespace game {
class MissileEvent : public StateUpdateEvent {
public:
MissileEvent(LifeCycle lifeCycle, Missile *missile)
: StateUpdateEvent(lifeCycle, EventType::Missile, missile)
{
}
};
}

View file

@ -0,0 +1,14 @@
#pragma once
#include "state/ship.hpp"
#include "state/state_update_event.hpp"
namespace game {
class ShipEvent : public StateUpdateEvent {
public:
ShipEvent(LifeCycle lifeCycle, Ship *ship)
: StateUpdateEvent(lifeCycle, EventType::Ship, ship)
{
}
};
}

View file

@ -0,0 +1,16 @@
#include "explosion.hpp"
namespace game {
size_t s_id_counter = 0;
Explosion::Explosion(size_t id, const glm::vec2 &pos, const glm::vec2 &missileVelocity, Hit hit, float maxAge)
: Object(id, pos)
, hit(hit)
, missileVelocity(missileVelocity)
, age(0.0)
, maxAge(maxAge * (1.0 + 0.1*util::randf_0_1()))
, maxRadius(0.05)
{
}
}

View file

@ -1,6 +1,6 @@
#pragma once
#include <glm/vec2.hpp>
#include "object.hpp"
#include "util.hpp"
@ -10,21 +10,12 @@ namespace game {
/**
* Explosion: just an effect which looks good.
*/
class Explosion {
class Explosion : public Object {
public:
Explosion(const glm::vec2 &pos, Hit hit, float maxAge=1.0)
: hit(hit)
, position(pos)
, age(0.0)
, maxAge(maxAge * (1.0 + 0.1*util::randf_0_1()))
, maxRadius(0.05)
{
static size_t id_counter = 0;
id = id_counter++;
}
Explosion(size_t id, const glm::vec2 &pos, const glm::vec2 &missileVelocity, Hit hit, float maxAge=1.0);
const Hit hit; // kind of the explosion depends on the hit type
const glm::vec2 position; // position where it starts
const glm::vec2 missileVelocity; // impact velocity of the missile
float age; // age (in seconsd) of the explosion
// age (in seconds) when the explosion is not visible
@ -32,6 +23,6 @@ namespace game {
const float maxAge;
const float maxRadius; // current radius depends on time.
size_t id;
//virtual void test() = 0;
};
}

View file

@ -11,9 +11,9 @@
#include <glm/gtx/norm.hpp>
namespace game {
Missile::Missile(Player *player, const glm::vec2 &pos, float angle, float speed)
: player(player)
, position(pos)
Missile::Missile(size_t id, Player *player, const glm::vec2 &pos, float angle, float speed)
: Object(id, pos)
, player(player)
{
velocity = speed * glm::vec2(std::sin(angle), std::cos(angle));
@ -37,7 +37,7 @@ namespace game {
if (dist <= other->ship->radius) {
// TODO: collect all hits and return the first one only
// TODO: find exact hit position!
return Missile::Event(position, player->id, other->id);
return Missile::Event(position, velocity, player->id, other->id);
}
}
}
@ -49,7 +49,7 @@ namespace game {
if (dist <= planet->radius) {
// TODO: collect all hits and return the first one only
// TODO: find exact hit position!
return Missile::Event(position, planet->id);
return Missile::Event(position, velocity, planet->id);
}
dist *= 20.0;
@ -77,9 +77,9 @@ namespace game {
// check if distance to center of the universe is getting too big
float distToCenter = glm::length(position);
if (distToCenter > state->maxMissileDistance()) {
return Missile::Event(position, Hit::BorderOfUniverse);
return Missile::Event(position, velocity, Hit::BorderOfUniverse);
}
return Missile::Event(position);
return Missile::Event(position, velocity);
}
}

View file

@ -1,7 +1,8 @@
#pragma once
#include "object.hpp"
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
#include "missile_hit_type.hpp"
@ -15,30 +16,31 @@ namespace game {
// missile belongs to a player and optionally fills a trace behind it.
// trace then belongs to the player.
class Missile {
class Missile : public Object {
public:
// missile advances to pos. if hit != Nothing, it hits something and
// stops existing afterwards.
class Event {
public:
Event(const glm::vec2 &pos)
: Event(pos, Hit::Nothing)
Event(const glm::vec2 &pos, const glm::vec2 &missileVelocity)
: Event(pos, missileVelocity, Hit::Nothing)
{
}
Event(const glm::vec2 &pos, Hit hit)
: hit(hit), position(pos)
Event(const glm::vec2 &pos, const glm::vec2 &missileVelocity, Hit hit)
: hit(hit), position(pos), missileVelocity(missileVelocity)
{
}
Event(const glm::vec2 &pos, int planetId) : Event(pos, Hit::Planet)
Event(const glm::vec2 &pos, const glm::vec2 &missileVelocity, size_t planetId)
: Event(pos, missileVelocity, Hit::Planet)
{
this->planetId = planetId;
}
Event(const glm::vec2 &pos, int playerIdKiller, int playerIdVictim)
: Event(pos, Hit::Ship)
Event(const glm::vec2 &pos, const glm::vec2 &missileVelocity, size_t playerIdKiller, size_t playerIdVictim)
: Event(pos, missileVelocity, Hit::Ship)
{
this->playerIdKiller = playerIdKiller;
this->playerIdVictim = playerIdVictim;
@ -46,16 +48,17 @@ namespace game {
Hit hit;
glm::vec2 position;
glm::vec2 missileVelocity;
// if a player was hit, these are valid.
int playerIdKiller;
int playerIdVictim;
size_t playerIdKiller;
size_t playerIdVictim;
// if a planet was hit, this is valid
int planetId;
size_t planetId;
};
Missile(Player *player, const glm::vec2 &pos, float angle, float speed);
Missile(size_t id, Player *player, const glm::vec2 &pos, float angle, float speed);
~Missile();
// try to advance. if something will be hit, return the first hit in
@ -63,7 +66,6 @@ namespace game {
Missile::Event advance(const game::State *state, float dt);
Player *player; // owner won't be hit by own missiles
glm::vec2 position;
glm::vec2 velocity;
Trace *trace;
};

View file

@ -3,13 +3,14 @@
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
class Object {
public:
Object(const glm::vec2 &pos, float r) : position(pos), radius(r)
namespace game {
class Object {
public:
Object(size_t id, const glm::vec2 &pos) : id(id), position(pos)
{
}
const glm::vec2 position;
const float radius;
};
const size_t id;
glm::vec2 position;
};
}

View file

@ -13,24 +13,18 @@ class Planet : public Object {
*/
enum class Material { Rock=0, Metal=1, Sand=2, Gas=3, Ice=4, Water=5, Sun=6 };
Planet(const glm::vec2 &pos, int id, float r)
: Planet(pos, id, r, Material::Rock)
{
}
Planet(const glm::vec2 &pos, int id, float r, Material mat)
: Object(pos, r), id(id), material(mat), seed(rand())
Planet(size_t id, const glm::vec2 &pos, float r, Material mat=Material::Rock)
: Object(id, pos), material(mat), radius(r), seed(rand())
{
}
glm::vec3 getColor() const;
int id;
// for rendering and physics (can fly through sun and outer gas planets)
Material material;
// just for rendering variation
float radius;
int seed;
};
}

View file

@ -14,7 +14,7 @@ namespace game {
class Player {
public:
int id;
size_t id;
bool alive;
float speed;
float energy;
@ -23,7 +23,7 @@ namespace game {
std::string name;
std::list<Missile*> missiles;
Player(int id) : id(id), alive(true), speed(1.0), energy(0.0), ship(nullptr), name("<unnamed>")
Player(size_t id) : id(id), alive(true), speed(1.0), energy(0.0), ship(nullptr), name("<unnamed>")
{
}

View file

@ -5,8 +5,10 @@
namespace game {
class Ship : public Object {
public:
Ship(const glm::vec2 &pos, float r) : Object(pos, r)
Ship(size_t id, const glm::vec2 &pos, float r) : Object(id, pos), radius(r)
{
}
float radius;
};
}

View file

@ -10,77 +10,63 @@
#include "object.hpp"
#include "missile.hpp"
#include "player.hpp"
#include "planet.hpp"
#include "ship.hpp"
#include "commands.hpp"
#include "trace.hpp"
#include "explosion.hpp"
#include "events/explosion_event.hpp"
#include "events/missile_event.hpp"
#include "events/ship_event.hpp"
#include "util.hpp"
namespace game {
void State::init(int numPlanets, bool devMode)
void State::init(int numPlanets)
{
m_nextId = 0;
// clear
for (Planet *planet : planets) {
delete(planet);
}
planets.clear();
// TODO: clear shots etc. too
m_time = 0.0;
m_shipRadius = 0.02;
m_maxMissileDistance = 2.0;
m_playerRespawnTime = 2.0;
m_defaultEnergy = 10.0;
m_maxNumTraces = 10;
m_developerMode = devMode;
Planet::Material mat = Planet::Material::Rock;
setPlayingFieldCenter(0, 0);
for (int i=0; i<numPlanets; i++) {
switch(i) {
case 0:
mat = Planet::Material::Sun;
break;
// TODO: need aspect ratio or data!
//setPlayingFieldSize(1000, 300);
case 1:
case 2:
mat = Planet::Material::Water;
break;
case 3:
case 4:
mat = Planet::Material::Sand;
break;
case 5:
mat = Planet::Material::Metal;
break;
default:
mat = Planet::Material::Rock;
setupPlanets(numPlanets);
}
glm::vec2 pos;
float radius = 0.03 + 0.07*util::randf_0_1();
if (i == 0) {
// sun is bigger but not too big
radius += 0.05;
if (radius > 0.9) {
radius = 0.9;
}
}
bool State::findPlanetSpawnPosition(bool planetIsSun, float radius, glm::vec2 *pos)
{
(void) planetIsSun;
bool tooNearToCenter = true;
bool collidesWithOtherPlanet = true;
const glm::vec2 spawnArea = 0.9f * (m_playingFieldSize/std::max(m_playingFieldSize.x, m_playingFieldSize.y));
// distribute but not in the center and not next to other planets
int tries = 0;
do {
pos = util::randv2_m1_1();
*pos = spawnArea * util::randv2_m1_1();
collidesWithOtherPlanet = false;
tooNearToCenter = glm::length(pos) < 0.1;
tooNearToCenter = glm::length(*pos) < 0.1;
if (!tooNearToCenter) {
for (const Planet *other : planets) {
float d = glm::distance(other->position, pos);
float d = glm::distance(other->position, *pos);
float extraDist = (other->material == Planet::Material::Sun)
? 4.0
@ -92,10 +78,57 @@ namespace game {
}
}
}
if (tries++ > 1000) {
return false;
}
} while((collidesWithOtherPlanet || tooNearToCenter) && tries++ < 1000);
} while(collidesWithOtherPlanet || tooNearToCenter);
planets.push_back(new Planet(pos, i, radius, mat));
return true;
}
void State::setupPlanets(int numPlanets)
{
for (int i=0; i<numPlanets; i++) {
Planet::Material mat = materialForStandardPlanetDistribution(i);
float radius = 0.03 + 0.07*util::randf_0_1();
if (i == 0) {
// sun is bigger but not too big
radius += 0.05;
if (radius > 0.9) {
radius = 0.9;
}
}
glm::vec2 pos;
if (findPlanetSpawnPosition(mat == Planet::Material::Sun, radius, &pos)) {
planets.push_back(new Planet(i, pos, radius, mat));
}
}
}
Planet::Material State::materialForStandardPlanetDistribution(int index)
{
// a few sun/water/sand/metall planents and the rest rocks
switch(index) {
case 0:
return Planet::Material::Sun;
case 1:
case 2:
return Planet::Material::Water;
case 3:
case 4:
return Planet::Material::Sand;
case 5:
return Planet::Material::Metal;
default:
return Planet::Material::Rock;
}
}
@ -106,23 +139,25 @@ namespace game {
return false;
}
player->ship = new Ship(spawnPos, m_shipRadius);
ships.push_back(player->ship);
Ship *ship = new Ship(generateId(), spawnPos, m_shipRadius);
player->ship = ship;
ships.push_back(ship);
player->energy = m_defaultEnergy;
addShip(ship);
return true;
}
int State::addPlayer()
size_t State::addPlayer()
{
int playerId = m_nextId++;
Player *player = new Player(playerId);
Player *player = new Player(generateId());
players.push_back(player);
return playerId;
return player->id;
}
void State::quitPlayer(int playerId)
void State::quitPlayer(size_t playerId)
{
std::cout << playerId << " quit" << std::endl;
@ -136,12 +171,12 @@ namespace game {
}
}
void State::clear(int playerId)
void State::clear(size_t playerId)
{
std::cout << playerId << " clear" << std::endl;
}
void State::setName(int playerId, std::string name)
void State::setName(size_t playerId, std::string name)
{
// discard if not unique
for (const Player *other : players) {
@ -154,7 +189,12 @@ namespace game {
playerForId(playerId)->name = name;
}
void State::setSpeed(int playerId, double speed)
void State::addShip(Ship *ship)
{
m_nextEvents.push_back(new ShipEvent(LifeCycle::Create, ship));
}
void State::setSpeed(size_t playerId, double speed)
{
playerForId(playerId)->speed = speed;
}
@ -235,10 +275,7 @@ namespace game {
std::vector<Missile*> rm;
for (Missile *missile : player->missiles) {
//std::cout<<"missile: " << (long unsigned int) missile << std::endl;
const Missile::Event evt = missile->advance(this, dt);
const bool isHit = (evt.hit != Hit::Nothing);
if (missile->trace != nullptr) {
@ -288,7 +325,8 @@ namespace game {
for (Missile *missile : rm) {
player->missiles.remove(missile);
delete(missile);
m_nextEvents.push_back(new MissileEvent(LifeCycle::Destroy, missile));
}
}
}
@ -306,12 +344,17 @@ namespace game {
for (Explosion *explosion : rm) {
explosions.remove(explosion);
delete(explosion);
m_nextEvents.push_back(new ExplosionEvent(LifeCycle::Destroy, explosion));
//delete(explosion);
}
}
void State::advance(float dt)
{
//std::cout<<"[state] (init) cycle: update events length is " << m_nextEvents.size() << std::endl;
m_time += dt;
advancePlayerShipSpawns(dt);
@ -323,9 +366,23 @@ namespace game {
advancePlayerCommands(dt);
advancePlayerMissiles(dt);
//std::cout<<"[state] (before move) cycle: update events length is " << m_nextEvents.size() << std::endl;
// put collected events into that list.
//m_allEvents.push_back(std::move(m_nextEvents));
for (auto *evt : m_nextEvents) {
m_allEvents.push_back(evt);
}
m_nextEvents.clear();
//std::cout<<"[state] (after move) cycle: update events length is " << m_nextEvents.size() << std::endl;
// finally remove things.
//m_nextEvents.clear();
}
Player *State::playerForId(int playerId)
Player *State::playerForId(size_t playerId)
{
for (Player *p : players) {
if (p->id == playerId) {
@ -370,7 +427,7 @@ namespace game {
}
}
void State::commandForPlayer(int playerId, game::Command *cmd)
void State::commandForPlayer(size_t playerId, game::Command *cmd)
{
Player *player = playerForId(playerId);
if (player != nullptr) {
@ -393,6 +450,16 @@ namespace game {
traces.push_back(trace);
}
void State::addMissile(Missile *missile)
{
Trace *trace = new Trace(missile);
missile->trace = trace;
addTrace(trace);
m_nextEvents.push_back(new MissileEvent(LifeCycle::Create, missile));
}
void State::deleteTrace(Trace *trace)
{
if (trace->missile != nullptr) {
@ -410,7 +477,13 @@ namespace game {
return;
}
explosions.push_back(new Explosion(evt->position, evt->hit));
Explosion *explosion = new Explosion(
generateId(), evt->position,
evt->missileVelocity, evt->hit);
explosions.push_back(explosion);
m_nextEvents.push_back(new ExplosionEvent(LifeCycle::Create, explosion));
}
void State::advanceTraceAges(float dt)
@ -430,4 +503,60 @@ namespace game {
deleteTrace(trace);
}
}
void State::setPlayingFieldCenter(int center_x, int center_y)
{
m_playingFieldCenter = glm::vec2((float) center_x, (float) center_y);
}
void State::setPlayingFieldSize(int width, int height)
{
m_playingFieldSize = glm::vec2(width, height);
}
size_t State::generateId()
{
return m_ids.makeNextId();
}
void State::applyAndClearAllOldStateUpdates()
{
for (auto *evt : m_allEvents) {
if (evt->lifeCycle() == LifeCycle::Destroy) {
switch(evt->eventType()) {
case EventType::Explosion:
{
ExplosionEvent *ee = static_cast<ExplosionEvent*>(evt);
//std::cout<<"got explosion delete event, finally deleting explosion #"
// << ee->object()->id << std::endl;
delete(ee->object());
}
break;
case EventType::Missile:
{
auto *me = static_cast<MissileEvent*>(evt);
//std::cout<<"got missile delete event, finally deleting missile #"
// << me->object()->id << std::endl;
delete(me->object());
}
break;
default:
std::cout<<"warning: unhandled deletion event for: event type "
<< (int) evt->eventType() << std::endl;
}
}
delete(evt);
}
m_allEvents.clear();
}
const std::list<StateUpdateEvent*> &State::currentStateUpdateEvents() const
{
return m_allEvents;
}
}

View file

@ -10,6 +10,9 @@
#include <glm/vec2.hpp>
#include "missile.hpp"
#include "planet.hpp"
#include "state_update_event.hpp"
// TODO:
// give points for equipment / better weapons / more energy when:
@ -27,11 +30,30 @@ namespace game {
// forward declarations
class Command;
class Player;
class Planet;
class Ship;
class Trace;
class Explosion;
class IdGenerator {
public:
IdGenerator() : m_nextId(0)
{
}
size_t makeNextId()
{
return m_nextId++;
if (m_nextId == 0) {
std::cerr << "note: id counter just wrapped to 0, "
"funny things can happen now." << std::endl;
}
}
private:
size_t m_nextId;
};
class State {
public:
/*************************************************************************/
@ -40,7 +62,7 @@ namespace game {
// called to setup the state (randomize planets, kill
// traces/missiles/ships etc.)
void init(int numPlanets=15, bool devMode=false);
void init(int numPlanets=15);
// main method to advance the simulation by the given timestamp in
// seconds.
@ -52,15 +74,15 @@ namespace game {
// The upper layer (network/renderer) calling these three functions
// should keep id's unique and give one (network) input an id.
int addPlayer();
void clear(int playerId);
void setName(int playerId, std::string name);
void setSpeed(int playerId, double speed);
void quitPlayer(int playerId);
void commandForPlayer(int playerId, Command *cmd);
size_t addPlayer();
void clear(size_t playerId);
void setName(size_t playerId, std::string name);
void setSpeed(size_t playerId, double speed);
void quitPlayer(size_t playerId);
void commandForPlayer(size_t playerId, Command *cmd);
// lookup. return nullptr on invalid playerId
Player *playerForId(int playerId);
Player *playerForId(size_t playerId);
/*************************************************************************/
/* Mixed stuff */
@ -71,9 +93,15 @@ namespace game {
// each ship has the same radius
float shipRadius() const { return m_shipRadius; }
// add a trace to the list of traces.
// add a trace to the state
void addTrace(Trace *trace);
// add a missile to the state
void addMissile(Missile *missile);
// add a ship to the state
void addShip(Ship *ship);
// delete traces with this command
void deleteTrace(Trace *trace); // using a pointer
@ -91,10 +119,26 @@ namespace game {
m_developerMode = on;
}
// called when the rendering window changes
// TODO: give dpi as information too
void setPlayingFieldSize(int width, int height);
void setPlayingFieldCenter(int width, int height);
size_t generateId();
void applyAndClearAllOldStateUpdates();
// delete the items for events that are to be removed in proper way
// after the events were given to other parts of the application.
const std::list<StateUpdateEvent*> &currentStateUpdateEvents() const;
/*************************************************************************/
/* Rendering */
/*************************************************************************/
// TODO: hide and replace by events
// Game items which should be rendered are here:
// (access missiles by iterating over player's missiles attribute)
std::vector<Planet*> planets;
@ -111,12 +155,15 @@ namespace game {
void playerKillsPlayer(Player *killer, Player *victim);
void addExplosionFromHit(const Missile::Event *evt);
bool findPlanetSpawnPosition(bool planetIsSun, float radius, glm::vec2 *pos);
void advanceTraceAges(float dt);
void advanceExplosions(float dt);
void advancePlayerShipSpawns(float dt);
void advancePlayerCommands(float dt);
void advancePlayerMissiles(float dt);
void setupPlanets(int numPlanets);
Planet::Material materialForStandardPlanetDistribution(int index);
// try to spawn a ship for this player.
// return true on success, false on failure to find a spot.
@ -130,9 +177,15 @@ namespace game {
float m_playerRespawnTime;
float m_shipRadius;
float m_defaultEnergy;
int m_nextId;
int m_maxNumTraces;
IdGenerator m_ids;
float m_time;
bool m_developerMode;
glm::vec2 m_playingFieldCenter;
glm::vec2 m_playingFieldSize;
std::list<StateUpdateEvent*> m_nextEvents;
std::list<StateUpdateEvent*> m_allEvents;
};
};
}

View file

@ -0,0 +1,73 @@
#pragma once
#include <string>
#include "object.hpp"
// TODO: make life cycle object class.
// objects from that class can be created and destroyed only through factory
// methods which create updates too.
namespace game {
enum class LifeCycle {
Create, // something was created
Modify, // something was modified (look at attributes)
Destroy // something was destroyed
};
// add all possible classes here
// TODO:
// there can be different things in here, like:
enum class EventType {
Explosion,
Missile,
Ship
};
class StateUpdateEvent {
public:
StateUpdateEvent(LifeCycle cycle, EventType event, Object *object, bool changesContinuously=false)
: m_lifeCycle(cycle), m_eventType(event)
, m_object(object)
, m_changesContinuously(changesContinuously)
{
}
LifeCycle lifeCycle() const { return m_lifeCycle; }
EventType eventType() const { return m_eventType; }
bool changesContinuously() const { return m_changesContinuously; }
Object* object() const { return m_object; }
void setChangesContinuously(bool enable) { m_changesContinuously = enable; }
//std::string description() const
//{
// // TODO
// return "StateUpdateEvent(" + lifeCycleToString(m_lifeCycle) + ", " + eventTypeToString(m_eventType) + ")";
//}
//static std::string lifeCycleToString(LifeCycle lifeCycle)
//{
// switch(lifeCycle) {
// case LifeCycle::Create: return "create";
// case LifeCycle::Modify: return "modify";
// case LifeCycle::Destroy: return "destroy";
// default: return "<no name>";
// }
//}
//static std::string eventTypeToString(EventType eventType)
//{
// switch(eventType) {
// case EventType::Explosion: return "explosion";
// default: return "<no name>";
// }
//}
private:
const LifeCycle m_lifeCycle;
const EventType m_eventType;
Object *m_object;
bool m_changesContinuously;
};
}

View file

@ -3,6 +3,8 @@
#include <cmath>
#include <cstdlib>
#include <sstream>
namespace util {
float randf_m1_1()
{
@ -33,4 +35,101 @@ namespace util {
{
return 180.0 * rad / M_PI;
}
IntersectionTest::IntersectionTest() : m_valid(false), m_distance(INFINITY)
{
}
IntersectionTest::IntersectionTest(float d) : m_valid(true), m_distance(d)
{
}
float IntersectionTest::distance() const { assert(m_valid); return m_distance; }
bool IntersectionTest::valid() const { return m_valid; }
glm::vec3 IntersectionTest::pointAtDistance(float d)
{
assert(m_valid);
return m_rayPos + m_rayDir * d;
}
bool IntersectionTest::raySphere(
const glm::vec3 &rayPos, const glm::vec3 &rayDir,
const glm::vec3 &spherePos, float sphereRadius)
{
m_valid = false;
(void) spherePos;
(void) sphereRadius;
// TODO: save if hit.
m_valid = true;
m_rayPos = rayPos;
m_rayDir = rayDir;
m_distance = 0.5;
// TODO: get code
return m_valid;
#if 0
const glm::vec3 &o = ray.pos();
const glm::vec3 &l = ray.dir();
const glm::vec3 &c = m_pos;
const float &r = m_radius;
const glm::vec3 omc = o-c;
const float dot = glm::dot(l, omc);
const float s = dot*dot - util::dotSelf(omc) + r*r;
if (s < 0.001) {
return RayIntersection();
}
const float t = -dot;
#if 0
// TODO: is that meant as t?
if (s == 0) {
// not interested in touch point
return RayIntersection();
//d1 = t;
//return RayTouchesOnePoint;
}
#endif
const float sr = sqrt(s);
d1 = t - sr;
d2 = t + sr;
// if end intersection lies behind it, this is completely uninteresting for us
// 0.01 because to get rid of rounding/precision errors
if (d2 <= 0.01) {
return RayIntersection();
}
// if the start of the interval lies behind us, make it 0 because we just
// want to know whats before us.
// 0.01 because to get rid of rounding/precision errors
if (d1 < 0.0) {
d1 = 0.0;
}
#endif
}
bool splitIntoTokenAndRest(const std::string &str, std::string &token, std::string &rest)
{
std::istringstream iss(str);
iss >> token;
if (token.size() == 0) {
return false;
}
// skip token + next whitespace
rest = str.substr(std::min(str.size(), token.size()+1));
return true;
}
}

View file

@ -1,6 +1,9 @@
#pragma once
#include <string>
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
namespace util {
@ -12,4 +15,40 @@ namespace util {
float deg2rad(float deg);
float rad2deg(float rad);
// split on whitespace into first token and the rest.
// return true if whitespace was found or just the token.
bool splitIntoTokenAndRest(const std::string &str, std::string &token, std::string &rest);
/**
* Test for intersection in positive direction of a ray.
* TODO: support inside?
*/
class IntersectionTest {
public:
IntersectionTest();
// creates a valid intersection point at distance d
IntersectionTest(float d);
// intersection distance
float distance() const;
glm::vec3 pointAtDistance(float d);
// returns true if there's an interseciton.
bool valid() const;
// return true if there's an intersection. then intersectionPoint is filled
// with details.
bool raySphere(
const glm::vec3 &rayPos, const glm::vec3 &rayDir,
const glm::vec3 &spherePos, float sphereRadius);
private:
bool m_valid;
float m_distance;
glm::vec3 m_rayPos;
glm::vec3 m_rayDir;
};
}

View file

@ -1,9 +0,0 @@
#!/usr/bin/env python3
import os
import random
for i in range(100):
a = random.randint(0, 360)
print("spawn #%d +shoot %d" % (i, a))
os.system("echo %d | ncat 192.168.0.191 3490" % a)

25
test/shoot_n_times.py Executable file
View file

@ -0,0 +1,25 @@
#!/usr/bin/env python3
import os
import random
import time
import socket
import sys
delay = 0.1
if len(sys.argv) <= 1:
print("usage: ./prog $NUM_SHOOTS [$FLOAT_DELAY_BETWEEN_SHOTS]")
exit(1)
if len(sys.argv) > 2:
delay = float(sys.argv[2])
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("192.168.0.191", 3490))
for i in range(int(sys.argv[1])):
a = random.randint(0, 360)
msg = str(a) + " \r\n"
s.send(msg.encode())
time.sleep(delay)