From 65187a55c6dfa608137dc5bd0b648952f581ae47 Mon Sep 17 00:00:00 2001 From: Kyzentun Date: Sat, 27 Dec 2014 21:41:55 -0700 Subject: [PATCH] CubicSpline class created. Temp spline rendering stuff in AMV. --- Docs/Changelog_sm5.txt | 5 + Docs/Luadoc/Lua.xml | 3 + Docs/Luadoc/LuaDocumentation.xml | 9 ++ src/Actor.cpp | 28 ++++ src/ActorMultiVertex.cpp | 126 +++++++++++++++ src/ActorMultiVertex.h | 13 ++ src/NoteDisplay.h | 4 +- src/RageMath.cpp | 260 +++++++++++++++++++++++++++++++ src/RageMath.h | 37 +++++ 9 files changed, 483 insertions(+), 2 deletions(-) diff --git a/Docs/Changelog_sm5.txt b/Docs/Changelog_sm5.txt index 49c0335c11..33512ebde7 100644 --- a/Docs/Changelog_sm5.txt +++ b/Docs/Changelog_sm5.txt @@ -4,6 +4,11 @@ The StepMania 5 Changelog covers all post-sm-ssc changes. For a list of changes from StepMania 4 alpha 5 to sm-ssc v1.2.5, see Changelog_sm-ssc.txt. ________________________________________________________________________________ +2014/12/26 +---------- +* [NoteField] Columns turned into actors that can be fetched with + GetColumnActors. [kyzentun] + 2014/12/20 ---------- * [Preferences] AllowMultipleHighScoreWithSameName, ComboContinuesBetweenSongs diff --git a/Docs/Luadoc/Lua.xml b/Docs/Luadoc/Lua.xml index 53d2d79fd4..590bfa43f0 100644 --- a/Docs/Luadoc/Lua.xml +++ b/Docs/Luadoc/Lua.xml @@ -272,6 +272,7 @@ + @@ -301,6 +302,7 @@ + @@ -968,6 +970,7 @@ + diff --git a/Docs/Luadoc/LuaDocumentation.xml b/Docs/Luadoc/LuaDocumentation.xml index fa8011dbe4..f7cb109daa 100644 --- a/Docs/Luadoc/LuaDocumentation.xml +++ b/Docs/Luadoc/LuaDocumentation.xml @@ -831,6 +831,12 @@ save yourself some time, copy this for undocumented things: Returns the Actor's parent, or nil if it doesn't have one. + + Returns the Actor's fake parent, or nil if it doesn't have one. + + + Sets the Actor's fake parent to p, or clears it if p is nil. + Returns the Actor's visibility. @@ -2953,6 +2959,9 @@ 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.
The callback for DidTapNote will not be called.
+ + Returns a table of the actors for the columns. + Same as SetDidTapNoteCallback, but for hold notes. Uses HoldNoteScore instead of TapNoteScore. diff --git a/src/Actor.cpp b/src/Actor.cpp index 318e06e8ab..d2ab5487a9 100644 --- a/src/Actor.cpp +++ b/src/Actor.cpp @@ -1739,6 +1739,32 @@ public: pParent->PushSelf(L); return 1; } + static int GetFakeParent(T* p, lua_State *L) + { + Actor* fake= p->GetFakeParent(); + if(fake == NULL) + { + lua_pushnil(L); + } + else + { + fake->PushSelf(L); + } + return 1; + } + static int SetFakeParent(T* p, lua_State* L) + { + if(lua_isnoneornil(L, 1)) + { + p->SetFakeParent(NULL); + } + else + { + Actor* fake= Luna::check(L, 1); + p->SetFakeParent(fake); + } + COMMON_RETURN_SELF; + } static int Draw( T* p, lua_State *L ) { LUA->YieldLua(); @@ -1910,6 +1936,8 @@ public: ADD_METHOD( GetName ); ADD_METHOD( GetParent ); + ADD_METHOD( GetFakeParent ); + ADD_METHOD( SetFakeParent ); ADD_METHOD( Draw ); } diff --git a/src/ActorMultiVertex.cpp b/src/ActorMultiVertex.cpp index c569eb3d7a..7d1b48c0ae 100644 --- a/src/ActorMultiVertex.cpp +++ b/src/ActorMultiVertex.cpp @@ -7,6 +7,7 @@ #include "RageLog.h" #include "RageDisplay.h" #include "RageTexture.h" +#include "RageTimer.h" #include "RageUtil.h" #include "ActorUtil.h" #include "Foreach.h" @@ -62,6 +63,9 @@ ActorMultiVertex::ActorMultiVertex() _EffectMode = EffectMode_Normal; _TextureMode = TextureMode_Modulate; + _using_spline= false; + _spline.redimension(3); + _spline.loop= false; } ActorMultiVertex::~ActorMultiVertex() @@ -73,6 +77,8 @@ ActorMultiVertex::ActorMultiVertex( const ActorMultiVertex &cpy ): Actor( cpy ) { #define CPY(a) a = cpy.a + CPY( _spline ); + CPY( _using_spline ); CPY( AMV_Tweens ); CPY( AMV_current ); CPY( AMV_start ); @@ -194,6 +200,75 @@ void ActorMultiVertex::SetVertexCoords( int index, float TexCoordX, float TexCoo AMV_DestTweenState().vertices[index].t = RageVector2( TexCoordX, TexCoordY ); } +bool ActorMultiVertex::GetUseSpline() +{ + return _using_spline; +} + +void ActorMultiVertex::SetUseSpline(bool use) +{ + _using_spline= use; +} + +void ActorMultiVertex::SplineSetPoint(size_t i, float x , float y , float z) +{ + vector v(3); + v[0]= x; v[1]= y; v[2]= z; + _spline.set_point(i, v); +} + +void ActorMultiVertex::SplineResize(size_t s) +{ + _spline.resize(s); +} + +size_t ActorMultiVertex::SplineSize() +{ + return _spline.size(); +} + +void ActorMultiVertex::SplineSolve() +{ + if(_spline.empty()) + { + return; + } + _spline.solve(); + float num_parts= 0.0f; + size_t num_verts= GetNumVertices(); + switch(GetDestDrawMode()) + { + case DrawMode_Quads: + break; + case DrawMode_QuadStrip: + break; + case DrawMode_Fan: + break; + case DrawMode_Strip: + break; + case DrawMode_Triangles: + break; + case DrawMode_LineStrip: + { + float conversion= static_cast(_spline.size()-1) / static_cast(num_verts-1); + if(_spline.loop) + { + conversion= static_cast(_spline.size()) / static_cast(num_verts-1); + } + for(size_t i= 0; i < num_verts; ++i) + { + float t= i * conversion; + vector p; + _spline.evaluate(t, p); + SetVertexPos(i, p[0], p[1], p[2]); + } + } + break; + case DrawMode_SymmetricQuadStrip: + break; + } +} + void ActorMultiVertex::DrawPrimitives() { Actor::SetGlobalRenderStates(); // set Actor-specified render states @@ -658,6 +733,48 @@ public: } return 1; } + DEFINE_METHOD(GetUseSpline, GetUseSpline()); + DEFINE_METHOD(SplineGetLoop, SplineGetLoop()); + static int SplineSetLoop(T* p, lua_State* L) + { + p->SplineSetLoop(lua_toboolean(L, 1)); + COMMON_RETURN_SELF; + } + static int SplineSize(T* p, lua_State* L) + { + lua_pushnumber(L, p->SplineSize()); + return 1; + } + static int SetUseSpline(T* p, lua_State* L) + { + p->SetUseSpline(lua_toboolean(L, 1)); + COMMON_RETURN_SELF; + } + static int SplineSetPoint(T* p, lua_State* L) + { + size_t i= IArg(1)-1; + if(i >= p->SplineSize()) + { + luaL_error(L, "Spline point index greater than the number of points."); + } + p->SplineSetPoint(i, FArg(2), FArg(3), FArg(4)); + COMMON_RETURN_SELF; + } + static int SplineResize(T* p, lua_State* L) + { + int s= IArg(1); + if(s < 0) + { + luaL_error(L, "Negative spline size not allowed."); + } + p->SplineResize(static_cast(s)); + COMMON_RETURN_SELF; + } + static int SplineSolve(T* p, lua_State* L) + { + p->SplineSolve(); + COMMON_RETURN_SELF; + } LunaActorMultiVertex() { @@ -679,6 +796,15 @@ public: ADD_METHOD( GetCurrFirstToDraw ); ADD_METHOD( GetCurrNumToDraw ); + ADD_METHOD(GetUseSpline); + ADD_METHOD(SetUseSpline); + ADD_METHOD(SplineGetLoop); + ADD_METHOD(SplineSetLoop); + ADD_METHOD(SplineSize); + ADD_METHOD(SplineResize); + ADD_METHOD(SplineSetPoint); + ADD_METHOD(SplineSolve); + // Copy from RageTexture ADD_METHOD( SetTexture ); ADD_METHOD( GetTexture ); diff --git a/src/ActorMultiVertex.h b/src/ActorMultiVertex.h index 176d2a7cad..ce340215ef 100644 --- a/src/ActorMultiVertex.h +++ b/src/ActorMultiVertex.h @@ -2,6 +2,7 @@ #include "Actor.h" #include "RageDisplay.h" +#include "RageMath.h" #include "RageTextureID.h" enum DrawMode @@ -102,11 +103,23 @@ public: void SetVertexColor( int index , RageColor c ); void SetVertexCoords( int index , float TexCoordX , float TexCoordY ); + bool GetUseSpline(); + void SetUseSpline(bool use); + bool SplineGetLoop() { return _spline.loop; } + void SplineSetLoop(bool loop) { _spline.loop= loop; } + void SplineSetPoint(size_t i, float x , float y , float z); + void SplineResize(size_t s); + size_t SplineSize(); + void SplineSolve(); + virtual void PushSelf( lua_State *L ); private: RageTexture* _Texture; + CubicSplineN _spline; + bool _using_spline; + vector _Vertices; vector AMV_Tweens; AMV_TweenState AMV_current; diff --git a/src/NoteDisplay.h b/src/NoteDisplay.h index 3487be8d6c..ade622f321 100644 --- a/src/NoteDisplay.h +++ b/src/NoteDisplay.h @@ -179,7 +179,7 @@ private: float m_fYReverseOffsetPixels; }; -// So, this is a bit screwy, and it's all because routine forces rendering +// So, this is a bit screwy, and it's partly because routine forces rendering // notes from different noteskins in the same column. // NoteColumnRenderer exists to hold all the data needed for rendering a // column and apply any transforms from that column's actor to the @@ -188,7 +188,7 @@ private: // 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 // state of their rows and the parent state of the column. -Kyz -class NoteColumnRenderer : public ActorFrame +class NoteColumnRenderer : public Actor { public: NoteDisplay* m_displays[PLAYER_INVALID+1]; diff --git a/src/RageMath.cpp b/src/RageMath.cpp index dd209974cc..8baf3b5403 100644 --- a/src/RageMath.cpp +++ b/src/RageMath.cpp @@ -5,6 +5,7 @@ */ #include "global.h" +#include "RageLog.h" #include "RageMath.h" #include "RageTypes.h" #include @@ -695,6 +696,265 @@ void RageBezier2D::SetFromBezier( m_Y.SetFromBezier( fC1Y, fC2Y, fC3Y, fC4Y ); } +// CubicSpline implementation written by Kyzentun 2014/12/27 + +void CubicSpline::solve_looped() +{ + if(check_minimum_size()) { return; } + size_t last= m_points.size(); + vector results(m_points.size()); + vector diagonals(m_points.size()); + results[0]= 3 * (m_points[1].a - m_points[last-1].a); + diagonals[0]= 4.0f; + prep_inner(last, diagonals, results); + results[last-1]= 3 * (m_points[0].a - m_points[last-2].a); + diagonals[last-1]= 4.0f; + + // The steps to solve the system of equations look like this: + // | 4 1 0 0 1 | -> | 4 0 0 0 1 | -> | 4 0 0 0 0 | -> | 4 0 0 0 0 | + // | 1 4 1 0 0 | -> | 0 d 1 0 x | -> | 0 d 1 0 x | -> | 0 d 0 0 x | + // | 0 1 4 1 0 | -> | 0 1 4 1 0 | -> | 0 1 4 1 0 | -> | 0 0 d 1 x | + // | 0 0 1 4 1 | -> | 0 0 1 4 1 | -> | 0 0 1 4 1 | -> | 0 0 1 4 1 | + // | 1 0 0 1 4 | -> | 1 x 0 1 4 | -> | 0 x 0 1 q | -> | 0 x x 1 q | + // V + // | 4 0 0 0 0 | -> | 4 0 0 0 0 | -> | 4 0 0 0 0 | -> | 4 0 0 0 0 | + // | 0 d 0 0 0 | -> | 0 d 0 0 0 | -> | 0 d 0 0 0 | -> | 0 d 0 0 0 | + // | 0 0 d 1 x | -> | 0 0 d 0 x | -> | 0 0 d 0 0 | -> | 0 0 d 0 0 | + // | 0 0 1 d 1 | -> | 0 0 0 d n | -> | 0 0 0 d n | -> | 0 0 0 d 0 | + // | 0 0 x 1 r | -> | 0 0 x n r | -> | 0 0 0 n s | -> | 0 0 0 0 t | + // Each time through the loop performs two of these steps, 4 operations. + + size_t end= last-1; + size_t stop= end-1; + float cedge= 1.0f; // [ri][cl] + float redge= 1.0f; // [rl][ci] + // The loop stops before end because the case where [ri][cl] == [ri][ci+1] + // needs special handling. + for(size_t i= 0; i < stop; ++i) + { + float next_cedge= 0.0f; // [ri+1][ce] + float next_redge= 0.0f; // [re][ci+1] + // Operation: Add col[i] / -[ri][ci] to col[i+1] to zero [ri][ci+1]. + float diag_recip= 1.0f / diagonals[i]; + diagonals[i+1]-= diag_recip; + next_redge-= redge * diag_recip; + // Operation: Add row[i] / -[ri][ci] to row[i+1] to zero [ri+1][ci]. + results[i+1]-= results[i] * diag_recip; + next_cedge-= cedge * diag_recip; + // Operation: Add col[i] * -(cedge/[ri][ci]) to col[e] to zero cedge. + diagonals[end]-= redge * (cedge / diagonals[i]); + cedge= next_cedge; // Do not use cedge after this point in the loop. + // Operation: Add row[i] * -(redge/[ri][ci]) to row[e] to zero redge. + results[end]-= results[i] * (redge / diagonals[i]); + redge= next_redge; // Do not use redge after this point in the loop. + } + // [rs][ce] is 1 - cedge, [re][cs] is 1 - redge + // Operation: Add col[s] * -([rs][ce] / [rs][cs]) to col[e] to zero redge. + diagonals[end]-= redge * ((1.0f - cedge) / diagonals[stop]); + // Operation: Add row[s] * -([re][cs] / [rs][cs]) to row[e] to zero redge. + results[end]-= results[stop] * ((1.0f - redge) / diagonals[stop]); + + set_results(last, diagonals, results); +} + +void CubicSpline::solve_straight() +{ + if(check_minimum_size()) { return; } + size_t last= m_points.size(); + vector results(m_points.size()); + vector diagonals(m_points.size()); + results[0]= 3 * (m_points[1].a - m_points[0].a); + diagonals[0]= 2.0f; + prep_inner(last, diagonals, results); + results[last-1]= 3 * (m_points[last-1].a - m_points[last-2].a); + diagonals[last-1]= 2.0f; + + // The system of equations to be solved looks like this: + // | 2 1 0 0 | = | results[0] | + // | 1 4 1 0 | = | results[1] | + // | 0 1 4 1 | = | results[2] | + // | 0 0 1 2 | = | results[3] | + // Operations are carefully chosen to only modify the values in the + // diagonals and the results, leaving the 1s unchanged. + // Operation: Add col[0] * -.5 to col[1] to zero [r0][c1]. + diagonals[1]-= .5f; + // Operation: Add row[0] * -.5 to row[1] to zero [r1][c0]. + results[1]-= results[0] * .5f; + for(size_t i= 1; i < last - 1; ++i) + { + float diag_recip= 1.0f / diagonals[i]; + // Operation: Add col[i] / -[ri][ci] to col[i+1] to zero [ri][ci+1]. + diagonals[i+1]-= diag_recip; + // Operation: Add row[i] / -[ri][ci] to row[i+1] to zero [ri+1][ci]; + results[i]-= results[i-1] * diag_recip; + } + set_results(last, diagonals, results); +} + +bool CubicSpline::check_minimum_size() +{ + size_t last= m_points.size(); + if(last < 2) { return true; } + if(last == 2) + { + m_points[0].b= m_points[1].a - m_points[0].a; + m_points[0].c= m_points[0].d= 0.0f; + // These will be used in the looping case. + m_points[1].b= m_points[0].a - m_points[1].a; + m_points[1].c= m_points[1].d= 0.0f; + return true; + } + float a= m_points[0].a; + for(size_t i= 1; i < m_points.size(); ++i) + { + m_points[i].b= m_points[i].c= m_points[i].d= 0.0f; + if(m_points[i].a != a) { return false; } + } + return true; +} + +void CubicSpline::prep_inner(size_t last, vector& diagonals, vector& results) +{ + for(size_t i= 1; i < last - 1; ++i) + { + results[i]= 3 * (m_points[i+1].a - m_points[i-1].a); + diagonals[i]= 4.0f; + } +} + +void CubicSpline::set_results(size_t last, vector& diagonals, vector& results) +{ + // No more operations left, everything not a diagonal should be zero now. + for(size_t i= 0; i < last; ++i) + { + results[i]/= diagonals[i]; + } + // Now we can go through and set the b, c, d values of each point. + // b, c, d values of the last point are not set because they are unused. + for(size_t i= 0; i < last; ++i) + { + size_t next= (i+1) % last; + float diff= m_points[next].a - m_points[i].a; + m_points[i].b= results[i]; + m_points[i].c= (3 * diff) - (2 * results[i]) - results[next]; + m_points[i].d= (2 * -diff) + results[i] + results[next]; + } + // Solving is now complete. +} + +float CubicSpline::evaluate(float t, bool loop) +{ + if(loop) + { + t= fmodf(t, m_points.size()); + } + int flort= static_cast(t); + if(flort < 0) + { + return m_points[0].a; + } + size_t p= min(static_cast(flort), m_points.size()-1); + float tfrac= t - static_cast(flort); + float tsq= tfrac * tfrac; + float tcub= tsq * tfrac; + return m_points[p].a + (m_points[p].b * tfrac) + + (m_points[p].c * tsq) + (m_points[p].d * tcub); +} + +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."); + m_points[i].a= v; +} + +void CubicSpline::resize(size_t s) +{ + m_points.resize(s); +} + +size_t CubicSpline::size() +{ + return m_points.size(); +} + +bool CubicSpline::empty() +{ + return m_points.empty(); +} + +void CubicSplineN::solve() +{ + if(loop) + { + for(spline_cont_t::iterator spline= m_splines.begin(); + spline != m_splines.end(); ++spline) + { + spline->solve_looped(); + } + } + else + { + for(spline_cont_t::iterator spline= m_splines.begin(); + spline != m_splines.end(); ++spline) + { + spline->solve_straight(); + } + } +} + +void CubicSplineN::evaluate(float t, vector& v) +{ + for(spline_cont_t::iterator spline= m_splines.begin(); + spline != m_splines.end(); ++spline) + { + v.push_back(spline->evaluate(t, loop)); + } +} + +void CubicSplineN::set_point(size_t i, vector const& v) +{ + ASSERT_M(v.size() == m_splines.size(), "CubicSplineN::set_point requires the passed point to be the same dimension as the spline."); + for(size_t n= 0; n < m_splines.size(); ++n) + { + m_splines[n].set_point(i, v[n]); + } +} + +void CubicSplineN::resize(size_t s) +{ + for(spline_cont_t::iterator spline= m_splines.begin(); + spline != m_splines.end(); ++spline) + { + spline->resize(s); + } +} + +size_t CubicSplineN::size() +{ + if(!m_splines.empty()) + { + return m_splines[0].size(); + } + return 0; +} + +bool CubicSplineN::empty() +{ + return m_splines.empty() || m_splines[0].empty(); +} + +void CubicSplineN::redimension(size_t d) +{ + m_splines.resize(d); +} + +size_t CubicSplineN::dimension() +{ + return m_splines.size(); +} + + + /* * Copyright (c) 2001-2006 Chris Danford, Glenn Maynard * All rights reserved. diff --git a/src/RageMath.h b/src/RageMath.h index 4e56aac737..4565fc80fb 100644 --- a/src/RageMath.h +++ b/src/RageMath.h @@ -85,6 +85,43 @@ private: RageQuadratic m_Y; }; +struct CubicSpline +{ + void solve_looped(); + void solve_straight(); + float evaluate(float t, bool loop); + void set_point(size_t i, float v); + void resize(size_t s); + size_t size(); + bool empty(); +private: + bool check_minimum_size(); + void prep_inner(size_t last, vector& diagonals, vector& results); + void set_results(size_t last, vector& diagonals, vector& results); + + struct SplinePoint + { + float a, b, c, d; + }; + vector m_points; +}; + +struct CubicSplineN +{ + void solve(); + void evaluate(float t, vector& v); + void set_point(size_t i, vector const& v); + void resize(size_t s); + size_t size(); + void redimension(size_t d); + size_t dimension(); + bool empty(); + typedef vector spline_cont_t; + bool loop; +private: + spline_cont_t m_splines; +}; + #endif /*