Merge branch 'master' of http://git.j3d1.de/KKK/KlassischeKeplerKriege
This commit is contained in:
commit
756e98fe9f
55 changed files with 123227 additions and 437 deletions
|
@ -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
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
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
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
18
data/mesh/rocket.scad
Normal 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
5126
data/mesh/rocket.stl
Normal file
File diff suppressed because it is too large
Load diff
13
data/shader/background.frag
Normal file
13
data/shader/background.frag
Normal 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);
|
||||
}
|
12
data/shader/background.vert
Normal file
12
data/shader/background.vert
Normal 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);
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
#version 120
|
||||
attribute vec2 in_vertex;
|
||||
attribute vec3 in_position;
|
||||
attribute vec3 in_velocity;
|
||||
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);
|
||||
}
|
|
@ -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})
|
||||
|
|
74
game/developer_console.cpp
Normal file
74
game/developer_console.cpp
Normal 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();
|
||||
}
|
||||
}
|
49
game/developer_console.hpp
Normal file
49
game/developer_console.hpp
Normal 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();
|
||||
}
|
|
@ -80,3 +80,7 @@ bool Game::cycle(float dt)
|
|||
|
||||
return true;
|
||||
}
|
||||
void Game::resize(int width, int height)
|
||||
{
|
||||
m_state->setPlayingFieldSize(width, height);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -6,136 +6,186 @@
|
|||
#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)
|
||||
{
|
||||
glEnableVertexAttribArray(index);
|
||||
glVertexAttribPointer(index, size, type, normalized, stride, 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() {}
|
||||
|
||||
void Shader::init() { m_program = glCreateProgram(); }
|
||||
|
||||
bool Shader::check() {
|
||||
GLint len = 0;
|
||||
GLint result = 0;
|
||||
|
||||
glGetProgramiv(m_program, GL_LINK_STATUS, &result);
|
||||
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);
|
||||
glGetProgramInfoLog(m_program, len, NULL, error);
|
||||
std::string str(error);
|
||||
std::cout << str << std::endl;
|
||||
}
|
||||
// std::cout << "checked program" << std::endl;
|
||||
|
||||
return (bool)result;
|
||||
}
|
||||
|
||||
bool Shader::checkShader(GLuint shader) {
|
||||
GLint len = 0;
|
||||
GLint result = 0;
|
||||
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
|
||||
|
||||
if (result == GL_FALSE) {
|
||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
|
||||
char *error = (char *)malloc(len + 1);
|
||||
glGetShaderInfoLog(shader, len, NULL, error);
|
||||
std::string str(error, error + len);
|
||||
std::cout << str << std::endl;
|
||||
}
|
||||
|
||||
Shader::Shader() : m_program(0) { }
|
||||
// std::cout << "checked shader" << std::endl;
|
||||
|
||||
Shader::~Shader() {}
|
||||
return result != GL_FALSE;
|
||||
}
|
||||
|
||||
void Shader::init() { m_program = glCreateProgram(); }
|
||||
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;
|
||||
|
||||
bool Shader::check() {
|
||||
GLint len = 0;
|
||||
GLint result = 0;
|
||||
|
||||
glGetProgramiv(m_program, GL_LINK_STATUS, &result);
|
||||
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);
|
||||
glGetProgramInfoLog(m_program, len, NULL, error);
|
||||
std::string str(error);
|
||||
std::cout << str << std::endl;
|
||||
}
|
||||
//std::cout << "checked program" << std::endl;
|
||||
|
||||
return (bool)result;
|
||||
exit(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
bool Shader::checkShader(GLuint shader) {
|
||||
GLint len = 0;
|
||||
GLint result = 0;
|
||||
glUseProgram(m_program);
|
||||
}
|
||||
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
|
||||
GLuint Shader::program() { return m_program; }
|
||||
|
||||
if (result == GL_FALSE) {
|
||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
|
||||
char *error = (char *)malloc(len+1);
|
||||
glGetShaderInfoLog(shader, len, NULL, error);
|
||||
std::string str(error, error + len);
|
||||
std::cout << str << std::endl;
|
||||
}
|
||||
void Shader::unbind() { glUseProgram(0); }
|
||||
|
||||
//std::cout << "checked shader" << std::endl;
|
||||
|
||||
return result != GL_FALSE;
|
||||
void Shader::load(const std::string &data, GLenum shadertype) {
|
||||
if (m_program == 0) {
|
||||
std::cout << "[shader] error: shader program is invalid (0)!"
|
||||
<< std::endl;
|
||||
exit(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
GLuint shader = glCreateShader(shadertype);
|
||||
|
||||
exit(-1);
|
||||
return;
|
||||
}
|
||||
const char *shaderdata = data.c_str();
|
||||
glShaderSource(shader, 1, &shaderdata, NULL);
|
||||
glCompileShader(shader);
|
||||
checkShader(shader);
|
||||
|
||||
glUseProgram(m_program);
|
||||
}
|
||||
glAttachShader(m_program, shader);
|
||||
glLinkProgram(m_program);
|
||||
check();
|
||||
|
||||
GLuint Shader::program() { return m_program; }
|
||||
glDeleteShader(shader);
|
||||
}
|
||||
|
||||
void Shader::unbind() { glUseProgram(0); }
|
||||
|
||||
void Shader::load(const std::string &data, GLenum shadertype) {
|
||||
if (m_program == 0) {
|
||||
std::cout<<"[shader] error: shader program is invalid (0)!" << std::endl;
|
||||
exit(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
GLuint shader = glCreateShader(shadertype);
|
||||
|
||||
const char *shaderdata = data.c_str();
|
||||
glShaderSource(shader, 1, &shaderdata, NULL);
|
||||
glCompileShader(shader);
|
||||
checkShader(shader);
|
||||
|
||||
glAttachShader(m_program, shader);
|
||||
glLinkProgram(m_program);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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; }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
159
game/renderer_polygon_3d/image_texture.cpp
Normal file
159
game/renderer_polygon_3d/image_texture.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
63
game/renderer_polygon_3d/image_texture.hpp
Normal file
63
game/renderer_polygon_3d/image_texture.hpp
Normal 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
|
||||
};
|
||||
}
|
|
@ -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 ¢er)
|
||||
|
@ -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;
|
||||
|
@ -81,17 +92,16 @@ namespace endofthejedi {
|
|||
|
||||
//std::cout<<"[ParticleBatch] setParticle " << index << std::endl;
|
||||
|
||||
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_position[index] = p;
|
||||
m_data_velocity[index] = v;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
size_t numParticles() const { return m_numParticles; }
|
||||
// set particle data
|
||||
void setParticle(size_t index, const glm::vec3 &p, const glm::vec3 &v, float maxDistance);
|
||||
|
||||
void setParticle(size_t index, const glm::vec3 &p, const glm::vec3 &v);
|
||||
// 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; }
|
||||
|
||||
// set attributes
|
||||
void setCenter(const glm::vec3 ¢er);
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
addModel("../data/mesh/ship_ufo.stl", &m_shipModel);
|
||||
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,18 +433,20 @@ 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) {
|
||||
glm::vec3 c = glm::vec3(1.0, 1.0, 0.3);
|
||||
glUniform3f(m_shader.location("materialColor"), c.x, c.y, c.z);
|
||||
for (const game::Missile *missile : m_missiles) {
|
||||
glm::vec3 c = glm::vec3(1.0, 1.0, 0.3);
|
||||
glUniform3f(m_shader_game_objects.location("materialColor"), c.x, c.y, c.z);
|
||||
|
||||
glm::mat4 model = computeModelMatrix(missile);
|
||||
glUniformMatrix4fv(m_shader.location("model"), 1, GL_FALSE, glm::value_ptr(model));
|
||||
// TODO: rename functions so their name represents what args they
|
||||
// take
|
||||
glm::mat4 model = computeModelMatrix(missile);
|
||||
glUniformMatrix4fv(m_shader_game_objects.location("model"), 1, GL_FALSE, glm::value_ptr(model));
|
||||
|
||||
m_missileModel->render();
|
||||
}
|
||||
m_missileModel->render();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -199,12 +454,12 @@ namespace endofthejedi {
|
|||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
131
game/sound/dummy_sound.cpp
Normal 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
1071
game/sound/sound.cpp
Normal file
File diff suppressed because it is too large
Load diff
196
game/sound/sound.hpp
Normal file
196
game/sound/sound.hpp
Normal 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
|
83
game/sound/sound_effects.cpp
Normal file
83
game/sound/sound_effects.cpp
Normal 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;
|
||||
}
|
||||
}
|
43
game/sound/sound_effects.hpp
Normal file
43
game/sound/sound_effects.hpp
Normal 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;
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
|
|
14
game/state/events/explosion_event.hpp
Normal file
14
game/state/events/explosion_event.hpp
Normal 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)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
14
game/state/events/missile_event.hpp
Normal file
14
game/state/events/missile_event.hpp
Normal 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)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
14
game/state/events/ship_event.hpp
Normal file
14
game/state/events/ship_event.hpp
Normal 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)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,26 +48,26 @@ 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
|
||||
// time.
|
||||
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;
|
||||
Player *player; // owner won't be hit by own missiles
|
||||
glm::vec2 velocity;
|
||||
Trace *trace;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
const glm::vec2 position;
|
||||
const float radius;
|
||||
};
|
||||
namespace game {
|
||||
class Object {
|
||||
public:
|
||||
Object(size_t id, const glm::vec2 &pos) : id(id), position(pos)
|
||||
{
|
||||
}
|
||||
|
||||
const size_t id;
|
||||
glm::vec2 position;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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
|
||||
int seed;
|
||||
float radius;
|
||||
int seed;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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>")
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -10,53 +10,88 @@
|
|||
#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;
|
||||
setupPlanets(numPlanets);
|
||||
}
|
||||
|
||||
case 3:
|
||||
case 4:
|
||||
mat = Planet::Material::Sand;
|
||||
break;
|
||||
bool State::findPlanetSpawnPosition(bool planetIsSun, float radius, glm::vec2 *pos)
|
||||
{
|
||||
(void) planetIsSun;
|
||||
|
||||
case 5:
|
||||
mat = Planet::Material::Metal;
|
||||
break;
|
||||
bool tooNearToCenter = true;
|
||||
bool collidesWithOtherPlanet = true;
|
||||
|
||||
default:
|
||||
mat = Planet::Material::Rock;
|
||||
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 = spawnArea * util::randv2_m1_1();
|
||||
|
||||
collidesWithOtherPlanet = false;
|
||||
tooNearToCenter = glm::length(*pos) < 0.1;
|
||||
|
||||
if (!tooNearToCenter) {
|
||||
for (const Planet *other : planets) {
|
||||
float d = glm::distance(other->position, *pos);
|
||||
|
||||
float extraDist = (other->material == Planet::Material::Sun)
|
||||
? 4.0
|
||||
: 1.0;
|
||||
|
||||
if (d < extraDist*other->radius + radius) {
|
||||
collidesWithOtherPlanet = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tries++ > 1000) {
|
||||
return false;
|
||||
}
|
||||
|
||||
glm::vec2 pos;
|
||||
} while(collidesWithOtherPlanet || tooNearToCenter);
|
||||
|
||||
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
|
||||
|
@ -67,35 +102,33 @@ namespace game {
|
|||
|
||||
}
|
||||
|
||||
bool tooNearToCenter = true;
|
||||
bool collidesWithOtherPlanet = true;
|
||||
glm::vec2 pos;
|
||||
if (findPlanetSpawnPosition(mat == Planet::Material::Sun, radius, &pos)) {
|
||||
planets.push_back(new Planet(i, pos, radius, mat));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// distribute but not in the center and not next to other planets
|
||||
int tries = 0;
|
||||
do {
|
||||
pos = util::randv2_m1_1();
|
||||
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;
|
||||
|
||||
collidesWithOtherPlanet = false;
|
||||
tooNearToCenter = glm::length(pos) < 0.1;
|
||||
case 1:
|
||||
case 2:
|
||||
return Planet::Material::Water;
|
||||
|
||||
if (!tooNearToCenter) {
|
||||
for (const Planet *other : planets) {
|
||||
float d = glm::distance(other->position, pos);
|
||||
case 3:
|
||||
case 4:
|
||||
return Planet::Material::Sand;
|
||||
|
||||
float extraDist = (other->material == Planet::Material::Sun)
|
||||
? 4.0
|
||||
: 1.0;
|
||||
case 5:
|
||||
return Planet::Material::Metal;
|
||||
|
||||
if (d < extraDist*other->radius + radius) {
|
||||
collidesWithOtherPlanet = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} while((collidesWithOtherPlanet || tooNearToCenter) && tries++ < 1000);
|
||||
|
||||
planets.push_back(new Planet(pos, i, radius, mat));
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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*> ¤tStateUpdateEvents() 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.
|
||||
|
@ -126,13 +173,19 @@ namespace game {
|
|||
// usefule for spanwing things
|
||||
bool findFreePositionWithRadius(float r, glm::vec2 &pos);
|
||||
|
||||
float m_maxMissileDistance;
|
||||
float m_playerRespawnTime;
|
||||
float m_shipRadius;
|
||||
float m_defaultEnergy;
|
||||
int m_nextId;
|
||||
int m_maxNumTraces;
|
||||
float m_time;
|
||||
bool m_developerMode;
|
||||
float m_maxMissileDistance;
|
||||
float m_playerRespawnTime;
|
||||
float m_shipRadius;
|
||||
float m_defaultEnergy;
|
||||
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;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
73
game/state/state_update_event.hpp
Normal file
73
game/state/state_update_event.hpp
Normal 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;
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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
25
test/shoot_n_times.py
Executable 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)
|
||||
|
Loading…
Reference in a new issue