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