copied source from Gravity Beats
This commit is contained in:
parent
018cb176c4
commit
61c889b0c6
5 changed files with 491 additions and 0 deletions
86
Beat.h
Normal file
86
Beat.h
Normal file
|
@ -0,0 +1,86 @@
|
|||
//
|
||||
// Beat.h
|
||||
// GravityBeats
|
||||
//
|
||||
// Created by Joshua Moerman on 1/9/13.
|
||||
// Copyright (c) 2013 Vadovas. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef GravityBeats_Beat_h
|
||||
#define GravityBeats_Beat_h
|
||||
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
|
||||
#include "UserInformation.h"
|
||||
|
||||
template <typename Information>
|
||||
struct Note : public UserInformation<Information>{
|
||||
// see http://en.wikipedia.org/wiki/Note_value
|
||||
enum Speed {
|
||||
kWholeNote =1, // 1 in one measure
|
||||
kHalfNote =2, // 2 in one measure
|
||||
kTriplet =3, // 3 in one measure
|
||||
kQuarterNote =4, // 4
|
||||
kHalfTriplet =6, // 6
|
||||
kEighthNote =8 // 8
|
||||
} speed;
|
||||
int progress = -2;
|
||||
|
||||
template <typename... S>
|
||||
Note(Speed speed, S... args)
|
||||
: UserInformation<Information>(args...)
|
||||
, speed(speed)
|
||||
{}
|
||||
|
||||
// we could return how much it is off, to give the balls the right initial position and speed.
|
||||
bool update(float time_in_measure){
|
||||
int new_progress = std::floor(time_in_measure*speed);
|
||||
if(new_progress != progress){
|
||||
if(progress == -2){
|
||||
progress = new_progress;
|
||||
return false;
|
||||
} else {
|
||||
progress = new_progress;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void reset(){
|
||||
progress = -1;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename NoteInformation>
|
||||
struct Beat {
|
||||
float total_length{4.0}; // 60 bpm
|
||||
float time{-0.1};
|
||||
|
||||
std::vector<Note<NoteInformation>> notes;
|
||||
|
||||
std::vector<NoteInformation> update(float dt){
|
||||
std::vector<NoteInformation> ret;
|
||||
time += dt;
|
||||
|
||||
// not needed, but keeps the floats small
|
||||
// also usefull when we want to change bpm
|
||||
if(time > total_length){
|
||||
time -= total_length;
|
||||
for(auto& n : notes){
|
||||
n.reset();
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& n : notes){
|
||||
if(n.update(time / total_length)){
|
||||
ret.push_back(n.information);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
99
Math.h
Normal file
99
Math.h
Normal file
|
@ -0,0 +1,99 @@
|
|||
//
|
||||
// Math.h
|
||||
// GravityBeats
|
||||
//
|
||||
// Created by Joshua Moerman on 1/12/13.
|
||||
// Copyright (c) 2013 Vadovas. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef GravityBeats_Math_h
|
||||
#define GravityBeats_Math_h
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace math {
|
||||
struct Vec2 {
|
||||
float x, y;
|
||||
|
||||
Vec2& operator *= (float rh){
|
||||
x *= rh;
|
||||
y *= rh;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vec2& operator+= (Vec2 const & rh){
|
||||
x += rh.x;
|
||||
y += rh.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
float sqr_length() const {
|
||||
return x*x + y*y;
|
||||
}
|
||||
|
||||
float length() const {
|
||||
return std::sqrt(sqr_length());
|
||||
}
|
||||
|
||||
Vec2& normalize(){
|
||||
const float l = length();
|
||||
if(l == 0.0f) { return *this; }
|
||||
x /= l;
|
||||
y /= l;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
inline Vec2 operator*(float lh, Vec2 rh){
|
||||
rh *= lh;
|
||||
return rh;
|
||||
}
|
||||
|
||||
inline Vec2 operator+(Vec2 lh, Vec2 const & rh){
|
||||
return lh += rh;
|
||||
}
|
||||
|
||||
inline Vec2 operator-(Vec2 lh, Vec2 const & rh){
|
||||
return lh += -1.0*rh;
|
||||
}
|
||||
|
||||
inline float dot(Vec2 const & lh, Vec2 const & rh){
|
||||
return lh.x*rh.x + lh.y*rh.y;
|
||||
}
|
||||
|
||||
inline Vec2 rotate_ccw(Vec2 v){
|
||||
std::swap(v.x, v.y);
|
||||
v.x *= -1.0;
|
||||
return v;
|
||||
}
|
||||
|
||||
inline Vec2 normalize(Vec2 v){
|
||||
return v.normalize();
|
||||
}
|
||||
|
||||
inline float distance_point_point(Vec2 p1, Vec2 p2){
|
||||
return (p2 - p1).length();
|
||||
}
|
||||
|
||||
// p point, [v,w] line segment
|
||||
inline float distance_point_line(Vec2 p, Vec2 v, Vec2 w){
|
||||
// http://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment
|
||||
const float l2 = (v-w).sqr_length();
|
||||
if (l2 == 0.0) return distance_point_point(p, v);
|
||||
|
||||
const float t = dot(p - v, w - v) / l2;
|
||||
if (t < 0.0) return distance_point_point(p, v);
|
||||
else if (t > 1.0) return distance_point_point(p, w);
|
||||
|
||||
auto projection = v + t * (w - v);
|
||||
return distance_point_point(p, projection);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T clamp(T input, T min, T max){
|
||||
return input > max ? max :
|
||||
input < min ? min : input;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
70
Scales.h
Normal file
70
Scales.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
//
|
||||
// Scales.h
|
||||
// GravityBeats
|
||||
//
|
||||
// Created by Joshua Moerman on 1/25/13.
|
||||
// Copyright (c) 2013 Vadovas. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef GravityBeats_Scales_h
|
||||
#define GravityBeats_Scales_h
|
||||
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
|
||||
// http://www.phys.unsw.edu.au/jw/notes.html
|
||||
|
||||
inline float pitch_for_midi_note(int note){
|
||||
// we say 440hz is 1 in our units
|
||||
float exponent = (note - 69) / 12.0f;
|
||||
return std::pow(2.0f, exponent);
|
||||
}
|
||||
|
||||
struct Scale {
|
||||
std::vector<int> notes;
|
||||
|
||||
int note_for_length(float length){
|
||||
// determine note
|
||||
length /= 200.0;
|
||||
float note = -std::log(length) / std::log(2.0f) * 12.0f + 69.0f;
|
||||
|
||||
// determine note in scale
|
||||
auto it = notes.begin();
|
||||
while(*it < note && it != notes.end()){
|
||||
++it;
|
||||
}
|
||||
|
||||
// determine closest note in scale
|
||||
if(it == notes.begin()){
|
||||
return *it;
|
||||
} else {
|
||||
auto it2 = it - 1;
|
||||
if(std::abs(*it - note) > std::abs(*it2 - note)){
|
||||
return *it2;
|
||||
} else {
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Scale load_from_file(std::string filename){
|
||||
std::ifstream file(filename);
|
||||
if(!file) throw std::runtime_error("Couldn't open file " + filename);
|
||||
|
||||
Scale scale;
|
||||
|
||||
int note = 0;
|
||||
while (file >> note) {
|
||||
scale.notes.push_back(note);
|
||||
}
|
||||
|
||||
std::sort(scale.notes.begin(), scale.notes.end());
|
||||
|
||||
return scale;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
211
Simulation.h
Normal file
211
Simulation.h
Normal file
|
@ -0,0 +1,211 @@
|
|||
//
|
||||
// Simulation.h
|
||||
// GravityBeats
|
||||
//
|
||||
// Created by Joshua Moerman on 1/5/13.
|
||||
// Copyright (c) 2013 Vadovas. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef GravityBeats_Simulation_h
|
||||
#define GravityBeats_Simulation_h
|
||||
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
|
||||
#include "Math.h"
|
||||
#include "UserInformation.h"
|
||||
|
||||
// TODO: if not needed, remove forcefield
|
||||
|
||||
namespace simulation {
|
||||
|
||||
template <typename Info>
|
||||
struct Ball : public UserInformation<Info> {
|
||||
math::Vec2 position{0.0, 0.0};
|
||||
math::Vec2 speed{0.0, 0.0};
|
||||
|
||||
template <typename... S>
|
||||
Ball(float x, float y, float dx, float dy, S... args)
|
||||
: UserInformation<Info>(args...)
|
||||
, position{x, y}
|
||||
, speed{dx, dy}
|
||||
{}
|
||||
};
|
||||
|
||||
enum LineKind {
|
||||
kFallThrough,
|
||||
kOneWay
|
||||
};
|
||||
|
||||
template <typename Info>
|
||||
struct Line : public UserInformation<Info> {
|
||||
math::Vec2 starting_point;
|
||||
math::Vec2 end_point;
|
||||
math::Vec2 normal;
|
||||
float sqr_length;
|
||||
LineKind line_kind;
|
||||
|
||||
template <typename... S>
|
||||
Line(math::Vec2 starting_point, math::Vec2 end_point, LineKind line_kind, S... args)
|
||||
: UserInformation<Info>(args...)
|
||||
, starting_point(starting_point)
|
||||
, end_point(end_point)
|
||||
, line_kind(line_kind)
|
||||
{
|
||||
update_normal_and_length();
|
||||
}
|
||||
|
||||
void update_normal_and_length(){
|
||||
auto dir = end_point - starting_point;
|
||||
normal = normalize(rotate_ccw(dir));
|
||||
sqr_length = dir.sqr_length();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename BallInfo, typename LineInfo>
|
||||
struct CollisionData {
|
||||
Ball<BallInfo> ball;
|
||||
Line<LineInfo> line;
|
||||
|
||||
// For this trick I need inheriting ctors... Which we dont have yet...
|
||||
//struct : public UserInformation<BallInfo> {} ball_information;
|
||||
//struct : public UserInformation<LineInfo> {} line_information;
|
||||
|
||||
CollisionData(Ball<BallInfo> const & b, Line<LineInfo> const & l)
|
||||
: ball(b), line(l)
|
||||
{}
|
||||
};
|
||||
|
||||
struct Bounds{
|
||||
float xmin{0};
|
||||
float xmax{1280};
|
||||
float ymin{0};
|
||||
float ymax{800};
|
||||
};
|
||||
|
||||
template <typename BallInfo, typename LineInfo>
|
||||
struct Simulation {
|
||||
typedef Ball<BallInfo> ball_type;
|
||||
typedef Line<LineInfo> line_type;
|
||||
typedef CollisionData<BallInfo, LineInfo> collision_type;
|
||||
|
||||
std::vector<ball_type> balls;
|
||||
std::vector<line_type> lines;
|
||||
std::vector<collision_type> collisions_in_update;
|
||||
|
||||
Bounds bounds;
|
||||
|
||||
float collision_timer{0.0};
|
||||
int total_collisions{0};
|
||||
int collisions{0};
|
||||
int collisions_per_second{0};
|
||||
|
||||
Simulation(){
|
||||
balls.reserve(500);
|
||||
lines.reserve(10);
|
||||
collisions_in_update.reserve(1000);
|
||||
}
|
||||
|
||||
std::vector<collision_type> update(float dt){
|
||||
collisions_in_update.clear();
|
||||
|
||||
// move balls
|
||||
for(auto& b : balls){
|
||||
b.speed += dt * math::Vec2{0.0f, 50.0f};
|
||||
|
||||
float rest_time = collide(b, lines, dt);
|
||||
b.position += rest_time*b.speed;
|
||||
}
|
||||
|
||||
// count collisions per second (per half second)
|
||||
collision_timer += dt;
|
||||
if(collision_timer > 0.5){
|
||||
collision_timer -= 0.5;
|
||||
collisions_per_second = collisions * 2;
|
||||
collisions = 0.0;
|
||||
}
|
||||
|
||||
// remove out-of-scene balls
|
||||
for(auto it = balls.begin(); it != balls.end();){
|
||||
ball_type & b = *it;
|
||||
if(b.position.y > bounds.ymax || b.position.x < bounds.xmin || b.position.x > bounds.xmax){
|
||||
it = balls.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
return collisions_in_update;
|
||||
}
|
||||
|
||||
/*
|
||||
Part about the collisions
|
||||
*/
|
||||
float collision_time(ball_type const & b, line_type const & l){
|
||||
// -dot(b.pos - l.start) / ...
|
||||
return dot(l.starting_point - b.position, l.normal) / dot(b.speed, l.normal);
|
||||
}
|
||||
|
||||
std::pair<bool, math::Vec2> check_collision(ball_type const & b, line_type const & l, float dt){
|
||||
auto t = collision_time(b, l);
|
||||
// does it collide within the given time? AND does it go the right way?
|
||||
if(0 <= t && t <= dt){
|
||||
if(l.line_kind == kOneWay && dot(b.speed, l.normal) > 0.0){
|
||||
return {false, {0,0}};
|
||||
}
|
||||
// does it collide with the finite line?
|
||||
auto collision = b.position + t*b.speed;
|
||||
auto on_line = dot(collision - l.starting_point, l.end_point - l.starting_point);
|
||||
if(0 <= on_line && on_line <= l.sqr_length){
|
||||
return {true, collision};
|
||||
} else {
|
||||
return {false, {0,0}};
|
||||
}
|
||||
} else {
|
||||
return {false, {0,0}};
|
||||
}
|
||||
}
|
||||
|
||||
float collide(ball_type& b, std::vector<line_type> const & lines, float dt, line_type const * ignore = nullptr){
|
||||
line_type const * closest_line = nullptr;
|
||||
math::Vec2 collision{0.0, 0.0};
|
||||
float closeness = 100.0f;
|
||||
|
||||
for(auto& l : lines){
|
||||
if(&l == ignore) continue;
|
||||
auto ret = check_collision(b, l, dt);
|
||||
if(ret.first){
|
||||
auto t = collision_time(b, l);
|
||||
|
||||
if(0 <= t && t < closeness){
|
||||
closest_line = &l;
|
||||
closeness = t;
|
||||
collision = ret.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(closest_line == nullptr){
|
||||
return dt;
|
||||
} else {
|
||||
++total_collisions;
|
||||
++collisions;
|
||||
|
||||
b.position = collision;
|
||||
auto const & l = *closest_line;
|
||||
|
||||
collisions_in_update.emplace_back(b, l);
|
||||
|
||||
if(l.line_kind != kFallThrough){
|
||||
auto b1 = -dot(b.speed, l.normal) * l.normal;
|
||||
b.speed = 2.0*b1 + b.speed;
|
||||
}
|
||||
|
||||
return collide(b, lines, dt - closeness, closest_line);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
25
UserInformation.h
Normal file
25
UserInformation.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// UserInformation.h
|
||||
// GravityBeats
|
||||
//
|
||||
// Created by Joshua Moerman on 2/19/13.
|
||||
// Copyright (c) 2013 Vadovas. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef GravityBeats_UserInformation_h
|
||||
#define GravityBeats_UserInformation_h
|
||||
|
||||
template <typename T>
|
||||
struct UserInformation {
|
||||
template <typename... S>
|
||||
UserInformation(S... args)
|
||||
: information(args...)
|
||||
{}
|
||||
|
||||
T information;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct UserInformation<void> {};
|
||||
|
||||
#endif
|
Reference in a new issue