Added rotation and zoom splines to note columns, with the help of a couple new classes. Reorganized args to NoteDisplay functions into helper structures. Made splines have 3 modes controlling their behavior. Hold twisting is now handled by an AA rotation and hold drawing in general is now more complex.

This commit is contained in:
Kyzentun
2015-01-01 04:36:20 -07:00
parent 0cb08dc8bd
commit 84e758c6c7
13 changed files with 886 additions and 276 deletions
+1 -1
View File
@@ -970,7 +970,7 @@
<Class base='ActorFrame' name='NoteField'> <Class base='ActorFrame' name='NoteField'>
<Function name='DidHoldNote'/> <Function name='DidHoldNote'/>
<Function name='DidTapNote'/> <Function name='DidTapNote'/>
<Function name='GetColumnActors'/> <Function name='get_column_actors'/>
<Function name='SetPressed'/> <Function name='SetPressed'/>
<Function name='SetDidHoldNoteCallback'/> <Function name='SetDidHoldNoteCallback'/>
<Function name='SetDidTapNoteCallback'/> <Function name='SetDidTapNoteCallback'/>
+1 -1
View File
@@ -2959,7 +2959,7 @@ save yourself some time, copy this for undocumented things:
Makes the NoteField act as if a tap note was hit in the column, with the given score and bright setting. <br /> Makes the NoteField act as if a tap note was hit in the column, with the given score and bright setting. <br />
The callback for DidTapNote will not be called. The callback for DidTapNote will not be called.
</Function> </Function>
<Function name='GetColumnActors' return='{actor}' arguments=''> <Function name='get_column_actors' return='{actor}' arguments=''>
Returns a table of the actors for the columns. Returns a table of the actors for the columns.
</Function> </Function>
<Function name='SetDidHoldNoteCallback' return= '' arguments='function callback'> <Function name='SetDidHoldNoteCallback' return= '' arguments='function callback'>
+8
View File
@@ -19,6 +19,14 @@ public:
return GetYOffset( pPlayerState, iCol, fNoteBeat, fThrowAway, bThrowAway, bAbsolute ); return GetYOffset( pPlayerState, iCol, fNoteBeat, fThrowAway, bThrowAway, bAbsolute );
} }
static void GetXYZPos(const PlayerState* player_state, int col, float y_offset, float y_reverse_offset, vector<float>& ret, bool with_reverse= true)
{
ASSERT(ret.size() == 3);
ret[0]= GetXPos(player_state, col, y_offset);
ret[1]= GetYPos(player_state, col, y_offset, y_reverse_offset, with_reverse);
ret[2]= GetZPos(player_state, col, y_offset);
}
/** /**
* @brief Retrieve the actual display position. * @brief Retrieve the actual display position.
* *
+183 -18
View File
@@ -265,6 +265,11 @@ void CubicSpline::set_results(size_t last, vector<float>& diagonals, vector<floa
m_points[i].b= results[i]; m_points[i].b= results[i];
m_points[i].c= (3 * diff) - (2 * results[i]) - results[next]; m_points[i].c= (3 * diff) - (2 * results[i]) - results[next];
m_points[i].d= (2 * -diff) + results[i] + results[next]; m_points[i].d= (2 * -diff) + results[i] + results[next];
#define UNNAN(n) if(n != n) { n = 0.0f; }
UNNAN(m_points[i].b);
UNNAN(m_points[i].c);
UNNAN(m_points[i].d);
#undef UNNAN
} }
// Solving is now complete. // Solving is now complete.
} }
@@ -275,17 +280,25 @@ float CubicSpline::evaluate(float t, bool loop) const
{ {
return 0.0f; return 0.0f;
} }
int flort= static_cast<int>(t);
if(loop) if(loop)
{ {
float max_t= m_points.size() + 1.0f; float max_t= m_points.size();
while(t > max_t) { t-= max_t; } while(t >= max_t) { t-= max_t; }
while(t < 0.0f) { t+= max_t; } while(t < 0.0f) { t+= max_t; }
flort= static_cast<int>(t);
} }
int flort= static_cast<int>(t); else
if(flort < 0) {
if(flort <= 0)
{ {
return m_points[0].a; return m_points[0].a;
} }
else if(static_cast<size_t>(flort) >= m_points.size() - 1)
{
return m_points[m_points.size() - 1].a;
}
}
size_t p= min(static_cast<size_t>(flort), m_points.size()-1); size_t p= min(static_cast<size_t>(flort), m_points.size()-1);
float tfrac= t - static_cast<float>(flort); float tfrac= t - static_cast<float>(flort);
float tsq= tfrac * tfrac; float tsq= tfrac * tfrac;
@@ -294,12 +307,56 @@ float CubicSpline::evaluate(float t, bool loop) const
(m_points[p].c * tsq) + (m_points[p].d * tcub); (m_points[p].c * tsq) + (m_points[p].d * tcub);
} }
float CubicSpline::evaluate_derivative(float t, bool loop) const
{
if(m_points.empty())
{
return 0.0f;
}
int flort= static_cast<int>(t);
if(loop)
{
float max_t= m_points.size();
while(t >= max_t) { t-= max_t; }
while(t < 0.0f) { t+= max_t; }
flort= static_cast<int>(t);
}
else
{
if(static_cast<size_t>(flort) >= m_points.size() - 1)
{
return 0.0f;
}
}
size_t p= min(static_cast<size_t>(flort), m_points.size()-1);
float tfrac= t - static_cast<float>(flort);
float tsq= tfrac * tfrac;
return m_points[p].b + (2.0f * m_points[p].c * tfrac) +
(3.0f * m_points[p].d * tsq);
}
void CubicSpline::set_point(size_t i, float v) void CubicSpline::set_point(size_t i, float v)
{ {
ASSERT_M(i < m_points.size(), "CubicSpline::set_point requires the index to be less than the number of points."); ASSERT_M(i < m_points.size(), "CubicSpline::set_point requires the index to be less than the number of points.");
m_points[i].a= v; m_points[i].a= v;
} }
void CubicSpline::set_coefficients(size_t i, float b, float c, float d)
{
ASSERT_M(i < m_points.size(), "CubicSpline: point index must be less than the number of points.");
m_points[i].b= b;
m_points[i].c= c;
m_points[i].d= d;
}
void CubicSpline::get_coefficients(size_t i, float& b, float& c, float& d)
{
ASSERT_M(i < m_points.size(), "CubicSpline: point index must be less than the number of points.");
b= m_points[i].b;
c= m_points[i].c;
d= m_points[i].d;
}
void CubicSpline::resize(size_t s) void CubicSpline::resize(size_t s)
{ {
m_points.resize(s); m_points.resize(s);
@@ -346,6 +403,15 @@ void CubicSplineN::evaluate(float t, vector<float>& v) const
} }
} }
void CubicSplineN::evaluate_derivative(float t, vector<float>& v) const
{
for(spline_cont_t::const_iterator spline= m_splines.begin();
spline != m_splines.end(); ++spline)
{
v.push_back(spline->evaluate_derivative(t, loop));
}
}
void CubicSplineN::set_point(size_t i, vector<float> const& v) void CubicSplineN::set_point(size_t i, vector<float> const& v)
{ {
ASSERT_M(v.size() == m_splines.size(), "CubicSplineN::set_point requires the passed point to be the same dimension as the spline."); ASSERT_M(v.size() == m_splines.size(), "CubicSplineN::set_point requires the passed point to be the same dimension as the spline.");
@@ -356,6 +422,30 @@ void CubicSplineN::set_point(size_t i, vector<float> const& v)
m_dirty= true; m_dirty= true;
} }
void CubicSplineN::set_coefficients(size_t i, vector<float> const& b,
vector<float> const& c, vector<float> const& d)
{
ASSERT_M(b.size() == c.size() && c.size() == d.size() &&
d.size() == m_splines.size(), "CubicSplineN: coefficient vectors must be "
"the same dimension as the spline.");
for(size_t n= 0; n < m_splines.size(); ++n)
{
m_splines[n].set_coefficients(i, b[n], c[n], d[n]);
}
}
void CubicSplineN::get_coefficients(size_t i, vector<float>& b,
vector<float>& c, vector<float>& d)
{
ASSERT_M(b.size() == c.size() && c.size() == d.size() &&
d.size() == m_splines.size(), "CubicSplineN: coefficient vectors must be "
"the same dimension as the spline.");
for(size_t n= 0; n < m_splines.size(); ++n)
{
m_splines[n].get_coefficients(i, b[n], c[n], d[n]);
}
}
void CubicSplineN::resize(size_t s) void CubicSplineN::resize(size_t s)
{ {
for(spline_cont_t::iterator spline= m_splines.begin(); for(spline_cont_t::iterator spline= m_splines.begin();
@@ -412,31 +502,47 @@ struct LunaCubicSplineN : Luna<CubicSplineN>
} }
return 1; return 1;
} }
static int evaluate_derivative(T* p, lua_State* L)
{
vector<float> pos;
p->evaluate_derivative(FArg(1), pos);
lua_createtable(L, pos.size(), 0);
for(size_t i= 0; i < pos.size(); ++i)
{
lua_pushnumber(L, pos[i]);
lua_rawseti(L, -2, i+1);
}
return 1;
}
static void get_element_table_from_stack(T* p, lua_State* L, int s,
size_t limit, vector<float>& ret)
{
size_t elements= lua_objlen(L, s);
// Too many elements is not an error because allowing it allows the user
// to reuse the same position data set after changing the dimension size.
// The same is true for too few elements.
for(size_t e= 0; e < elements; ++e)
{
lua_rawgeti(L, s, e+1);
ret.push_back(FArg(-1));
}
while(ret.size() < limit)
{
ret.push_back(0.0f);
}
}
static void set_point_from_stack(T* p, lua_State* L, size_t i, int s) static void set_point_from_stack(T* p, lua_State* L, size_t i, int s)
{ {
if(!lua_istable(L, s)) if(!lua_istable(L, s))
{ {
luaL_error(L, "Spline point must be a table."); luaL_error(L, "Spline point must be a table.");
} }
size_t elements= lua_objlen(L, s);
// Too many elements is not an error because allowing it allows the user
// to reuse the same position data set after changing the dimension size.
// The same is true for too few elements.
vector<float> pos; vector<float> pos;
for(size_t e= 0; e < elements; ++e) get_element_table_from_stack(p, L, s, p->dimension(), pos);
{
lua_rawgeti(L, s, e+1);
pos.push_back(FArg(-1));
}
while(pos.size() < p->dimension())
{
pos.push_back(0.0f);
}
p->set_point(i, pos); p->set_point(i, pos);
} }
static int set_point(T* p, lua_State* L) static int set_point(T* p, lua_State* L)
{ {
vector<float> pos;
int i= IArg(1)-1; int i= IArg(1)-1;
if(i < 0) if(i < 0)
{ {
@@ -449,6 +555,62 @@ struct LunaCubicSplineN : Luna<CubicSplineN>
set_point_from_stack(p, L, static_cast<size_t>(i), 2); set_point_from_stack(p, L, static_cast<size_t>(i), 2);
COMMON_RETURN_SELF; COMMON_RETURN_SELF;
} }
static void set_coefficients_from_stack(T* p, lua_State* L, size_t i, int s)
{
if(!lua_istable(L, s) || !lua_istable(L, s+1) || !lua_istable(L, s+2))
{
luaL_error(L, "Spline coefficient args must be three tables.");
}
size_t limit= p->dimension();
vector<float> b; get_element_table_from_stack(p, L, s, limit, b);
vector<float> c; get_element_table_from_stack(p, L, s+1, limit, c);
vector<float> d; get_element_table_from_stack(p, L, s+2, limit, d);
p->set_coefficients(i, b, c, d);
}
static int set_coefficients(T* p, lua_State* L)
{
int i= IArg(1)-1;
if(i < 0)
{
luaL_error(L, "Cannot set spline coefficients at index less than 1.");
}
if(static_cast<size_t>(i) >= p->size())
{
luaL_error(L, "Cannot set spline coefficients at index greater than size.");
}
set_coefficients_from_stack(p, L, i, 2);
COMMON_RETURN_SELF;
}
static int get_coefficients(T* p, lua_State* L)
{
int i= IArg(1)-1;
if(i < 0)
{
luaL_error(L, "Cannot get spline coefficients at index less than 1.");
}
if(static_cast<size_t>(i) >= p->size())
{
luaL_error(L, "Cannot get spline coefficients at index greater than size.");
}
size_t limit= p->dimension();
vector<vector<float> > coeff(3);
coeff[0].resize(limit);
coeff[1].resize(limit);
coeff[2].resize(limit);
p->get_coefficients(i, coeff[0], coeff[1], coeff[2]);
lua_createtable(L, 3, 0);
for(size_t co= 0; co < coeff.size(); ++co)
{
lua_createtable(L, limit, 0);
for(size_t v= 0; v < limit; ++v)
{
lua_pushnumber(L, coeff[co][v]);
lua_rawseti(L, -2, v+1);
}
lua_rawseti(L, -2, co+1);
}
return 1;
}
static int resize(T* p, lua_State* L) static int resize(T* p, lua_State* L)
{ {
int siz= IArg(1); int siz= IArg(1);
@@ -503,7 +665,10 @@ struct LunaCubicSplineN : Luna<CubicSplineN>
{ {
ADD_METHOD(solve); ADD_METHOD(solve);
ADD_METHOD(evaluate); ADD_METHOD(evaluate);
ADD_METHOD(evaluate_derivative);
ADD_METHOD(set_point); ADD_METHOD(set_point);
ADD_METHOD(set_coefficients);
ADD_METHOD(get_coefficients);
ADD_METHOD(resize); ADD_METHOD(resize);
ADD_METHOD(size); ADD_METHOD(size);
ADD_METHOD(redimension); ADD_METHOD(redimension);
+8
View File
@@ -10,7 +10,10 @@ struct CubicSpline
void solve_looped(); void solve_looped();
void solve_straight(); void solve_straight();
float evaluate(float t, bool loop) const; float evaluate(float t, bool loop) const;
float evaluate_derivative(float t, bool loop) const;
void set_point(size_t i, float v); void set_point(size_t i, float v);
void set_coefficients(size_t i, float b, float c, float d);
void get_coefficients(size_t i, float& b, float& c, float& d);
void resize(size_t s); void resize(size_t s);
size_t size() const; size_t size() const;
bool empty() const; bool empty() const;
@@ -33,7 +36,12 @@ struct CubicSplineN
{} {}
void solve(); void solve();
void evaluate(float t, vector<float>& v) const; void evaluate(float t, vector<float>& v) const;
void evaluate_derivative(float t, vector<float>& v) const;
void set_point(size_t i, vector<float> const& v); void set_point(size_t i, vector<float> const& v);
void set_coefficients(size_t i, vector<float> const& b,
vector<float> const& c, vector<float> const& d);
void get_coefficients(size_t i, vector<float>& b,
vector<float>& c, vector<float>& d);
void resize(size_t s); void resize(size_t s);
size_t size() const; size_t size() const;
void redimension(size_t d); void redimension(size_t d);
+1 -16
View File
@@ -56,23 +56,8 @@ void GhostArrowRow::Update( float fDeltaTime )
{ {
for( unsigned c=0; c<m_Ghost.size(); c++ ) for( unsigned c=0; c<m_Ghost.size(); c++ )
{ {
vector<float> spline_pos;
(*m_renderers)[c].m_spline.evaluate((*m_renderers)[c].m_receptor_t, spline_pos);
m_Ghost[c]->Update( fDeltaTime ); m_Ghost[c]->Update( fDeltaTime );
(*m_renderers)[c].UpdateReceptorGhostStuff(m_Ghost[c]);
float fX = ArrowEffects::GetXPos( m_pPlayerState, c, 0 );
float fY = ArrowEffects::GetYPos( m_pPlayerState, c, 0, m_fYReverseOffsetPixels );
float fZ = ArrowEffects::GetZPos( m_pPlayerState, c, 0 );
m_Ghost[c]->SetX( fX + spline_pos[0] );
m_Ghost[c]->SetY( fY + spline_pos[1] );
m_Ghost[c]->SetZ( fZ + spline_pos[2] );
const float fRotation = ArrowEffects::ReceptorGetRotationZ( m_pPlayerState );
m_Ghost[c]->SetRotationZ( fRotation );
const float fZoom = ArrowEffects::GetZoom( m_pPlayerState );
m_Ghost[c]->SetZoom( fZoom );
} }
for( unsigned i = 0; i < m_bHoldShowing.size(); ++i ) for( unsigned i = 0; i < m_bHoldShowing.size(); ++i )
+499 -143
View File
@@ -17,6 +17,9 @@
#include "Foreach.h" #include "Foreach.h"
#include "RageMath.h" #include "RageMath.h"
static const double PI_180= PI / 180.0;
static const double PI_180R= 180.0 / PI;
const RString& NoteNotePartToString( NotePart i ); const RString& NoteNotePartToString( NotePart i );
/** @brief A foreach loop going through the different NoteParts. */ /** @brief A foreach loop going through the different NoteParts. */
#define FOREACH_NotePart( i ) FOREACH_ENUM( NotePart, i ) #define FOREACH_NotePart( i ) FOREACH_ENUM( NotePart, i )
@@ -43,6 +46,15 @@ XToString( NoteColorType );
StringToX( NoteColorType ); StringToX( NoteColorType );
LuaXType( NoteColorType ); LuaXType( NoteColorType );
static const char* NoteColumnSplineModeNames[] = {
"Disabled",
"Offset",
"Position",
};
XToString(NoteColumnSplineMode);
StringToX(NoteColumnSplineMode);
LuaXType(NoteColumnSplineMode);
static bool IsVectorZero( const RageVector2 &v ) static bool IsVectorZero( const RageVector2 &v )
{ {
return v.x == 0 && v.y == 0; return v.x == 0 && v.y == 0;
@@ -277,6 +289,101 @@ XToString( ActiveType );
float NCSplineHandler::BeatToTValue(float song_beat, float note_beat) const
{
float relative_beat= note_beat;
// This allows someone to do something really fancy like having a spline
// that extends the length of the song. Think of arrows tracing a path
// as the song progresses. -Kyz
if(m_subtract_song_beat_from_curr)
{
relative_beat-= song_beat;
return (relative_beat / m_beats_per_t) - m_receptor_t;
}
return relative_beat / m_beats_per_t;
}
void NCSplineHandler::EvalForBeat(float song_beat, float note_beat, vector<float>& ret) const
{
float t_value= BeatToTValue(song_beat, note_beat);
m_spline.evaluate(t_value, ret);
}
void NCSplineHandler::EvalDerivForBeat(float song_beat, float note_beat, vector<float>& ret) const
{
float t_value= BeatToTValue(song_beat, note_beat);
m_spline.evaluate_derivative(t_value, ret);
}
void NCSplineHandler::EvalForReceptor(float song_beat, vector<float>& ret) const
{
float t_value= m_receptor_t;
if(!m_subtract_song_beat_from_curr)
{
t_value= song_beat / m_beats_per_t;
}
m_spline.evaluate(t_value, ret);
}
void NoteColumnRenderArgs::spae_pos_for_beat(PlayerState const* state,
float beat, float y_offset, float y_reverse_offset,
vector<float>& sp_pos, vector<float>& ae_pos) const
{
switch(pos_handler->m_spline_mode)
{
case NCSM_Disabled:
ArrowEffects::GetXYZPos(state, column, y_offset, y_reverse_offset, ae_pos);
sp_pos.resize(3);
// Sure, resize is supposed to call the default constructor, and for
// numbers the default constructor is supposed to set it to zero, but
// I got bit for relying on that once. -Kyz
sp_pos[0]= sp_pos[1]= sp_pos[2]= 0.0f;
break;
case NCSM_Offset:
ArrowEffects::GetXYZPos(state, column, y_offset, y_reverse_offset, ae_pos);
pos_handler->EvalForBeat(song_beat, beat, sp_pos);
break;
case NCSM_Position:
pos_handler->EvalForBeat(song_beat, beat, sp_pos);
break;
}
}
void NoteColumnRenderArgs::spae_zoom_for_beat(PlayerState const* state, float beat,
vector<float>& sp_zoom, vector<float>& ae_zoom) const
{
switch(zoom_handler->m_spline_mode)
{
case NCSM_Disabled:
ae_zoom[0]= ae_zoom[1]= ae_zoom[2]= ArrowEffects::GetZoom(state);
sp_zoom.resize(3);
sp_zoom[0]= sp_zoom[1]= sp_zoom[2]= 0.0f;
break;
case NCSM_Offset:
ae_zoom[0]= ae_zoom[1]= ae_zoom[2]= ArrowEffects::GetZoom(state);
zoom_handler->EvalForBeat(song_beat, beat, sp_zoom);
break;
case NCSM_Position:
zoom_handler->EvalForBeat(song_beat, beat, sp_zoom);
break;
}
}
void NoteColumnRenderArgs::SetPRZForActor(Actor* actor,
vector<float> const& sp_pos, vector<float> const& ae_pos,
vector<float> const& sp_rot, vector<float> const& ae_rot,
vector<float> const& sp_zoom, vector<float> const& ae_zoom) const
{
actor->SetX(sp_pos[0] + ae_pos[0]);
actor->SetY(sp_pos[1] + ae_pos[1]);
actor->SetZ(sp_pos[2] + ae_pos[2]);
actor->SetRotationX(sp_rot[0] * PI_180R + ae_rot[0]);
actor->SetRotationY(sp_rot[1] * PI_180R + ae_rot[1]);
actor->SetRotationZ(sp_rot[2] * PI_180R + ae_rot[2]);
actor->SetZoomX(sp_zoom[0] + ae_zoom[0]);
actor->SetZoomY(sp_zoom[1] + ae_zoom[1]);
actor->SetZoomZ(sp_zoom[2] + ae_zoom[2]);
}
NoteDisplay::NoteDisplay() NoteDisplay::NoteDisplay()
{ {
cache = new NoteMetricCache_t; cache = new NoteMetricCache_t;
@@ -342,15 +449,9 @@ bool NoteDisplay::IsOnScreen( float fBeat, int iCol, int iDrawDistanceAfterTarge
return true; return true;
} }
float NoteDisplay::BeatToTValue(CommonColumnRenderArgs const& args, float beat) const bool NoteDisplay::DrawHoldsInRange(NoteFieldRenderArgs const& field_args,
{ NoteColumnRenderArgs const& column_args,
float song_beat= m_pPlayerState->GetDisplayedPosition().m_fSongBeatVisible; vector<NoteData::TrackMap::const_iterator> const& tap_set)
float relative_beat= beat - song_beat;
return (relative_beat / args.beats_per_t) - args.receptor_t;
}
bool NoteDisplay::DrawHoldsInRange(CommonColumnRenderArgs const& args,
int column, vector<NoteData::TrackMap::const_iterator> const& tap_set)
{ {
bool any_upcoming = false; bool any_upcoming = false;
for(vector<NoteData::TrackMap::const_iterator>::const_iterator tapit= for(vector<NoteData::TrackMap::const_iterator>::const_iterator tapit=
@@ -367,18 +468,18 @@ bool NoteDisplay::DrawHoldsInRange(CommonColumnRenderArgs const& args,
float throw_away; float throw_away;
bool start_past_peak = false; bool start_past_peak = false;
bool end_past_peak = false; bool end_past_peak = false;
float start_y = ArrowEffects::GetYOffset(m_pPlayerState, column, float start_y = ArrowEffects::GetYOffset(m_pPlayerState, column_args.column,
NoteRowToVisibleBeat(m_pPlayerState, start_row), throw_away, NoteRowToVisibleBeat(m_pPlayerState, start_row), throw_away,
start_past_peak); start_past_peak);
float end_y = ArrowEffects::GetYOffset(m_pPlayerState, column, float end_y = ArrowEffects::GetYOffset(m_pPlayerState, column_args.column,
NoteRowToVisibleBeat(m_pPlayerState, end_row), throw_away, NoteRowToVisibleBeat(m_pPlayerState, end_row), throw_away,
end_past_peak); end_past_peak);
bool tail_visible = args.draw_pixels_after_targets <= end_y && bool tail_visible = field_args.draw_pixels_after_targets <= end_y &&
end_y <= args.draw_pixels_before_targets; end_y <= field_args.draw_pixels_before_targets;
bool head_visible = args.draw_pixels_after_targets <= start_y && bool head_visible = field_args.draw_pixels_after_targets <= start_y &&
start_y <= args.draw_pixels_before_targets; start_y <= field_args.draw_pixels_before_targets;
bool straddling_visible = start_y <= args.draw_pixels_after_targets && bool straddling_visible = start_y <= field_args.draw_pixels_after_targets &&
args.draw_pixels_before_targets <= end_y; field_args.draw_pixels_before_targets <= end_y;
bool straddling_peak = start_past_peak && !end_past_peak; bool straddling_peak = start_past_peak && !end_past_peak;
if(!(tail_visible || head_visible || straddling_visible || straddling_peak)) if(!(tail_visible || head_visible || straddling_visible || straddling_peak))
{ {
@@ -393,21 +494,21 @@ bool NoteDisplay::DrawHoldsInRange(CommonColumnRenderArgs const& args,
const bool is_holding = tn.HoldResult.bHeld; const bool is_holding = tn.HoldResult.bHeld;
if(hold_ghost_showing) if(hold_ghost_showing)
{ {
args.ghost_row->SetHoldShowing(column, tn); field_args.ghost_row->SetHoldShowing(column_args.column, tn);
} }
ASSERT_M(NoteRowToBeat(start_row) > -2000, ssprintf("%i %i %i", start_row, end_row, column)); ASSERT_M(NoteRowToBeat(start_row) > -2000, ssprintf("%i %i %i", start_row, end_row, column_args.column));
bool in_selection_range = false; bool in_selection_range = false;
if(*args.selection_begin_marker != -1 && *args.selection_end_marker != -1) if(*field_args.selection_begin_marker != -1 && *field_args.selection_end_marker != -1)
{ {
in_selection_range = (*args.selection_begin_marker <= start_row && in_selection_range = (*field_args.selection_begin_marker <= start_row &&
end_row < *args.selection_end_marker); end_row < *field_args.selection_end_marker);
} }
DrawHold(tn, args, column, start_row, is_holding, result, DrawHold(tn, field_args, column_args, start_row, is_holding, result,
use_addition_coloring, use_addition_coloring,
in_selection_range ? args.selection_glow : args.fail_fade); in_selection_range ? field_args.selection_glow : field_args.fail_fade);
bool note_upcoming = NoteRowToBeat(start_row) > bool note_upcoming = NoteRowToBeat(start_row) >
m_pPlayerState->GetDisplayedPosition().m_fSongBeat; m_pPlayerState->GetDisplayedPosition().m_fSongBeat;
@@ -416,8 +517,9 @@ bool NoteDisplay::DrawHoldsInRange(CommonColumnRenderArgs const& args,
return any_upcoming; return any_upcoming;
} }
bool NoteDisplay::DrawTapsInRange(CommonColumnRenderArgs const& args, bool NoteDisplay::DrawTapsInRange(NoteFieldRenderArgs const& field_args,
int column, vector<NoteData::TrackMap::const_iterator> const& tap_set) NoteColumnRenderArgs const& column_args,
vector<NoteData::TrackMap::const_iterator> const& tap_set)
{ {
bool any_upcoming= false; bool any_upcoming= false;
// draw notes from furthest to closest // draw notes from furthest to closest
@@ -430,8 +532,8 @@ bool NoteDisplay::DrawTapsInRange(CommonColumnRenderArgs const& args,
// TRICKY: If boomerang is on, then all notes in the range // TRICKY: If boomerang is on, then all notes in the range
// [first_row,last_row] aren't necessarily visible. // [first_row,last_row] aren't necessarily visible.
// Test every note to make sure it's on screen before drawing. // Test every note to make sure it's on screen before drawing.
if(!IsOnScreen(NoteRowToBeat(tap_row), column, if(!IsOnScreen(NoteRowToBeat(tap_row), column_args.column,
args.draw_pixels_after_targets, args.draw_pixels_before_targets)) field_args.draw_pixels_after_targets, field_args.draw_pixels_before_targets))
{ {
continue; // skip continue; // skip
} }
@@ -451,9 +553,9 @@ bool NoteDisplay::DrawTapsInRange(CommonColumnRenderArgs const& args,
bool hold_begins_on_this_beat = false; bool hold_begins_on_this_beat = false;
if(DrawHoldHeadForTapsOnSameRow()) if(DrawHoldHeadForTapsOnSameRow())
{ {
for(int c2= 0; c2 < args.note_data->GetNumTracks(); ++c2) for(int c2= 0; c2 < field_args.note_data->GetNumTracks(); ++c2)
{ {
const TapNote &tmp = args.note_data->GetTapNote(c2, tap_row); const TapNote &tmp = field_args.note_data->GetTapNote(c2, tap_row);
if(tmp.type == TapNoteType_HoldHead && if(tmp.type == TapNoteType_HoldHead &&
tmp.subType == TapNoteSubType_Hold) tmp.subType == TapNoteSubType_Hold)
{ {
@@ -467,9 +569,9 @@ bool NoteDisplay::DrawTapsInRange(CommonColumnRenderArgs const& args,
bool roll_begins_on_this_beat = false; bool roll_begins_on_this_beat = false;
if(DrawRollHeadForTapsOnSameRow()) if(DrawRollHeadForTapsOnSameRow())
{ {
for(int c2= 0; c2 < args.note_data->GetNumTracks(); ++c2) for(int c2= 0; c2 < field_args.note_data->GetNumTracks(); ++c2)
{ {
const TapNote &tmp = args.note_data->GetTapNote(c2, tap_row); const TapNote &tmp = field_args.note_data->GetTapNote(c2, tap_row);
if(tmp.type == TapNoteType_HoldHead && if(tmp.type == TapNoteType_HoldHead &&
tmp.subType == TapNoteSubType_Roll) tmp.subType == TapNoteSubType_Roll)
{ {
@@ -480,19 +582,20 @@ bool NoteDisplay::DrawTapsInRange(CommonColumnRenderArgs const& args,
} }
bool in_selection_range = false; bool in_selection_range = false;
if(*args.selection_begin_marker != -1 && *args.selection_end_marker != -1) if(*field_args.selection_begin_marker != -1 && *field_args.selection_end_marker != -1)
{ {
in_selection_range = *args.selection_begin_marker <= tap_row && in_selection_range = *field_args.selection_begin_marker <= tap_row &&
tap_row < *args.selection_end_marker; tap_row < *field_args.selection_end_marker;
} }
bool is_addition = (tn.source == TapNoteSource_Addition); bool is_addition = (tn.source == TapNoteSource_Addition);
bool hopo_possible = (tn.bHopoPossible); bool hopo_possible = (tn.bHopoPossible);
bool use_addition_coloring = is_addition || hopo_possible; bool use_addition_coloring = is_addition || hopo_possible;
DrawTap(tn, args, column, NoteRowToVisibleBeat(m_pPlayerState, tap_row), DrawTap(tn, field_args, column_args,
NoteRowToVisibleBeat(m_pPlayerState, tap_row),
hold_begins_on_this_beat, roll_begins_on_this_beat, hold_begins_on_this_beat, roll_begins_on_this_beat,
use_addition_coloring, use_addition_coloring,
in_selection_range ? args.selection_glow : args.fail_fade); in_selection_range ? field_args.selection_glow : field_args.fail_fade);
any_upcoming |= NoteRowToBeat(tap_row) > any_upcoming |= NoteRowToBeat(tap_row) >
m_pPlayerState->GetDisplayedPosition().m_fSongBeat; m_pPlayerState->GetDisplayedPosition().m_fSongBeat;
@@ -614,28 +717,24 @@ struct StripBuffer
}; };
void NoteDisplay::DrawHoldPart(vector<Sprite*> &vpSpr, void NoteDisplay::DrawHoldPart(vector<Sprite*> &vpSpr,
CommonColumnRenderArgs const& args, int iCol, int fYStep, NoteFieldRenderArgs const& field_args,
NoteColumnRenderArgs const& column_args, int fYStep,
float fPercentFadeToFail, float fColorScale, bool bGlow, float fPercentFadeToFail, float fColorScale, bool bGlow,
float fOverlappedTime, float fYTop, float fYBottom, float fYStartPos, float fOverlappedTime, float fYTop, float fYBottom, float fYStartPos,
float fYEndPos, bool bWrapping, bool bAnchorToTop, float fYEndPos, bool bWrapping, bool bAnchorToTop,
bool bFlipTextureVertically, float top_t, float bottom_t) bool bFlipTextureVertically, float top_beat, float bottom_beat)
{ {
ASSERT( !vpSpr.empty() ); ASSERT( !vpSpr.empty() );
float ae_zoom= ArrowEffects::GetZoom(m_pPlayerState);
Sprite *pSprite = vpSpr.front(); Sprite *pSprite = vpSpr.front();
FOREACH( Sprite *, vpSpr, s )
{
(*s)->SetZoom( ArrowEffects::GetZoom(m_pPlayerState) );
ASSERT( (*s)->GetUnzoomedWidth() == pSprite->GetUnzoomedWidth() );
ASSERT( (*s)->GetUnzoomedHeight() == pSprite->GetUnzoomedHeight() );
}
// draw manually in small segments // draw manually in small segments
RectF rect = *pSprite->GetCurrentTextureCoordRect(); RectF rect = *pSprite->GetCurrentTextureCoordRect();
if( bFlipTextureVertically ) if( bFlipTextureVertically )
swap( rect.top, rect.bottom ); swap( rect.top, rect.bottom );
const float fFrameWidth = pSprite->GetZoomedWidth(); const float fFrameWidth = pSprite->GetUnzoomedWidth();
const float fFrameHeight = pSprite->GetZoomedHeight(); const float fFrameHeight = pSprite->GetUnzoomedHeight() * ae_zoom;
/* Only draw the section that's within the range specified. If a hold note is /* Only draw the section that's within the range specified. If a hold note is
* very long, don't process or draw the part outside of the range. Don't change * very long, don't process or draw the part outside of the range. Don't change
@@ -674,6 +773,10 @@ void NoteDisplay::DrawHoldPart(vector<Sprite*> &vpSpr,
const float fTexCoordRight = rect.right; const float fTexCoordRight = rect.right;
const float fTexCoordCenter = (fTexCoordLeft+fTexCoordRight)/2; const float fTexCoordCenter = (fTexCoordLeft+fTexCoordRight)/2;
// pos_z_vec will be used later to orient the hold. Read below. -Kyz
static RageVector3 const pos_z_vec(0.0f, 0.0f, 1.0f);
static RageVector3 const pos_y_vec(0.0f, 1.0f, 0.0f);
StripBuffer queue; StripBuffer queue;
for( float fY = fYStartPos; !bLast; fY += fYStep ) for( float fY = fYStartPos; !bLast; fY += fYStep )
{ {
@@ -683,50 +786,165 @@ void NoteDisplay::DrawHoldPart(vector<Sprite*> &vpSpr,
bLast = true; bLast = true;
} }
float curtsy= top_t; const float fYOffset = ArrowEffects::GetYOffsetFromYPos( m_pPlayerState, column_args.column, fY, m_fYReverseOffsetPixels );
if(top_t != bottom_t)
float cur_beat= top_beat;
if(top_beat != bottom_beat)
{ {
curtsy= SCALE(fY, fYTop, fYBottom, top_t, bottom_t); cur_beat= SCALE(fY, fYTop, fYBottom, top_beat, bottom_beat);
}
// Fun times ahead with vector math. If the notes are being moved by the
// position spline, the vectors used to position the edges of the strip
// need to be adjusted or the hold will vanish when the notes move
// horizontally.
// To accomplish this, we use the derivative at the current point from
// AE and the position spline. That gives us the forward vector for the
// strip, pointing to where the next center vert will be. (step 1)
// The vectors pointing left and right to the edges of the strip are
// obtained from the cross product of the forward vector and pos_z_vec.
// (unless the forward vec is too close to pos_z_vec or -pos_z_vec, in
// which case pos_y_vec is used) The result of a cross product is a
// vector perpendicular to both, so forward crossed with pos_z_vec gives
// us the left vector. Right is of course -left. (step 2)
// After that step, the left and right vectors need to be rotated around
// the forward vector axis by the y rotation value, to allow the hold to
// twist. (step 3)
// Steps will be labeled where they occur below. -Kyz
// TODO: Figure out whether it's worth the time investment to figure out
// a way to skip the complex vector handling if the spline is disabled.
vector<float> sp_pos;
vector<float> sp_pos_forward;
vector<float> sp_rot;
vector<float> sp_zoom;
vector<float> ae_pos(3, 0.0f);
vector<float> ae_rot(3, 0.0f);
// (step 1 of vector handling, part 1)
// ArrowEffects only contributes to the Y component of the vector to
// maintain the old behavior of how holds are drawn when they wave back
// and forth. -Kyz
RageVector3 render_forward(0.0f, 1.0f, 0.0f);
column_args.spae_pos_for_beat(m_pPlayerState, cur_beat,
fYOffset, m_fYReverseOffsetPixels, sp_pos, ae_pos);
// fX and fZ are sp_pos[0] + ae_pos[0] and sp_pos[2] + ae_pos[2]. -Kyz
// fY is the actual y position that should be used, not whatever spae
// fetched from ArrowEffects. -Kyz
switch(column_args.pos_handler->m_spline_mode)
{
case NCSM_Disabled:
ae_pos[1]= fY;
sp_pos_forward.resize(3);
sp_pos_forward[0]= sp_pos_forward[1]= sp_pos_forward[2]= 0.0f;
break;
case NCSM_Offset:
ae_pos[1]= fY;
column_args.pos_handler->EvalDerivForBeat(column_args.song_beat, cur_beat, sp_pos_forward);
VectorFloatNormalize(sp_pos_forward);
break;
case NCSM_Position:
ae_pos[1]= 0.0f;
render_forward.y= 0.0f;
column_args.pos_handler->EvalDerivForBeat(column_args.song_beat, cur_beat, sp_pos_forward);
VectorFloatNormalize(sp_pos_forward);
break;
}
render_forward.x+= sp_pos_forward[0];
render_forward.y+= sp_pos_forward[1];
render_forward.z+= sp_pos_forward[2];
// Normalize the vector so it'll be easy to test when determining whether
// to use pos_z_vec or pos_y_vec for the cross product in step 2.
RageVec3Normalize(&render_forward, &render_forward);
// Holds are only affected by the x axis of the zoom spline because they
// are flat sprites. -Kyz
float render_width= fFrameWidth;
switch(column_args.zoom_handler->m_spline_mode)
{
case NCSM_Disabled:
render_width= fFrameWidth * ae_zoom;
break;
case NCSM_Offset:
column_args.zoom_handler->EvalForBeat(column_args.song_beat, cur_beat, sp_zoom);
render_width= fFrameWidth * (ae_zoom + sp_zoom[0]);
break;
case NCSM_Position:
column_args.zoom_handler->EvalForBeat(column_args.song_beat, cur_beat, sp_zoom);
render_width= fFrameWidth * sp_zoom[0];
break;
} }
vector<float> spline_pos;
args.spline->evaluate(curtsy, spline_pos);
const float fYOffset = ArrowEffects::GetYOffsetFromYPos( m_pPlayerState, iCol, fY, m_fYReverseOffsetPixels );
const float fZ = ArrowEffects::GetZPos( m_pPlayerState, iCol, fYOffset ) + spline_pos[2];
const float fFrameWidthScale = ArrowEffects::GetFrameWidthScale( m_pPlayerState, fYOffset, fOverlappedTime ); const float fFrameWidthScale = ArrowEffects::GetFrameWidthScale( m_pPlayerState, fYOffset, fOverlappedTime );
const float fScaledFrameWidth = fFrameWidth * fFrameWidthScale; const float fScaledFrameWidth = render_width * fFrameWidthScale;
float fX = ArrowEffects::GetXPos( m_pPlayerState, iCol, fYOffset ) + spline_pos[0]; // Can't use the same code as for taps because hold bodies can only rotate
// around the y axis. -Kyz
switch(column_args.rot_handler->m_spline_mode)
{
case NCSM_Disabled:
// XXX: Actor rotations use degrees, Math uses radians. Convert here.
ae_rot[1]= ArrowEffects::GetRotationY(m_pPlayerState, fYOffset) * PI_180;
sp_rot.resize(3);
sp_rot[0]= sp_rot[1]= sp_rot[2]= 0.0f;
break;
case NCSM_Offset:
ae_rot[1]= ArrowEffects::GetRotationY(m_pPlayerState, fYOffset) * PI_180;
column_args.rot_handler->EvalForBeat(column_args.song_beat, cur_beat, sp_rot);
break;
case NCSM_Position:
column_args.rot_handler->EvalForBeat(column_args.song_beat, cur_beat, sp_rot);
break;
}
// XXX: Actor rotations use degrees, RageFastCos/Sin use radians. Convert here. RageVector3 center_vert(sp_pos[0] + ae_pos[0],
const float fRotationY = ArrowEffects::GetRotationY( m_pPlayerState, fYOffset ) * PI/180; sp_pos[1] + ae_pos[1], sp_pos[2] + ae_pos[2]);
// if we're rotating, we need to modify the X and Z coords for the outer edges. // Special case for hold caps, which have the same top and bottom beat.
const float fRotOffsetX = (fScaledFrameWidth/2) * RageFastCos(fRotationY); if(top_beat == bottom_beat && fY != fYStartPos)
const float fRotOffsetZ = (fScaledFrameWidth/2) * RageFastSin(fRotationY); {
center_vert.x+= render_forward.x;
center_vert.y+= render_forward.y;
center_vert.z+= render_forward.z;
}
//const float fXLeft = fX - (fScaledFrameWidth/2); const float render_roty= (sp_rot[1] + ae_rot[1]);
const float fXLeft = fX - fRotOffsetX;
const float fXCenter = fX; // (step 2 of vector handling)
//const float fXRight = fX + (fScaledFrameWidth/2); RageVector3 render_left;
const float fXRight = fX + fRotOffsetX; if(abs(render_forward.z) > 0.9f) // 0.9 arbitrariliy picked.
const float fZLeft = fZ - fRotOffsetZ; {
const float fZCenter = fZ; RageVec3Cross(&render_left, &render_forward, &pos_y_vec);
const float fZRight = fZ + fRotOffsetZ; }
else
{
RageVec3Cross(&render_left, &render_forward, &pos_z_vec);
}
RageAARotate(&render_left, &render_forward, render_roty);
const float half_width= fScaledFrameWidth * .5f;
render_left.x*= half_width;
render_left.y*= half_width;
render_left.z*= half_width;
RageVector3 const left_vert(center_vert.x + render_left.x,
center_vert.y + render_left.y, center_vert.z + render_left.z);
RageVector3 const right_vert(center_vert.x - render_left.x,
center_vert.y - render_left.y, center_vert.z - render_left.z);
const float fDistFromTop = fY - fYTop; const float fDistFromTop = fY - fYTop;
float fTexCoordTop = SCALE( fDistFromTop, 0, fFrameHeight, rect.top, rect.bottom ); float fTexCoordTop = SCALE( fDistFromTop, 0, fFrameHeight, rect.top, rect.bottom );
fTexCoordTop += fAddToTexCoord; fTexCoordTop += fAddToTexCoord;
const float fAlpha = ArrowGetAlphaOrGlow( bGlow, m_pPlayerState, iCol, fYOffset, fPercentFadeToFail, m_fYReverseOffsetPixels, args.draw_pixels_before_targets, args.fade_before_targets ); const float fAlpha = ArrowGetAlphaOrGlow( bGlow, m_pPlayerState, column_args.column, fYOffset, fPercentFadeToFail, m_fYReverseOffsetPixels, field_args.draw_pixels_before_targets, field_args.fade_before_targets );
const RageColor color = RageColor(fColorScale,fColorScale,fColorScale,fAlpha); const RageColor color = RageColor(fColorScale,fColorScale,fColorScale,fAlpha);
if( fAlpha > 0 ) if( fAlpha > 0 )
bAllAreTransparent = false; bAllAreTransparent = false;
queue.v[0].p = RageVector3(fXLeft, fY + spline_pos[1], fZLeft); queue.v[0].c = color; queue.v[0].t = RageVector2(fTexCoordLeft, fTexCoordTop); queue.v[0].p = left_vert; queue.v[0].c = color; queue.v[0].t = RageVector2(fTexCoordLeft, fTexCoordTop);
queue.v[1].p = RageVector3(fXCenter, fY + spline_pos[1], fZCenter); queue.v[1].c = color; queue.v[1].t = RageVector2(fTexCoordCenter, fTexCoordTop); queue.v[1].p = center_vert; queue.v[1].c = color; queue.v[1].t = RageVector2(fTexCoordCenter, fTexCoordTop);
queue.v[2].p = RageVector3(fXRight, fY + spline_pos[1], fZRight); queue.v[2].c = color; queue.v[2].t = RageVector2(fTexCoordRight, fTexCoordTop); queue.v[2].p = right_vert; queue.v[2].c = color; queue.v[2].t = RageVector2(fTexCoordRight, fTexCoordTop);
queue.v+=3; queue.v+=3;
if( queue.Free() < 3 || bLast ) if( queue.Free() < 3 || bLast )
@@ -753,10 +971,11 @@ void NoteDisplay::DrawHoldPart(vector<Sprite*> &vpSpr,
} }
void NoteDisplay::DrawHoldBody(const TapNote& tn, void NoteDisplay::DrawHoldBody(const TapNote& tn,
CommonColumnRenderArgs const& args, int iCol, float fBeat, NoteFieldRenderArgs const& field_args,
NoteColumnRenderArgs const& column_args, float fBeat,
bool bIsBeingHeld, float fYHead, float fYTail, bool bIsAddition, bool bIsBeingHeld, float fYHead, float fYTail, bool bIsAddition,
float fPercentFadeToFail, float fColorScale, bool bGlow, float fPercentFadeToFail, float fColorScale, bool bGlow,
float top_t, float bottom_t) float top_beat, float bottom_beat)
{ {
vector<Sprite*> vpSprTop; vector<Sprite*> vpSprTop;
Sprite *pSpriteTop = GetHoldSprite( m_HoldTopCap, NotePart_HoldTopCap, fBeat, tn.subType == TapNoteSubType_Roll, bIsBeingHeld && !cache->m_bHoldActiveIsAddLayer ); Sprite *pSpriteTop = GetHoldSprite( m_HoldTopCap, NotePart_HoldTopCap, fBeat, tn.subType == TapNoteSubType_Roll, bIsBeingHeld && !cache->m_bHoldActiveIsAddLayer );
@@ -780,7 +999,7 @@ void NoteDisplay::DrawHoldBody(const TapNote& tn,
vpSprBottom.push_back( pSprBottom ); vpSprBottom.push_back( pSprBottom );
} }
const bool bReverse = m_pPlayerState->m_PlayerOptions.GetCurrent().GetReversePercentForColumn(iCol) > 0.5f; const bool bReverse = m_pPlayerState->m_PlayerOptions.GetCurrent().GetReversePercentForColumn(column_args.column) > 0.5f;
bool bFlipHoldBody = bReverse && cache->m_bFlipHoldBodyWhenReverse; bool bFlipHoldBody = bReverse && cache->m_bFlipHoldBodyWhenReverse;
if( bFlipHoldBody ) if( bFlipHoldBody )
{ {
@@ -814,55 +1033,56 @@ void NoteDisplay::DrawHoldBody(const TapNote& tn,
const float fFrameHeightTop = pSpriteTop->GetUnzoomedHeight(); const float fFrameHeightTop = pSpriteTop->GetUnzoomedHeight();
const float fFrameHeightBottom = pSpriteBottom->GetUnzoomedHeight(); const float fFrameHeightBottom = pSpriteBottom->GetUnzoomedHeight();
float fYStartPos = ArrowEffects::GetYPos( m_pPlayerState, iCol, float fYStartPos = ArrowEffects::GetYPos( m_pPlayerState, column_args.column,
args.draw_pixels_after_targets, m_fYReverseOffsetPixels ); field_args.draw_pixels_after_targets, m_fYReverseOffsetPixels );
float fYEndPos = ArrowEffects::GetYPos( m_pPlayerState, iCol, float fYEndPos = ArrowEffects::GetYPos( m_pPlayerState, column_args.column,
args.draw_pixels_before_targets, m_fYReverseOffsetPixels ); field_args.draw_pixels_before_targets, m_fYReverseOffsetPixels );
if( bReverse ) if( bReverse )
swap( fYStartPos, fYEndPos ); swap( fYStartPos, fYEndPos );
bool bTopAnchor = bReverse && cache->m_bTopHoldAnchorWhenReverse; bool bTopAnchor = bReverse && cache->m_bTopHoldAnchorWhenReverse;
// Draw the top cap // Draw the top cap
DrawHoldPart(vpSprTop, args, iCol, fYStep, fPercentFadeToFail, DrawHoldPart(vpSprTop, field_args, column_args, fYStep, fPercentFadeToFail,
fColorScale, bGlow, fColorScale, bGlow,
tn.HoldResult.fOverlappedTime, tn.HoldResult.fOverlappedTime,
fYHead-fFrameHeightTop, fYHead, fYHead-fFrameHeightTop, fYHead,
fYStartPos, fYEndPos, fYStartPos, fYEndPos,
false, bTopAnchor, bFlipHoldBody, top_t, top_t); false, bTopAnchor, bFlipHoldBody, top_beat, top_beat);
// Draw the body // Draw the body
DrawHoldPart(vpSprBody, args, iCol, fYStep, fPercentFadeToFail, DrawHoldPart(vpSprBody, field_args, column_args, fYStep, fPercentFadeToFail,
fColorScale, bGlow, fColorScale, bGlow,
tn.HoldResult.fOverlappedTime, tn.HoldResult.fOverlappedTime,
fYHead, fYTail, fYHead, fYTail,
fYStartPos, fYEndPos, fYStartPos, fYEndPos,
true, bTopAnchor, bFlipHoldBody, top_t, bottom_t); true, bTopAnchor, bFlipHoldBody, top_beat, bottom_beat);
// Draw the bottom cap // Draw the bottom cap
DrawHoldPart(vpSprBottom, args, iCol, fYStep, fPercentFadeToFail, DrawHoldPart(vpSprBottom, field_args, column_args, fYStep, fPercentFadeToFail,
fColorScale, bGlow, fColorScale, bGlow,
tn.HoldResult.fOverlappedTime, tn.HoldResult.fOverlappedTime,
fYTail, fYTail+fFrameHeightBottom, fYTail, fYTail+fFrameHeightBottom,
max(fYStartPos, fYHead), fYEndPos, max(fYStartPos, fYHead), fYEndPos,
false, bTopAnchor, bFlipHoldBody, bottom_t, bottom_t); false, bTopAnchor, bFlipHoldBody, bottom_beat, bottom_beat);
} }
void NoteDisplay::DrawHold(const TapNote& tn, void NoteDisplay::DrawHold(const TapNote& tn,
CommonColumnRenderArgs const& args, int iCol, int iRow, bool bIsBeingHeld, NoteFieldRenderArgs const& field_args,
NoteColumnRenderArgs const& column_args, int iRow, bool bIsBeingHeld,
const HoldNoteResult &Result, bool bIsAddition, float fPercentFadeToFail) const HoldNoteResult &Result, bool bIsAddition, float fPercentFadeToFail)
{ {
int iEndRow = iRow + tn.iDuration; int iEndRow = iRow + tn.iDuration;
float top_t= BeatToTValue(args, NoteRowToVisibleBeat(m_pPlayerState, iRow)); float top_beat= NoteRowToVisibleBeat(m_pPlayerState, iRow);
float bottom_t= BeatToTValue(args, NoteRowToVisibleBeat(m_pPlayerState, iEndRow)); float bottom_beat= NoteRowToVisibleBeat(m_pPlayerState, iEndRow);
if(bIsBeingHeld) if(bIsBeingHeld)
{ {
top_t= args.receptor_t; top_beat= column_args.song_beat;
} }
// bDrawGlowOnly is a little hacky. We need to draw the diffuse part and the glow part one pass at a time to minimize state changes // bDrawGlowOnly is a little hacky. We need to draw the diffuse part and the glow part one pass at a time to minimize state changes
bool bReverse = m_pPlayerState->m_PlayerOptions.GetCurrent().GetReversePercentForColumn(iCol) > 0.5f; bool bReverse = m_pPlayerState->m_PlayerOptions.GetCurrent().GetReversePercentForColumn(column_args.column) > 0.5f;
float fStartBeat = NoteRowToBeat( max(tn.HoldResult.iLastHeldRow, iRow) ); float fStartBeat = NoteRowToBeat( max(tn.HoldResult.iLastHeldRow, iRow) );
float fThrowAway = 0; float fThrowAway = 0;
@@ -872,11 +1092,11 @@ void NoteDisplay::DrawHold(const TapNote& tn,
if( tn.HoldResult.bActive && tn.HoldResult.fLife > 0 ) if( tn.HoldResult.bActive && tn.HoldResult.fLife > 0 )
; // use the default values filled in above ; // use the default values filled in above
else else
fStartYOffset = ArrowEffects::GetYOffset( m_pPlayerState, iCol, fStartBeat, fThrowAway, bStartIsPastPeak ); fStartYOffset = ArrowEffects::GetYOffset( m_pPlayerState, column_args.column, fStartBeat, fThrowAway, bStartIsPastPeak );
float fEndPeakYOffset = 0; float fEndPeakYOffset = 0;
bool bEndIsPastPeak = false; bool bEndIsPastPeak = false;
float fEndYOffset = ArrowEffects::GetYOffset( m_pPlayerState, iCol, NoteRowToBeat(iEndRow), fEndPeakYOffset, bEndIsPastPeak ); float fEndYOffset = ArrowEffects::GetYOffset( m_pPlayerState, column_args.column, NoteRowToBeat(iEndRow), fEndPeakYOffset, bEndIsPastPeak );
// In boomerang, the arrows reverse direction at Y offset value fPeakAtYOffset. // In boomerang, the arrows reverse direction at Y offset value fPeakAtYOffset.
// If fPeakAtYOffset lies inside of the hold we're drawing, then the we // If fPeakAtYOffset lies inside of the hold we're drawing, then the we
@@ -889,8 +1109,8 @@ void NoteDisplay::DrawHold(const TapNote& tn,
if( bReverse ) if( bReverse )
swap( fStartYOffset, fEndYOffset ); swap( fStartYOffset, fEndYOffset );
const float fYHead = ArrowEffects::GetYPos( m_pPlayerState, iCol, fStartYOffset, m_fYReverseOffsetPixels ); const float fYHead = ArrowEffects::GetYPos( m_pPlayerState, column_args.column, fStartYOffset, m_fYReverseOffsetPixels );
const float fYTail = ArrowEffects::GetYPos( m_pPlayerState, iCol, fEndYOffset, m_fYReverseOffsetPixels ); const float fYTail = ArrowEffects::GetYPos( m_pPlayerState, column_args.column, fEndYOffset, m_fYReverseOffsetPixels );
const float fColorScale = SCALE( tn.HoldResult.fLife, 0.0f, 1.0f, cache->m_fHoldLetGoGrayPercent, 1.0f ); const float fColorScale = SCALE( tn.HoldResult.fLife, 0.0f, 1.0f, cache->m_fHoldLetGoGrayPercent, 1.0f );
@@ -899,6 +1119,9 @@ void NoteDisplay::DrawHold(const TapNote& tn,
/* The body and caps should have no overlap, so their order doesn't matter. /* The body and caps should have no overlap, so their order doesn't matter.
* Draw the head last, so it appears on top. */ * Draw the head last, so it appears on top. */
float fBeat = NoteRowToBeat(iRow); float fBeat = NoteRowToBeat(iRow);
// Side note: I don't know why these two checks were commented out and I
// didn't bother to update them when rewriting the arguments that are
// passed to the note drawing functions. -Kyz
/* /*
if( !cache->m_bHoldHeadIsAboveWavyParts ) if( !cache->m_bHoldHeadIsAboveWavyParts )
{ {
@@ -912,8 +1135,8 @@ void NoteDisplay::DrawHold(const TapNote& tn,
} }
*/ */
DrawHoldBody(tn, args, iCol, fBeat, bIsBeingHeld, fYHead, fYTail, bIsAddition, fPercentFadeToFail, fColorScale, false, top_t, bottom_t); DrawHoldBody(tn, field_args, column_args, fBeat, bIsBeingHeld, fYHead, fYTail, bIsAddition, fPercentFadeToFail, fColorScale, false, top_beat, bottom_beat);
DrawHoldBody(tn, args, iCol, fBeat, bIsBeingHeld, fYHead, fYTail, bIsAddition, fPercentFadeToFail, fColorScale, true, top_t, bottom_t); DrawHoldBody(tn, field_args, column_args, fBeat, bIsBeingHeld, fYHead, fYTail, bIsAddition, fPercentFadeToFail, fColorScale, true, top_beat, bottom_beat);
/* These set the texture mode themselves. */ /* These set the texture mode themselves. */
// this part was modified in pumpmania, where it flips the draw order // this part was modified in pumpmania, where it flips the draw order
@@ -921,56 +1144,76 @@ void NoteDisplay::DrawHold(const TapNote& tn,
if( cache->m_bHoldTailIsAboveWavyParts ) if( cache->m_bHoldTailIsAboveWavyParts )
{ {
Actor *pActor = GetHoldActor( m_HoldTail, NotePart_HoldTail, NoteRowToBeat(iRow), tn.subType == TapNoteSubType_Roll, bIsBeingHeld ); Actor *pActor = GetHoldActor( m_HoldTail, NotePart_HoldTail, NoteRowToBeat(iRow), tn.subType == TapNoteSubType_Roll, bIsBeingHeld );
DrawActor(tn, pActor, NotePart_HoldTail, args, iCol, bFlipHeadAndTail ? fStartYOffset : fEndYOffset, fBeat, bIsAddition, fPercentFadeToFail, fColorScale, false); DrawActor(tn, pActor, NotePart_HoldTail, field_args, column_args, bFlipHeadAndTail ? fStartYOffset : fEndYOffset, fBeat, bIsAddition, fPercentFadeToFail, fColorScale, false);
} }
if( cache->m_bHoldHeadIsAboveWavyParts ) if( cache->m_bHoldHeadIsAboveWavyParts )
{ {
Actor *pActor = GetHoldActor( m_HoldHead, NotePart_HoldHead, NoteRowToBeat(iRow), tn.subType == TapNoteSubType_Roll, bIsBeingHeld ); Actor *pActor = GetHoldActor( m_HoldHead, NotePart_HoldHead, NoteRowToBeat(iRow), tn.subType == TapNoteSubType_Roll, bIsBeingHeld );
DrawActor(tn, pActor, NotePart_HoldHead, args, iCol, bFlipHeadAndTail ? fEndYOffset : fStartYOffset, fBeat, bIsAddition, fPercentFadeToFail, fColorScale, bIsBeingHeld); DrawActor(tn, pActor, NotePart_HoldHead, field_args, column_args, bFlipHeadAndTail ? fEndYOffset : fStartYOffset, fBeat, bIsAddition, fPercentFadeToFail, fColorScale, bIsBeingHeld);
} }
} }
void NoteDisplay::DrawActor(const TapNote& tn, Actor* pActor, NotePart part, void NoteDisplay::DrawActor(const TapNote& tn, Actor* pActor, NotePart part,
CommonColumnRenderArgs const& args, int iCol, float fYOffset, float fBeat, NoteFieldRenderArgs const& field_args, NoteColumnRenderArgs const& column_args, float fYOffset, float fBeat,
bool bIsAddition, float fPercentFadeToFail, float fColorScale, bool bIsAddition, float fPercentFadeToFail, float fColorScale,
bool is_being_held) bool is_being_held)
{ {
if (tn.type == TapNoteType_AutoKeysound && !GAMESTATE->m_bInStepEditor) return; if (tn.type == TapNoteType_AutoKeysound && !GAMESTATE->m_bInStepEditor) return;
if(fYOffset < args.draw_pixels_after_targets || if(fYOffset < field_args.draw_pixels_after_targets ||
fYOffset > args.draw_pixels_before_targets) fYOffset > field_args.draw_pixels_before_targets)
return; return;
float spline_t= BeatToTValue(args, fBeat); float spline_beat= fBeat;
if(is_being_held) { spline_t= args.receptor_t; } if(is_being_held) { spline_beat= column_args.song_beat; }
vector<float> spline_pos;
args.spline->evaluate(spline_t, spline_pos); const float fAlpha = ArrowEffects::GetAlpha( m_pPlayerState, column_args.column, fYOffset, fPercentFadeToFail, m_fYReverseOffsetPixels, field_args.draw_pixels_before_targets, field_args.fade_before_targets );
const float fY = ArrowEffects::GetYPos( m_pPlayerState, iCol, fYOffset, m_fYReverseOffsetPixels ) + spline_pos[1]; const float fGlow = ArrowEffects::GetGlow( m_pPlayerState, column_args.column, fYOffset, fPercentFadeToFail, m_fYReverseOffsetPixels, field_args.draw_pixels_before_targets, field_args.fade_before_targets );
const float fX = ArrowEffects::GetXPos( m_pPlayerState, iCol, fYOffset ) + spline_pos[0];
const float fZ = ArrowEffects::GetZPos( m_pPlayerState, iCol, fYOffset ) + spline_pos[2];
const float fAlpha = ArrowEffects::GetAlpha( m_pPlayerState, iCol, fYOffset, fPercentFadeToFail, m_fYReverseOffsetPixels, args.draw_pixels_before_targets, args.fade_before_targets );
const float fGlow = ArrowEffects::GetGlow( m_pPlayerState, iCol, fYOffset, fPercentFadeToFail, m_fYReverseOffsetPixels, args.draw_pixels_before_targets, args.fade_before_targets );
const RageColor diffuse = RageColor(fColorScale,fColorScale,fColorScale,fAlpha); const RageColor diffuse = RageColor(fColorScale,fColorScale,fColorScale,fAlpha);
const RageColor glow = RageColor(1,1,1,fGlow); const RageColor glow = RageColor(1,1,1,fGlow);
float fRotationX = 0, fRotationZ = 0;
const float fRotationY = ArrowEffects::GetRotationY( m_pPlayerState, fYOffset );
bool bIsHoldHead = tn.type == TapNoteType_HoldHead; bool bIsHoldHead = tn.type == TapNoteType_HoldHead;
bool bIsHoldCap = bIsHoldHead || tn.type == TapNoteType_HoldTail; bool bIsHoldCap = bIsHoldHead || tn.type == TapNoteType_HoldTail;
fRotationZ = ArrowEffects::GetRotationZ( m_pPlayerState, fBeat, bIsHoldHead );
if( !bIsHoldCap )
{
fRotationX = ArrowEffects::GetRotationX( m_pPlayerState, fYOffset );
}
if( tn.type != TapNoteType_HoldHead ) if( tn.type != TapNoteType_HoldHead )
fColorScale *= ArrowEffects::GetBrightness( m_pPlayerState, fBeat ); fColorScale *= ArrowEffects::GetBrightness( m_pPlayerState, fBeat );
pActor->SetRotationX( fRotationX ); // same logical structure as in UpdateReceptorGhostStuff, I just haven't
pActor->SetRotationY( fRotationY ); // figured out a good way to combine them. -Kyz
pActor->SetRotationZ( fRotationZ ); vector<float> sp_pos;
pActor->SetXY( fX, fY ); vector<float> sp_rot;
pActor->SetZ( fZ ); vector<float> sp_zoom;
pActor->SetZoom( ArrowEffects::GetZoom(m_pPlayerState) ); vector<float> ae_pos(3, 0.0f);
vector<float> ae_rot(3, 0.0f);
vector<float> ae_zoom(3, 0.0f);
column_args.spae_pos_for_beat(m_pPlayerState, spline_beat,
fYOffset, m_fYReverseOffsetPixels, sp_pos, ae_pos);
switch(column_args.rot_handler->m_spline_mode)
{
case NCSM_Disabled:
if(!bIsHoldCap)
{
ae_rot[0]= ArrowEffects::GetRotationX(m_pPlayerState, fYOffset);
}
ae_rot[1]= ArrowEffects::GetRotationY(m_pPlayerState, fYOffset);
ae_rot[2]= ArrowEffects::GetRotationZ(m_pPlayerState, fBeat, bIsHoldHead);
sp_rot.resize(3);
sp_rot[0]= sp_rot[1]= sp_rot[2]= 0.0f;
break;
case NCSM_Offset:
if(!bIsHoldCap)
{
ae_rot[0]= ArrowEffects::GetRotationX(m_pPlayerState, fYOffset);
}
ae_rot[1]= ArrowEffects::GetRotationY(m_pPlayerState, fYOffset);
ae_rot[2]= ArrowEffects::GetRotationZ(m_pPlayerState, fBeat, bIsHoldHead);
column_args.rot_handler->EvalForBeat(column_args.song_beat, spline_beat, sp_rot);
break;
case NCSM_Position:
column_args.rot_handler->EvalForBeat(column_args.song_beat, spline_beat, sp_rot);
break;
}
column_args.spae_zoom_for_beat(m_pPlayerState, spline_beat, sp_zoom, ae_zoom);
column_args.SetPRZForActor(pActor, sp_pos, ae_pos, sp_rot, ae_rot, sp_zoom, ae_zoom);
// [AJ] this two lines (and how they're handled) piss off many people: // [AJ] this two lines (and how they're handled) piss off many people:
pActor->SetDiffuse( diffuse ); pActor->SetDiffuse( diffuse );
pActor->SetGlow( glow ); pActor->SetGlow( glow );
@@ -1004,7 +1247,8 @@ void NoteDisplay::DrawActor(const TapNote& tn, Actor* pActor, NotePart part,
} }
void NoteDisplay::DrawTap(const TapNote& tn, void NoteDisplay::DrawTap(const TapNote& tn,
CommonColumnRenderArgs const& args, int iCol, float fBeat, NoteFieldRenderArgs const& field_args,
NoteColumnRenderArgs const& column_args, float fBeat,
bool bOnSameRowAsHoldStart, bool bOnSameRowAsRollStart, bool bOnSameRowAsHoldStart, bool bOnSameRowAsRollStart,
bool bIsAddition, float fPercentFadeToFail) bool bIsAddition, float fPercentFadeToFail)
{ {
@@ -1078,21 +1322,89 @@ void NoteDisplay::DrawTap(const TapNote& tn,
pActor->HandleMessage( msg ); pActor->HandleMessage( msg );
} }
const float fYOffset = ArrowEffects::GetYOffset( m_pPlayerState, iCol, fBeat ); const float fYOffset = ArrowEffects::GetYOffset( m_pPlayerState, column_args.column, fBeat );
// this is the line that forces the (1,1,1,x) part of the noteskin diffuse -aj // this is the line that forces the (1,1,1,x) part of the noteskin diffuse -aj
DrawActor(tn, pActor, part, args, iCol, fYOffset, fBeat, bIsAddition, fPercentFadeToFail, 1.0f, false); DrawActor(tn, pActor, part, field_args, column_args, fYOffset, fBeat, bIsAddition, fPercentFadeToFail, 1.0f, false);
if( tn.type == TapNoteType_Attack ) if( tn.type == TapNoteType_Attack )
pActor->PlayCommand( "UnsetAttack" ); pActor->PlayCommand( "UnsetAttack" );
} }
void NoteColumnRenderer::UpdateReceptorGhostStuff(Actor* receptor) const
{
PlayerState const* player_state= m_field_render_args->player_state;
float song_beat= player_state->GetDisplayedPosition().m_fSongBeatVisible;
// sp_* will be filled with the settings from the splines.
// ae_* will be filled with the settings from ArrowEffects.
// The two together will be applied to the actor.
// sp_* will be zeroes in NCSM_Disabled, and ae_* will be zeroes in
// NCSM_Position, so the setting step won't have to check the mode. -Kyz
// sp_* are sized by the spline evaluate function.
vector<float> sp_pos;
vector<float> sp_rot;
vector<float> sp_zoom;
vector<float> ae_pos(3, 0.0f);
vector<float> ae_rot(3, 0.0f);
vector<float> ae_zoom(3, 0.0f);
switch(m_pos_handler.m_spline_mode)
{
case NCSM_Disabled:
ArrowEffects::GetXYZPos(player_state, m_column, 0, m_field_render_args->reverse_offset_pixels, ae_pos);
sp_pos.resize(3);
// Sure, resize is supposed to call the default constructor, and for
// numbers the default constructor is supposed to set it to zero, but
// I got bit for relying on that once. -Kyz
sp_pos[0]= sp_pos[1]= sp_pos[2]= 0.0f;
break;
case NCSM_Offset:
ArrowEffects::GetXYZPos(player_state, m_column, 0, m_field_render_args->reverse_offset_pixels, ae_pos);
m_pos_handler.EvalForReceptor(song_beat, sp_pos);
break;
case NCSM_Position:
m_pos_handler.EvalForReceptor(song_beat, sp_pos);
break;
}
switch(m_rot_handler.m_spline_mode)
{
case NCSM_Disabled:
ae_rot[2]= ArrowEffects::ReceptorGetRotationZ(player_state);
sp_rot.resize(3);
sp_rot[0]= sp_rot[1]= sp_rot[2]= 0.0f;
break;
case NCSM_Offset:
ae_rot[2]= ArrowEffects::ReceptorGetRotationZ(player_state);
m_rot_handler.EvalForReceptor(song_beat, sp_rot);
break;
case NCSM_Position:
m_rot_handler.EvalForReceptor(song_beat, sp_rot);
break;
}
switch(m_zoom_handler.m_spline_mode)
{
case NCSM_Disabled:
ae_zoom[0]= ae_zoom[1]= ae_zoom[2]= ArrowEffects::GetZoom(player_state);
sp_zoom.resize(3);
sp_zoom[0]= sp_zoom[1]= sp_zoom[2]= 0.0f;
break;
case NCSM_Offset:
ae_zoom[0]= ae_zoom[1]= ae_zoom[2]= ArrowEffects::GetZoom(player_state);
m_zoom_handler.EvalForReceptor(song_beat, sp_zoom);
break;
case NCSM_Position:
m_zoom_handler.EvalForReceptor(song_beat, sp_zoom);
break;
}
m_column_render_args.SetPRZForActor(receptor, sp_pos, ae_pos, sp_rot, ae_rot, sp_zoom, ae_zoom);
}
void NoteColumnRenderer::DrawPrimitives() void NoteColumnRenderer::DrawPrimitives()
{ {
m_render_args->spline= &m_spline; m_column_render_args.song_beat= m_field_render_args->player_state->GetDisplayedPosition().m_fSongBeatVisible;
m_render_args->receptor_t= m_receptor_t; m_column_render_args.pos_handler= &m_pos_handler;
m_render_args->beats_per_t= m_beats_per_t; m_column_render_args.rot_handler= &m_rot_handler;
m_render_args->first_beat= NoteRowToBeat(m_render_args->first_row); m_column_render_args.zoom_handler= &m_zoom_handler;
m_render_args->last_beat= NoteRowToBeat(m_render_args->last_row); m_field_render_args->first_beat= NoteRowToBeat(m_field_render_args->first_row);
m_field_render_args->last_beat= NoteRowToBeat(m_field_render_args->last_row);
bool any_upcoming= false; bool any_upcoming= false;
// Build lists of holds and taps for each player number, then pass those // Build lists of holds and taps for each player number, then pass those
// lists to the displays to draw. // lists to the displays to draw.
@@ -1101,8 +1413,8 @@ void NoteColumnRenderer::DrawPrimitives()
vector<vector<NoteData::TrackMap::const_iterator> > holds(PLAYER_INVALID+1); vector<vector<NoteData::TrackMap::const_iterator> > holds(PLAYER_INVALID+1);
vector<vector<NoteData::TrackMap::const_iterator> > taps(PLAYER_INVALID+1); vector<vector<NoteData::TrackMap::const_iterator> > taps(PLAYER_INVALID+1);
NoteData::TrackMap::const_iterator begin, end; NoteData::TrackMap::const_iterator begin, end;
m_render_args->note_data->GetTapNoteRangeInclusive(m_column, m_field_render_args->note_data->GetTapNoteRangeInclusive(m_column,
m_render_args->first_row, m_render_args->last_row+1, begin, end); m_field_render_args->first_row, m_field_render_args->last_row+1, begin, end);
for(; begin != end; ++begin) for(; begin != end; ++begin)
{ {
TapNote const& tn= begin->second; TapNote const& tn= begin->second;
@@ -1133,7 +1445,7 @@ void NoteColumnRenderer::DrawPrimitives()
#define DTS_INNER(pn, tap_set, draw_func, disp) \ #define DTS_INNER(pn, tap_set, draw_func, disp) \
if(!tap_set[pn].empty()) \ if(!tap_set[pn].empty()) \
{ \ { \
any_upcoming|= disp->draw_func(*m_render_args, m_column, tap_set[pn]); \ any_upcoming|= disp->draw_func(*m_field_render_args, m_column_render_args, tap_set[pn]); \
} }
#define DRAW_TAP_SET(tap_set, draw_func) \ #define DRAW_TAP_SET(tap_set, draw_func) \
FOREACH_PlayerNumber(pn) \ FOREACH_PlayerNumber(pn) \
@@ -1146,12 +1458,12 @@ void NoteColumnRenderer::DrawPrimitives()
DTS_INNER(PLAYER_INVALID, taps, DrawTapsInRange, m_displays[PLAYER_INVALID]); DTS_INNER(PLAYER_INVALID, taps, DrawTapsInRange, m_displays[PLAYER_INVALID]);
#undef DTS_INNER #undef DTS_INNER
#undef DRAW_TAP_SET #undef DRAW_TAP_SET
m_render_args->receptor_row->SetNoteUpcoming(m_column, any_upcoming); m_field_render_args->receptor_row->SetNoteUpcoming(m_column, any_upcoming);
} }
#include "LuaBinding.h" #include "LuaBinding.h"
struct LunaNoteColumnRenderer : Luna<NoteColumnRenderer> struct LunaNCSplineHandler : Luna<NCSplineHandler>
{ {
static int get_spline(T* p, lua_State* L) static int get_spline(T* p, lua_State* L)
{ {
@@ -1166,22 +1478,66 @@ struct LunaNoteColumnRenderer : Luna<NoteColumnRenderer>
p->member= FArg(1); \ p->member= FArg(1); \
COMMON_RETURN_SELF; \ COMMON_RETURN_SELF; \
} }
#define SET_B(member, name) \
static int name(T* p, lua_State* L) \
{ \
p->member= BArg(1); \
COMMON_RETURN_SELF; \
}
SET_T(m_receptor_t, set_receptor_t); SET_T(m_receptor_t, set_receptor_t);
SET_T(m_beats_per_t, set_beats_per_t); SET_T(m_beats_per_t, set_beats_per_t);
SET_B(m_subtract_song_beat_from_curr, set_subtract_song_beat);
#undef SET_T
#undef SET_B
static int set_spline_mode(T* p, lua_State* L)
{
p->m_spline_mode= Enum::Check<NoteColumnSplineMode>(L, 1);
COMMON_RETURN_SELF;
}
DEFINE_METHOD(get_spline_mode, m_spline_mode);
DEFINE_METHOD(get_subtract_song_beat, m_subtract_song_beat_from_curr);
LunaNCSplineHandler()
{
ADD_METHOD(get_spline);
ADD_METHOD(get_beats_per_t);
ADD_METHOD(set_beats_per_t);
ADD_METHOD(get_receptor_t);
ADD_METHOD(set_receptor_t);
ADD_METHOD(get_spline_mode);
ADD_METHOD(set_spline_mode);
ADD_METHOD(get_subtract_song_beat);
ADD_METHOD(set_subtract_song_beat);
}
};
LUA_REGISTER_CLASS(NCSplineHandler);
struct LunaNoteColumnRenderer : Luna<NoteColumnRenderer>
{
#define GET_HANDLER(member, name) \
static int name(T* p, lua_State* L) \
{ \
p->member.PushSelf(L); \
return 1; \
}
GET_HANDLER(m_pos_handler, get_pos_handler);
GET_HANDLER(m_rot_handler, get_rot_handler);
GET_HANDLER(m_zoom_handler, get_zoom_handler);
#undef GET_HANDLER
LunaNoteColumnRenderer() LunaNoteColumnRenderer()
{ {
ADD_METHOD(get_spline); ADD_METHOD(get_pos_handler);
ADD_METHOD(get_receptor_t); ADD_METHOD(get_rot_handler);
ADD_METHOD(get_beats_per_t); ADD_METHOD(get_zoom_handler);
ADD_METHOD(set_receptor_t);
ADD_METHOD(set_beats_per_t);
} }
}; };
LUA_REGISTER_DERIVED_CLASS(NoteColumnRenderer, Actor) LUA_REGISTER_DERIVED_CLASS(NoteColumnRenderer, Actor)
/* /*
* NoteColumnRenderer and associated spline stuff (c) Eric Reese 2014-2015
* (c) 2001-2006 Brian Bugh, Ben Nordstrom, Chris Danford, Steve Checkoway * (c) 2001-2006 Brian Bugh, Ben Nordstrom, Chris Danford, Steve Checkoway
* All rights reserved. * All rights reserved.
* *
+95 -31
View File
@@ -87,16 +87,24 @@ enum ActiveType
#define FOREACH_ActiveType( i ) FOREACH_ENUM( ActiveType, i ) #define FOREACH_ActiveType( i ) FOREACH_ENUM( ActiveType, i )
const RString &ActiveTypeToString( ActiveType at ); const RString &ActiveTypeToString( ActiveType at );
enum NoteColumnSplineMode
{
NCSM_Disabled,
NCSM_Offset,
NCSM_Position,
NUM_NoteColumnSplineMode,
NoteColumnSplineMode_Invalid
};
// A little pod struct to carry the data the NoteField needs to pass to the // A little pod struct to carry the data the NoteField needs to pass to the
// NoteDisplay during rendering. // NoteDisplay during rendering.
struct CommonColumnRenderArgs struct NoteFieldRenderArgs
{ {
PlayerState const* player_state; // to look up PlayerOptions
float reverse_offset_pixels;
ReceptorArrowRow* receptor_row; ReceptorArrowRow* receptor_row;
GhostArrowRow* ghost_row; GhostArrowRow* ghost_row;
NoteData const* note_data; NoteData const* note_data;
CubicSplineN const* spline;
float receptor_t;
float beats_per_t;
float first_beat; float first_beat;
float last_beat; float last_beat;
int first_row; int first_row;
@@ -110,6 +118,52 @@ struct CommonColumnRenderArgs
float fade_before_targets; float fade_before_targets;
}; };
// NCSplineHandler exists to allow NoteColumnRenderer to have separate
// splines for position, rotation, and zoom, while concisely presenting the
// same interface for all three.
struct NCSplineHandler
{
NCSplineHandler()
{
m_spline.redimension(3);
m_spline.m_owned_by_actor= true;
m_spline_mode= NCSM_Disabled;
m_receptor_t= 0.0f;
m_beats_per_t= 1.0f;
m_subtract_song_beat_from_curr= true;
}
float BeatToTValue(float song_beat, float note_beat) const;
void EvalForBeat(float song_beat, float note_beat, vector<float>& ret) const;
void EvalDerivForBeat(float song_beat, float note_beat, vector<float>& ret) const;
void EvalForReceptor(float song_beat, vector<float>& ret) const;
CubicSplineN m_spline;
NoteColumnSplineMode m_spline_mode;
float m_receptor_t;
float m_beats_per_t;
bool m_subtract_song_beat_from_curr;
void PushSelf(lua_State* L);
};
struct NoteColumnRenderArgs
{
void spae_pos_for_beat(PlayerState const* state,
float beat, float y_offset, float y_reverse_offset,
vector<float>& sp_pos, vector<float>& ae_pos) const;
void spae_zoom_for_beat(PlayerState const* state, float beat,
vector<float>& sp_zoom, vector<float>& ae_zoom) const;
void SetPRZForActor(Actor* actor,
vector<float> const& sp_pos, vector<float> const& ae_pos,
vector<float> const& sp_rot, vector<float> const& ae_rot,
vector<float> const& sp_zoom, vector<float> const& ae_zoom) const;
NCSplineHandler const* pos_handler;
NCSplineHandler const* rot_handler;
NCSplineHandler const* zoom_handler;
float song_beat;
int column;
};
/** @brief Draws TapNotes and HoldNotes. */ /** @brief Draws TapNotes and HoldNotes. */
class NoteDisplay class NoteDisplay
{ {
@@ -122,11 +176,12 @@ public:
static void Update( float fDeltaTime ); static void Update( float fDeltaTime );
bool IsOnScreen( float fBeat, int iCol, int iDrawDistanceAfterTargetsPixels, int iDrawDistanceBeforeTargetsPixels ) const; bool IsOnScreen( float fBeat, int iCol, int iDrawDistanceAfterTargetsPixels, int iDrawDistanceBeforeTargetsPixels ) const;
float BeatToTValue(CommonColumnRenderArgs const& args, float beat) const;
bool DrawHoldsInRange(CommonColumnRenderArgs const& args, int column, bool DrawHoldsInRange(NoteFieldRenderArgs const& field_args,
NoteColumnRenderArgs const& column_args,
vector<NoteData::TrackMap::const_iterator> const& tap_set); vector<NoteData::TrackMap::const_iterator> const& tap_set);
bool DrawTapsInRange(CommonColumnRenderArgs const& args, int column, bool DrawTapsInRange(NoteFieldRenderArgs const& field_args,
NoteColumnRenderArgs const& column_args,
vector<NoteData::TrackMap::const_iterator> const& tap_set); vector<NoteData::TrackMap::const_iterator> const& tap_set);
/** /**
* @brief Draw the TapNote onto the NoteField. * @brief Draw the TapNote onto the NoteField.
@@ -141,11 +196,13 @@ public:
* @param fDrawDistanceAfterTargetsPixels how much to draw after the receptors. * @param fDrawDistanceAfterTargetsPixels how much to draw after the receptors.
* @param fDrawDistanceBeforeTargetsPixels how much ot draw before the receptors. * @param fDrawDistanceBeforeTargetsPixels how much ot draw before the receptors.
* @param fFadeInPercentOfDrawFar when to start fading in. */ * @param fFadeInPercentOfDrawFar when to start fading in. */
void DrawTap(const TapNote& tn, CommonColumnRenderArgs const& args, void DrawTap(const TapNote& tn, NoteFieldRenderArgs const& field_args,
int iCol, float fBeat, bool bOnSameRowAsHoldStart, NoteColumnRenderArgs const& column_args, float fBeat,
bool bOnSameRowAsHoldStart,
bool bOnSameRowAsRollBeat, bool bIsAddition, float fPercentFadeToFail); bool bOnSameRowAsRollBeat, bool bIsAddition, float fPercentFadeToFail);
void DrawHold(const TapNote& tn, CommonColumnRenderArgs const& args, void DrawHold(const TapNote& tn, NoteFieldRenderArgs const& field_args,
int iCol, int iRow, bool bIsBeingHeld, const HoldNoteResult &Result, NoteColumnRenderArgs const& column_args, int iRow, bool bIsBeingHeld,
const HoldNoteResult &Result,
bool bIsAddition, float fPercentFadeToFail); bool bIsAddition, float fPercentFadeToFail);
bool DrawHoldHeadForTapsOnSameRow() const; bool DrawHoldHeadForTapsOnSameRow() const;
@@ -158,19 +215,22 @@ private:
Sprite *GetHoldSprite( NoteColorSprite ncs[NUM_HoldType][NUM_ActiveType], NotePart part, float fNoteBeat, bool bIsRoll, bool bIsBeingHeld ); Sprite *GetHoldSprite( NoteColorSprite ncs[NUM_HoldType][NUM_ActiveType], NotePart part, float fNoteBeat, bool bIsRoll, bool bIsBeingHeld );
void DrawActor(const TapNote& tn, Actor* pActor, NotePart part, void DrawActor(const TapNote& tn, Actor* pActor, NotePart part,
CommonColumnRenderArgs const& args, int iCol, float fYOffset, float fBeat, NoteFieldRenderArgs const& field_args,
NoteColumnRenderArgs const& column_args, float fYOffset, float fBeat,
bool bIsAddition, float fPercentFadeToFail, float fColorScale, bool bIsAddition, float fPercentFadeToFail, float fColorScale,
bool is_being_held); bool is_being_held);
void DrawHoldBody(const TapNote& tn, CommonColumnRenderArgs const& args, void DrawHoldBody(const TapNote& tn, NoteFieldRenderArgs const& field_args,
int iCol, float fBeat, bool bIsBeingHeld, float fYHead, float fYTail, NoteColumnRenderArgs const& column_args, float fBeat, bool bIsBeingHeld,
float fYHead, float fYTail,
bool bIsAddition, float fPercentFadeToFail, float fColorScale, bool bIsAddition, float fPercentFadeToFail, float fColorScale,
bool bGlow, float top_t, float bottom_t); bool bGlow, float top_beat, float bottom_beat);
void DrawHoldPart(vector<Sprite*> &vpSpr, void DrawHoldPart(vector<Sprite*> &vpSpr,
CommonColumnRenderArgs const& args, int iCol, int fYStep, NoteFieldRenderArgs const& field_args,
NoteColumnRenderArgs const& column_args, int fYStep,
float fPercentFadeToFail, float fColorScale, bool bGlow, float fPercentFadeToFail, float fColorScale, bool bGlow,
float fOverlappedTime, float fYTop, float fYBottom, float fYStartPos, float fOverlappedTime, float fYTop, float fYBottom, float fYStartPos,
float fYEndPos, bool bWrapping, bool bAnchorToTop, float fYEndPos, bool bWrapping, bool bAnchorToTop,
bool bFlipTextureVertically, float top_t, float bottom_t); bool bFlipTextureVertically, float top_beat, float bottom_beat);
const PlayerState *m_pPlayerState; // to look up PlayerOptions const PlayerState *m_pPlayerState; // to look up PlayerOptions
NoteMetricCache_t *cache; NoteMetricCache_t *cache;
@@ -196,21 +256,24 @@ private:
// actors so they can move with the rest of the column. I didn't use // actors so they can move with the rest of the column. I didn't use
// ActorProxy because the receptor/ghost actors need to pull in the parent // ActorProxy because the receptor/ghost actors need to pull in the parent
// state of their rows and the parent state of the column. -Kyz // state of their rows and the parent state of the column. -Kyz
class NoteColumnRenderer : public Actor
{
public:
NoteColumnRenderer()
{
m_spline.redimension(3);
m_spline.m_owned_by_actor= true;
}
NoteDisplay* m_displays[PLAYER_INVALID+1];
CommonColumnRenderArgs* m_render_args;
int m_column;
float m_receptor_t;
float m_beats_per_t;
CubicSplineN m_spline;
struct NoteColumnRenderer : public Actor
{
NoteDisplay* m_displays[PLAYER_INVALID+1];
NoteFieldRenderArgs* m_field_render_args;
NoteColumnRenderArgs m_column_render_args;
int m_column;
// m_column_render_args has pointers to these fields that are updated
// every frame instead of using m_column_render_args directly so that they
// can be moved into a tween state later. -Kyz
NCSplineHandler m_pos_handler;
NCSplineHandler m_rot_handler;
NCSplineHandler m_zoom_handler;
// UpdateReceptorGhostStuff takes care of the logic for making the ghost
// and receptor positions follow the splines. It's called by their row
// update functions. -Kyz
void UpdateReceptorGhostStuff(Actor* receptor) const;
virtual void DrawPrimitives(); virtual void DrawPrimitives();
virtual void PushSelf(lua_State* L); virtual void PushSelf(lua_State* L);
}; };
@@ -218,6 +281,7 @@ class NoteColumnRenderer : public Actor
#endif #endif
/** /**
* NoteColumnRenderer and associated spline stuff (c) Eric Reese 2014-2015
* @file * @file
* @author Brian Bugh, Ben Nordstrom, Chris Danford, Steve Checkoway (c) 2001-2006 * @author Brian Bugh, Ben Nordstrom, Chris Danford, Steve Checkoway (c) 2001-2006
* @section LICENSE * @section LICENSE
+53 -50
View File
@@ -63,11 +63,11 @@ NoteField::NoteField()
// I decided to do it this way because I don't want to dig through // I decided to do it this way because I don't want to dig through
// ScreenEdit to change all the places it touches the markers. -Kyz // ScreenEdit to change all the places it touches the markers. -Kyz
m_ColumnRenderArgs.selection_begin_marker= &m_iBeginMarker; m_FieldRenderArgs.selection_begin_marker= &m_iBeginMarker;
m_ColumnRenderArgs.selection_end_marker= &m_iEndMarker; m_FieldRenderArgs.selection_end_marker= &m_iEndMarker;
m_iBeginMarker = m_iEndMarker = -1; m_iBeginMarker = m_iEndMarker = -1;
m_ColumnRenderArgs.fail_fade = -1; m_FieldRenderArgs.fail_fade = -1;
m_StepCallback.SetFromNil(); m_StepCallback.SetFromNil();
m_SetPressedCallback.SetFromNil(); m_SetPressedCallback.SetFromNil();
@@ -192,7 +192,7 @@ void NoteField::Load(
m_iDrawDistanceBeforeTargetsPixels = iDrawDistanceBeforeTargetsPixels; m_iDrawDistanceBeforeTargetsPixels = iDrawDistanceBeforeTargetsPixels;
ASSERT( m_iDrawDistanceBeforeTargetsPixels >= m_iDrawDistanceAfterTargetsPixels ); ASSERT( m_iDrawDistanceBeforeTargetsPixels >= m_iDrawDistanceAfterTargetsPixels );
m_ColumnRenderArgs.fail_fade = -1; m_FieldRenderArgs.fail_fade = -1;
//int i1 = m_pNoteData->GetNumTracks(); //int i1 = m_pNoteData->GetNumTracks();
//int i2 = GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer; //int i2 = GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer;
@@ -248,9 +248,11 @@ void NoteField::Load(
void NoteField::InitColumnRenderers() void NoteField::InitColumnRenderers()
{ {
m_ColumnRenderArgs.receptor_row= &(m_pCurDisplay->m_ReceptorArrowRow); m_FieldRenderArgs.player_state= m_pPlayerState;
m_ColumnRenderArgs.ghost_row= &(m_pCurDisplay->m_GhostArrowRow); m_FieldRenderArgs.reverse_offset_pixels= m_fYReverseOffsetPixels;
m_ColumnRenderArgs.note_data= m_pNoteData; m_FieldRenderArgs.receptor_row= &(m_pCurDisplay->m_ReceptorArrowRow);
m_FieldRenderArgs.ghost_row= &(m_pCurDisplay->m_GhostArrowRow);
m_FieldRenderArgs.note_data= m_pNoteData;
m_ColumnRenderers.resize(GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer); m_ColumnRenderers.resize(GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer);
for(size_t ncr= 0; ncr < m_ColumnRenderers.size(); ++ncr) for(size_t ncr= 0; ncr < m_ColumnRenderers.size(); ++ncr)
{ {
@@ -260,7 +262,8 @@ void NoteField::InitColumnRenderers()
} }
m_ColumnRenderers[ncr].m_displays[PLAYER_INVALID]= &(m_pCurDisplay->display[ncr]); m_ColumnRenderers[ncr].m_displays[PLAYER_INVALID]= &(m_pCurDisplay->display[ncr]);
m_ColumnRenderers[ncr].m_column= ncr; m_ColumnRenderers[ncr].m_column= ncr;
m_ColumnRenderers[ncr].m_render_args= &m_ColumnRenderArgs; m_ColumnRenderers[ncr].m_column_render_args.column= ncr;
m_ColumnRenderers[ncr].m_field_render_args= &m_FieldRenderArgs;
} }
m_pCurDisplay->m_ReceptorArrowRow.SetColumnRenderers(m_ColumnRenderers); m_pCurDisplay->m_ReceptorArrowRow.SetColumnRenderers(m_ColumnRenderers);
m_pCurDisplay->m_GhostArrowRow.SetColumnRenderers(m_ColumnRenderers); m_pCurDisplay->m_GhostArrowRow.SetColumnRenderers(m_ColumnRenderers);
@@ -305,11 +308,11 @@ void NoteField::Update( float fDeltaTime )
cur->m_ReceptorArrowRow.Update( fDeltaTime ); cur->m_ReceptorArrowRow.Update( fDeltaTime );
cur->m_GhostArrowRow.Update( fDeltaTime ); cur->m_GhostArrowRow.Update( fDeltaTime );
if( m_ColumnRenderArgs.fail_fade >= 0 ) if( m_FieldRenderArgs.fail_fade >= 0 )
m_ColumnRenderArgs.fail_fade = min( m_ColumnRenderArgs.fail_fade + fDeltaTime/FADE_FAIL_TIME, 1 ); m_FieldRenderArgs.fail_fade = min( m_FieldRenderArgs.fail_fade + fDeltaTime/FADE_FAIL_TIME, 1 );
// Update fade to failed // Update fade to failed
m_pCurDisplay->m_ReceptorArrowRow.SetFadeToFailPercent( m_ColumnRenderArgs.fail_fade ); m_pCurDisplay->m_ReceptorArrowRow.SetFadeToFailPercent( m_FieldRenderArgs.fail_fade );
NoteDisplay::Update( fDeltaTime ); NoteDisplay::Update( fDeltaTime );
/* Update all NoteDisplays. Hack: We need to call this once per frame, not /* Update all NoteDisplays. Hack: We need to call this once per frame, not
@@ -861,41 +864,41 @@ void NoteField::DrawPrimitives()
const PlayerOptions &current_po = m_pPlayerState->m_PlayerOptions.GetCurrent(); const PlayerOptions &current_po = m_pPlayerState->m_PlayerOptions.GetCurrent();
// Adjust draw range depending on some effects // Adjust draw range depending on some effects
m_ColumnRenderArgs.draw_pixels_after_targets= m_iDrawDistanceAfterTargetsPixels; m_FieldRenderArgs.draw_pixels_after_targets= m_iDrawDistanceAfterTargetsPixels;
// HACK: If boomerang and centered are on, then we want to draw much // HACK: If boomerang and centered are on, then we want to draw much
// earlier so that the notes don't pop on screen. // earlier so that the notes don't pop on screen.
float fCenteredTimesBoomerang = float fCenteredTimesBoomerang =
current_po.m_fScrolls[PlayerOptions::SCROLL_CENTERED] * current_po.m_fScrolls[PlayerOptions::SCROLL_CENTERED] *
current_po.m_fAccels[PlayerOptions::ACCEL_BOOMERANG]; current_po.m_fAccels[PlayerOptions::ACCEL_BOOMERANG];
m_ColumnRenderArgs.draw_pixels_after_targets += int(SCALE( fCenteredTimesBoomerang, 0.f, 1.f, 0.f, -SCREEN_HEIGHT/2 )); m_FieldRenderArgs.draw_pixels_after_targets += int(SCALE( fCenteredTimesBoomerang, 0.f, 1.f, 0.f, -SCREEN_HEIGHT/2 ));
m_ColumnRenderArgs.draw_pixels_before_targets = m_iDrawDistanceBeforeTargetsPixels; m_FieldRenderArgs.draw_pixels_before_targets = m_iDrawDistanceBeforeTargetsPixels;
float fDrawScale = 1; float fDrawScale = 1;
fDrawScale *= 1 + 0.5f * fabsf( current_po.m_fPerspectiveTilt ); fDrawScale *= 1 + 0.5f * fabsf( current_po.m_fPerspectiveTilt );
fDrawScale *= 1 + fabsf( current_po.m_fEffects[PlayerOptions::EFFECT_MINI] ); fDrawScale *= 1 + fabsf( current_po.m_fEffects[PlayerOptions::EFFECT_MINI] );
m_ColumnRenderArgs.draw_pixels_after_targets = (int)(m_ColumnRenderArgs.draw_pixels_after_targets * fDrawScale); m_FieldRenderArgs.draw_pixels_after_targets = (int)(m_FieldRenderArgs.draw_pixels_after_targets * fDrawScale);
m_ColumnRenderArgs.draw_pixels_before_targets = (int)(m_ColumnRenderArgs.draw_pixels_before_targets * fDrawScale); m_FieldRenderArgs.draw_pixels_before_targets = (int)(m_FieldRenderArgs.draw_pixels_before_targets * fDrawScale);
// Probe for first and last notes on the screen // Probe for first and last notes on the screen
float fFirstBeatToDraw = FindFirstDisplayedBeat( m_pPlayerState, m_ColumnRenderArgs.draw_pixels_after_targets ); float fFirstBeatToDraw = FindFirstDisplayedBeat( m_pPlayerState, m_FieldRenderArgs.draw_pixels_after_targets );
float fLastBeatToDraw = FindLastDisplayedBeat( m_pPlayerState, m_ColumnRenderArgs.draw_pixels_before_targets ); float fLastBeatToDraw = FindLastDisplayedBeat( m_pPlayerState, m_FieldRenderArgs.draw_pixels_before_targets );
m_pPlayerState->m_fLastDrawnBeat = fLastBeatToDraw; m_pPlayerState->m_fLastDrawnBeat = fLastBeatToDraw;
m_ColumnRenderArgs.first_row = BeatToNoteRow(fFirstBeatToDraw); m_FieldRenderArgs.first_row = BeatToNoteRow(fFirstBeatToDraw);
m_ColumnRenderArgs.last_row = BeatToNoteRow(fLastBeatToDraw); m_FieldRenderArgs.last_row = BeatToNoteRow(fLastBeatToDraw);
//LOG->Trace( "start = %f.1, end = %f.1", fFirstBeatToDraw-fSongBeat, fLastBeatToDraw-fSongBeat ); //LOG->Trace( "start = %f.1, end = %f.1", fFirstBeatToDraw-fSongBeat, fLastBeatToDraw-fSongBeat );
//LOG->Trace( "Drawing elements %d through %d", m_ColumnRenderArgs.first_row, m_ColumnRenderArgs.last_row ); //LOG->Trace( "Drawing elements %d through %d", m_FieldRenderArgs.first_row, m_FieldRenderArgs.last_row );
#define IS_ON_SCREEN( fBeat ) ( fFirstBeatToDraw <= (fBeat) && (fBeat) <= fLastBeatToDraw && IsOnScreen( fBeat, 0, m_ColumnRenderArgs.draw_pixels_after_targets, m_ColumnRenderArgs.draw_pixels_before_targets ) ) #define IS_ON_SCREEN( fBeat ) ( fFirstBeatToDraw <= (fBeat) && (fBeat) <= fLastBeatToDraw && IsOnScreen( fBeat, 0, m_FieldRenderArgs.draw_pixels_after_targets, m_FieldRenderArgs.draw_pixels_before_targets ) )
// Draw board // Draw board
if( SHOW_BOARD ) if( SHOW_BOARD )
{ {
DrawBoard( m_ColumnRenderArgs.draw_pixels_after_targets, m_ColumnRenderArgs.draw_pixels_before_targets ); DrawBoard( m_FieldRenderArgs.draw_pixels_after_targets, m_FieldRenderArgs.draw_pixels_before_targets );
} }
// Draw Receptors // Draw Receptors
@@ -918,7 +921,7 @@ void NoteField::DrawPrimitives()
for (i = 0; i < tSigs.size(); i++) for (i = 0; i < tSigs.size(); i++)
{ {
const TimeSignatureSegment *ts = ToTimeSignature(tSigs[i]); const TimeSignatureSegment *ts = ToTimeSignature(tSigs[i]);
int iSegmentEndRow = (i + 1 == tSigs.size()) ? m_ColumnRenderArgs.last_row : tSigs[i+1]->GetRow(); int iSegmentEndRow = (i + 1 == tSigs.size()) ? m_FieldRenderArgs.last_row : tSigs[i+1]->GetRow();
// beat bars every 16th note // beat bars every 16th note
int iDrawBeatBarsEveryRows = BeatToNoteRow( ((float)ts->GetDen()) / 4 ) / 4; int iDrawBeatBarsEveryRows = BeatToNoteRow( ((float)ts->GetDen()) / 4 ) / 4;
@@ -961,7 +964,7 @@ void NoteField::DrawPrimitives()
for (i = 0; i < segs[SEGMENT_SCROLL]->size(); i++) for (i = 0; i < segs[SEGMENT_SCROLL]->size(); i++)
{ {
ScrollSegment *seg = ToScroll( segs[SEGMENT_SCROLL]->at(i) ); ScrollSegment *seg = ToScroll( segs[SEGMENT_SCROLL]->at(i) );
if( seg->GetRow() >= m_ColumnRenderArgs.first_row && seg->GetRow() <= m_ColumnRenderArgs.last_row ) if( seg->GetRow() >= m_FieldRenderArgs.first_row && seg->GetRow() <= m_FieldRenderArgs.last_row )
{ {
float fBeat = seg->GetBeat(); float fBeat = seg->GetBeat();
if( IS_ON_SCREEN(fBeat) ) if( IS_ON_SCREEN(fBeat) )
@@ -973,7 +976,7 @@ void NoteField::DrawPrimitives()
for (i = 0; i < segs[SEGMENT_BPM]->size(); i++) for (i = 0; i < segs[SEGMENT_BPM]->size(); i++)
{ {
const BPMSegment *seg = ToBPM( segs[SEGMENT_BPM]->at(i) ); const BPMSegment *seg = ToBPM( segs[SEGMENT_BPM]->at(i) );
if( seg->GetRow() >= m_ColumnRenderArgs.first_row && seg->GetRow() <= m_ColumnRenderArgs.last_row ) if( seg->GetRow() >= m_FieldRenderArgs.first_row && seg->GetRow() <= m_FieldRenderArgs.last_row )
{ {
float fBeat = seg->GetBeat(); float fBeat = seg->GetBeat();
if( IS_ON_SCREEN(fBeat) ) if( IS_ON_SCREEN(fBeat) )
@@ -985,7 +988,7 @@ void NoteField::DrawPrimitives()
for (i = 0; i < segs[SEGMENT_STOP]->size(); i++) for (i = 0; i < segs[SEGMENT_STOP]->size(); i++)
{ {
const StopSegment *seg = ToStop( segs[SEGMENT_STOP]->at(i) ); const StopSegment *seg = ToStop( segs[SEGMENT_STOP]->at(i) );
if( seg->GetRow() >= m_ColumnRenderArgs.first_row && seg->GetRow() <= m_ColumnRenderArgs.last_row ) if( seg->GetRow() >= m_FieldRenderArgs.first_row && seg->GetRow() <= m_FieldRenderArgs.last_row )
{ {
float fBeat = seg->GetBeat(); float fBeat = seg->GetBeat();
if( IS_ON_SCREEN(fBeat) ) if( IS_ON_SCREEN(fBeat) )
@@ -997,7 +1000,7 @@ void NoteField::DrawPrimitives()
for (i = 0; i < segs[SEGMENT_DELAY]->size(); i++) for (i = 0; i < segs[SEGMENT_DELAY]->size(); i++)
{ {
const DelaySegment *seg = ToDelay( segs[SEGMENT_DELAY]->at(i) ); const DelaySegment *seg = ToDelay( segs[SEGMENT_DELAY]->at(i) );
if( seg->GetRow() >= m_ColumnRenderArgs.first_row && seg->GetRow() <= m_ColumnRenderArgs.last_row ) if( seg->GetRow() >= m_FieldRenderArgs.first_row && seg->GetRow() <= m_FieldRenderArgs.last_row )
{ {
float fBeat = seg->GetBeat(); float fBeat = seg->GetBeat();
if( IS_ON_SCREEN(fBeat) ) if( IS_ON_SCREEN(fBeat) )
@@ -1009,7 +1012,7 @@ void NoteField::DrawPrimitives()
for (i = 0; i < segs[SEGMENT_WARP]->size(); i++) for (i = 0; i < segs[SEGMENT_WARP]->size(); i++)
{ {
const WarpSegment *seg = ToWarp( segs[SEGMENT_WARP]->at(i) ); const WarpSegment *seg = ToWarp( segs[SEGMENT_WARP]->at(i) );
if( seg->GetRow() >= m_ColumnRenderArgs.first_row && seg->GetRow() <= m_ColumnRenderArgs.last_row ) if( seg->GetRow() >= m_FieldRenderArgs.first_row && seg->GetRow() <= m_FieldRenderArgs.last_row )
{ {
float fBeat = seg->GetBeat(); float fBeat = seg->GetBeat();
if( IS_ON_SCREEN(fBeat) ) if( IS_ON_SCREEN(fBeat) )
@@ -1021,7 +1024,7 @@ void NoteField::DrawPrimitives()
for (i = 0; i < segs[SEGMENT_TIME_SIG]->size(); i++) for (i = 0; i < segs[SEGMENT_TIME_SIG]->size(); i++)
{ {
const TimeSignatureSegment *seg = ToTimeSignature( segs[SEGMENT_TIME_SIG]->at(i) ); const TimeSignatureSegment *seg = ToTimeSignature( segs[SEGMENT_TIME_SIG]->at(i) );
if( seg->GetRow() >= m_ColumnRenderArgs.first_row && seg->GetRow() <= m_ColumnRenderArgs.last_row ) if( seg->GetRow() >= m_FieldRenderArgs.first_row && seg->GetRow() <= m_FieldRenderArgs.last_row )
{ {
float fBeat = seg->GetBeat(); float fBeat = seg->GetBeat();
if( IS_ON_SCREEN(fBeat) ) if( IS_ON_SCREEN(fBeat) )
@@ -1033,7 +1036,7 @@ void NoteField::DrawPrimitives()
for (i = 0; i < segs[SEGMENT_TICKCOUNT]->size(); i++) for (i = 0; i < segs[SEGMENT_TICKCOUNT]->size(); i++)
{ {
const TickcountSegment *seg = ToTickcount( segs[SEGMENT_TICKCOUNT]->at(i) ); const TickcountSegment *seg = ToTickcount( segs[SEGMENT_TICKCOUNT]->at(i) );
if( seg->GetRow() >= m_ColumnRenderArgs.first_row && seg->GetRow() <= m_ColumnRenderArgs.last_row ) if( seg->GetRow() >= m_FieldRenderArgs.first_row && seg->GetRow() <= m_FieldRenderArgs.last_row )
{ {
float fBeat = seg->GetBeat(); float fBeat = seg->GetBeat();
if( IS_ON_SCREEN(fBeat) ) if( IS_ON_SCREEN(fBeat) )
@@ -1045,7 +1048,7 @@ void NoteField::DrawPrimitives()
for (i = 0; i < segs[SEGMENT_COMBO]->size(); i++) for (i = 0; i < segs[SEGMENT_COMBO]->size(); i++)
{ {
const ComboSegment *seg = ToCombo( segs[SEGMENT_COMBO]->at(i) ); const ComboSegment *seg = ToCombo( segs[SEGMENT_COMBO]->at(i) );
if( seg->GetRow() >= m_ColumnRenderArgs.first_row && seg->GetRow() <= m_ColumnRenderArgs.last_row ) if( seg->GetRow() >= m_FieldRenderArgs.first_row && seg->GetRow() <= m_FieldRenderArgs.last_row )
{ {
float fBeat = seg->GetBeat(); float fBeat = seg->GetBeat();
if( IS_ON_SCREEN(fBeat) ) if( IS_ON_SCREEN(fBeat) )
@@ -1057,7 +1060,7 @@ void NoteField::DrawPrimitives()
for (i = 0; i < segs[SEGMENT_LABEL]->size(); i++) for (i = 0; i < segs[SEGMENT_LABEL]->size(); i++)
{ {
const LabelSegment *seg = ToLabel( segs[SEGMENT_LABEL]->at(i) ); const LabelSegment *seg = ToLabel( segs[SEGMENT_LABEL]->at(i) );
if( seg->GetRow() >= m_ColumnRenderArgs.first_row && seg->GetRow() <= m_ColumnRenderArgs.last_row ) if( seg->GetRow() >= m_FieldRenderArgs.first_row && seg->GetRow() <= m_FieldRenderArgs.last_row )
{ {
float fBeat = seg->GetBeat(); float fBeat = seg->GetBeat();
if( IS_ON_SCREEN(fBeat) ) if( IS_ON_SCREEN(fBeat) )
@@ -1069,7 +1072,7 @@ void NoteField::DrawPrimitives()
for (i = 0; i < segs[SEGMENT_SPEED]->size(); i++) for (i = 0; i < segs[SEGMENT_SPEED]->size(); i++)
{ {
const SpeedSegment *seg = ToSpeed( segs[SEGMENT_SPEED]->at(i) ); const SpeedSegment *seg = ToSpeed( segs[SEGMENT_SPEED]->at(i) );
if( seg->GetRow() >= m_ColumnRenderArgs.first_row && seg->GetRow() <= m_ColumnRenderArgs.last_row ) if( seg->GetRow() >= m_FieldRenderArgs.first_row && seg->GetRow() <= m_FieldRenderArgs.last_row )
{ {
float fBeat = seg->GetBeat(); float fBeat = seg->GetBeat();
if( IS_ON_SCREEN(fBeat) ) if( IS_ON_SCREEN(fBeat) )
@@ -1082,7 +1085,7 @@ void NoteField::DrawPrimitives()
for (i = 0; i < segs[SEGMENT_FAKE]->size(); i++) for (i = 0; i < segs[SEGMENT_FAKE]->size(); i++)
{ {
const FakeSegment *seg = ToFake( segs[SEGMENT_FAKE]->at(i) ); const FakeSegment *seg = ToFake( segs[SEGMENT_FAKE]->at(i) );
if( seg->GetRow() >= m_ColumnRenderArgs.first_row && seg->GetRow() <= m_ColumnRenderArgs.last_row ) if( seg->GetRow() >= m_FieldRenderArgs.first_row && seg->GetRow() <= m_FieldRenderArgs.last_row )
{ {
float fBeat = seg->GetBeat(); float fBeat = seg->GetBeat();
if( IS_ON_SCREEN(fBeat) ) if( IS_ON_SCREEN(fBeat) )
@@ -1102,8 +1105,8 @@ void NoteField::DrawPrimitives()
float fSecond = a->fStartSecond; float fSecond = a->fStartSecond;
float fBeat = timing.GetBeatFromElapsedTime( fSecond ); float fBeat = timing.GetBeatFromElapsedTime( fSecond );
if( BeatToNoteRow(fBeat) >= m_ColumnRenderArgs.first_row && if( BeatToNoteRow(fBeat) >= m_FieldRenderArgs.first_row &&
BeatToNoteRow(fBeat) <= m_ColumnRenderArgs.last_row) BeatToNoteRow(fBeat) <= m_FieldRenderArgs.last_row)
{ {
if( IS_ON_SCREEN(fBeat) ) if( IS_ON_SCREEN(fBeat) )
DrawAttackText( fBeat, *a ); DrawAttackText( fBeat, *a );
@@ -1121,8 +1124,8 @@ void NoteField::DrawPrimitives()
FOREACH_CONST(Attack, attacks, a) FOREACH_CONST(Attack, attacks, a)
{ {
float fBeat = timing.GetBeatFromElapsedTime(a->fStartSecond); float fBeat = timing.GetBeatFromElapsedTime(a->fStartSecond);
if (BeatToNoteRow(fBeat) >= m_ColumnRenderArgs.first_row && if (BeatToNoteRow(fBeat) >= m_FieldRenderArgs.first_row &&
BeatToNoteRow(fBeat) <= m_ColumnRenderArgs.last_row && BeatToNoteRow(fBeat) <= m_FieldRenderArgs.last_row &&
IS_ON_SCREEN(fBeat)) IS_ON_SCREEN(fBeat))
{ {
this->DrawAttackText(fBeat, *a); this->DrawAttackText(fBeat, *a);
@@ -1206,20 +1209,20 @@ void NoteField::DrawPrimitives()
{ {
int iBegin = m_iBeginMarker; int iBegin = m_iBeginMarker;
int iEnd = m_iEndMarker; int iEnd = m_iEndMarker;
CLAMP( iBegin, m_ColumnRenderArgs.first_row, m_ColumnRenderArgs.last_row ); CLAMP( iBegin, m_FieldRenderArgs.first_row, m_FieldRenderArgs.last_row );
CLAMP( iEnd, m_ColumnRenderArgs.first_row, m_ColumnRenderArgs.last_row ); CLAMP( iEnd, m_FieldRenderArgs.first_row, m_FieldRenderArgs.last_row );
DrawAreaHighlight( iBegin, iEnd ); DrawAreaHighlight( iBegin, iEnd );
} }
else if( m_iBeginMarker != -1 ) else if( m_iBeginMarker != -1 )
{ {
if( m_iBeginMarker >= m_ColumnRenderArgs.first_row && if( m_iBeginMarker >= m_FieldRenderArgs.first_row &&
m_iBeginMarker <= m_ColumnRenderArgs.last_row ) m_iBeginMarker <= m_FieldRenderArgs.last_row )
DrawMarkerBar( m_iBeginMarker ); DrawMarkerBar( m_iBeginMarker );
} }
else if( m_iEndMarker != -1 ) else if( m_iEndMarker != -1 )
{ {
if( m_iEndMarker >= m_ColumnRenderArgs.first_row && if( m_iEndMarker >= m_FieldRenderArgs.first_row &&
m_iEndMarker <= m_ColumnRenderArgs.last_row ) m_iEndMarker <= m_FieldRenderArgs.last_row )
DrawMarkerBar( m_iEndMarker ); DrawMarkerBar( m_iEndMarker );
} }
} }
@@ -1233,9 +1236,9 @@ void NoteField::DrawPrimitives()
ssprintf("NumTracks %d != ColsPerPlayer %d",m_pNoteData->GetNumTracks(), ssprintf("NumTracks %d != ColsPerPlayer %d",m_pNoteData->GetNumTracks(),
GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer)); GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer));
m_ColumnRenderArgs.selection_glow= SCALE( m_FieldRenderArgs.selection_glow= SCALE(
RageFastCos(RageTimer::GetTimeSinceStartFast()*2), -1, 1, 0.1f, 0.3f); RageFastCos(RageTimer::GetTimeSinceStartFast()*2), -1, 1, 0.1f, 0.3f);
m_ColumnRenderArgs.fade_before_targets= FADE_BEFORE_TARGETS_PERCENT; m_FieldRenderArgs.fade_before_targets= FADE_BEFORE_TARGETS_PERCENT;
for( int j=0; j<m_pNoteData->GetNumTracks(); j++ ) // for each arrow column for( int j=0; j<m_pNoteData->GetNumTracks(); j++ ) // for each arrow column
{ {
@@ -1248,7 +1251,7 @@ void NoteField::DrawPrimitives()
void NoteField::FadeToFail() void NoteField::FadeToFail()
{ {
m_ColumnRenderArgs.fail_fade = max( 0.0f, m_ColumnRenderArgs.fail_fade ); // this will slowly increase every Update() m_FieldRenderArgs.fail_fade = max( 0.0f, m_FieldRenderArgs.fail_fade ); // this will slowly increase every Update()
// don't fade all over again if this is called twice // don't fade all over again if this is called twice
} }
@@ -1446,7 +1449,7 @@ public:
return 0; return 0;
} }
static int GetColumnActors(T* p, lua_State* L) static int get_column_actors(T* p, lua_State* L)
{ {
lua_createtable(L, p->m_ColumnRenderers.size(), 0); lua_createtable(L, p->m_ColumnRenderers.size(), 0);
for(size_t i= 0; i < p->m_ColumnRenderers.size(); ++i) for(size_t i= 0; i < p->m_ColumnRenderers.size(); ++i)
@@ -1467,7 +1470,7 @@ public:
ADD_METHOD(SetPressed); ADD_METHOD(SetPressed);
ADD_METHOD(DidTapNote); ADD_METHOD(DidTapNote);
ADD_METHOD(DidHoldNote); ADD_METHOD(DidHoldNote);
ADD_METHOD(GetColumnActors); ADD_METHOD(get_column_actors);
} }
}; };
+1 -1
View File
@@ -105,7 +105,7 @@ protected:
~NoteDisplayCols() { delete [] display; } ~NoteDisplayCols() { delete [] display; }
}; };
CommonColumnRenderArgs m_ColumnRenderArgs; NoteFieldRenderArgs m_FieldRenderArgs;
/* All loaded note displays, mapped by their name. */ /* All loaded note displays, mapped by their name. */
map<RString, NoteDisplayCols *> m_NoteDisplays; map<RString, NoteDisplayCols *> m_NoteDisplays;
+31
View File
@@ -41,6 +41,22 @@ void RageVec3Normalize( RageVector3* pOut, const RageVector3* pV )
pOut->z = pV->z * scale; pOut->z = pV->z * scale;
} }
void VectorFloatNormalize(vector<float>& v)
{
ASSERT_M(v.size() == 3, "Can't normalize a non-3D vector.");
float scale = 1.0f / sqrtf(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
v[0]*= scale;
v[1]*= scale;
v[2]*= scale;
}
void RageVec3Cross(RageVector3* ret, RageVector3 const* a, RageVector3 const* b)
{
ret->x= (a->y * b->z) - (a->z * b->y);
ret->y= ((a->x * b->z) - (a->z * b->x));
ret->z= (a->x * b->y) - (a->y * b->x);
}
void RageVec3TransformCoord( RageVector3* pOut, const RageVector3* pV, const RageMatrix* pM ) void RageVec3TransformCoord( RageVector3* pOut, const RageVector3* pV, const RageMatrix* pM )
{ {
RageVector4 temp( pV->x, pV->y, pV->z, 1.0f ); // translate RageVector4 temp( pV->x, pV->y, pV->z, 1.0f ); // translate
@@ -314,6 +330,21 @@ void RageMatrixRotationXYZ( RageMatrix* pOut, float rX, float rY, float rZ )
pOut->m33 = 1; pOut->m33 = 1;
} }
void RageAARotate(RageVector3* inret, RageVector3 const* axis, float angle)
{
float ha= angle/2.0f;
float ca2= RageFastCos(ha);
float sa2= RageFastSin(ha);
RageVector4 quat(axis->x * sa2, axis->y * sa2, axis->z * sa2, ca2);
RageVector4 quatc(-quat.x, -quat.y, -quat.z, ca2);
RageVector4 point(inret->x, inret->y, inret->z, 0.0f);
RageQuatMultiply(&point, quat, point);
RageQuatMultiply(&point, point, quatc);
inret->x= point.x;
inret->y= point.y;
inret->z= point.z;
}
void RageQuatMultiply( RageVector4* pOut, const RageVector4 &pA, const RageVector4 &pB ) void RageQuatMultiply( RageVector4* pOut, const RageVector4 &pA, const RageVector4 &pB )
{ {
RageVector4 out; RageVector4 out;
+3
View File
@@ -18,6 +18,8 @@ void RageVec3AddToBounds( const RageVector3 &p, RageVector3 &mins, RageVector3 &
void RageVec2Normalize( RageVector2* pOut, const RageVector2* pV ); void RageVec2Normalize( RageVector2* pOut, const RageVector2* pV );
void RageVec3Normalize( RageVector3* pOut, const RageVector3* pV ); void RageVec3Normalize( RageVector3* pOut, const RageVector3* pV );
void VectorFloatNormalize(vector<float>& v);
void RageVec3Cross(RageVector3* ret, RageVector3 const* a, RageVector3 const* b);
void RageVec3TransformCoord( RageVector3* pOut, const RageVector3* pV, const RageMatrix* pM ); void RageVec3TransformCoord( RageVector3* pOut, const RageVector3* pV, const RageMatrix* pM );
void RageVec3TransformNormal( RageVector3* pOut, const RageVector3* pV, const RageMatrix* pM ); void RageVec3TransformNormal( RageVector3* pOut, const RageVector3* pV, const RageMatrix* pM );
void RageVec4TransformCoord( RageVector4* pOut, const RageVector4* pV, const RageMatrix* pM ); void RageVec4TransformCoord( RageVector4* pOut, const RageVector4* pV, const RageMatrix* pM );
@@ -34,6 +36,7 @@ void RageMatrixRotationX( RageMatrix* pOut, float fTheta );
void RageMatrixRotationY( RageMatrix* pOut, float fTheta ); void RageMatrixRotationY( RageMatrix* pOut, float fTheta );
void RageMatrixRotationZ( RageMatrix* pOut, float fTheta ); void RageMatrixRotationZ( RageMatrix* pOut, float fTheta );
void RageMatrixRotationXYZ( RageMatrix* pOut, float rX, float rY, float rZ ); void RageMatrixRotationXYZ( RageMatrix* pOut, float rX, float rY, float rZ );
void RageAARotate(RageVector3* inret, RageVector3 const* axis, float angle);
void RageQuatFromHPR(RageVector4* pOut, RageVector3 hpr ); void RageQuatFromHPR(RageVector4* pOut, RageVector3 hpr );
void RageQuatFromPRH(RageVector4* pOut, RageVector3 prh ); void RageQuatFromPRH(RageVector4* pOut, RageVector3 prh );
void RageMatrixFromQuat( RageMatrix* pOut, const RageVector4 q ); void RageMatrixFromQuat( RageMatrix* pOut, const RageVector4 q );
+1 -14
View File
@@ -53,8 +53,6 @@ void ReceptorArrowRow::Update( float fDeltaTime )
for( unsigned c=0; c<m_ReceptorArrow.size(); c++ ) for( unsigned c=0; c<m_ReceptorArrow.size(); c++ )
{ {
vector<float> spline_pos;
(*m_renderers)[c].m_spline.evaluate((*m_renderers)[c].m_receptor_t, spline_pos);
// m_fDark==1 or m_fFadeToFailPercent==1 should make fBaseAlpha==0 // m_fDark==1 or m_fFadeToFailPercent==1 should make fBaseAlpha==0
float fBaseAlpha = (1 - m_pPlayerState->m_PlayerOptions.GetCurrent().m_fDark); float fBaseAlpha = (1 - m_pPlayerState->m_PlayerOptions.GetCurrent().m_fDark);
if( m_fFadeToFailPercent != -1 ) if( m_fFadeToFailPercent != -1 )
@@ -65,18 +63,7 @@ void ReceptorArrowRow::Update( float fDeltaTime )
m_ReceptorArrow[c]->SetBaseAlpha( fBaseAlpha ); m_ReceptorArrow[c]->SetBaseAlpha( fBaseAlpha );
// set arrow XYZ // set arrow XYZ
float fX = ArrowEffects::GetXPos( m_pPlayerState, c, 0 ); (*m_renderers)[c].UpdateReceptorGhostStuff(m_ReceptorArrow[c]);
const float fY = ArrowEffects::GetYPos( m_pPlayerState, c, 0, m_fYReverseOffsetPixels );
const float fZ = ArrowEffects::GetZPos( m_pPlayerState, c, 0 );
m_ReceptorArrow[c]->SetX( fX + spline_pos[0] );
m_ReceptorArrow[c]->SetY( fY + spline_pos[1] );
m_ReceptorArrow[c]->SetZ( fZ + spline_pos[2] );
const float fRotation = ArrowEffects::ReceptorGetRotationZ( m_pPlayerState );
m_ReceptorArrow[c]->SetRotationZ( fRotation );
const float fZoom = ArrowEffects::GetZoom( m_pPlayerState );
m_ReceptorArrow[c]->SetZoom( fZoom );
} }
} }