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