Joshua Moerman
12 years ago
5 changed files with 491 additions and 0 deletions
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 new issue