Maybe editing the changelog should only be done after merging a pull, to avoid conflicts.

This commit is contained in:
Kyzentun
2015-01-10 12:32:07 -07:00
25 changed files with 2879 additions and 416 deletions
+64 -2
View File
@@ -164,6 +164,7 @@ Actor::Actor()
m_size = RageVector2( 1, 1 );
InitState();
m_pParent = NULL;
m_FakeParent= NULL;
m_bFirstUpdate = true;
}
@@ -183,6 +184,7 @@ Actor::Actor( const Actor &cpy ):
#define CPY(x) x = cpy.x
CPY( m_sName );
CPY( m_pParent );
CPY( m_FakeParent );
CPY( m_pLuaInstance );
CPY( m_baseRotation );
@@ -279,24 +281,56 @@ void Actor::LoadFromNode( const XNode* pNode )
PlayCommandNoRecurse( Message("Init") );
}
bool Actor::PartiallyOpaque()
{
return m_pTempState->diffuse[0].a > 0 || m_pTempState->diffuse[1].a > 0 ||
m_pTempState->diffuse[2].a > 0 || m_pTempState->diffuse[3].a > 0 ||
m_pTempState->glow.a > 0;
}
void Actor::Draw()
{
if( !m_bVisible ||
m_fHibernateSecondsLeft > 0 ||
this->EarlyAbortDraw() )
{
return; // early abort
}
bool fake_parent_partially_opaque= true;
if(m_FakeParent)
{
if(!m_FakeParent->m_bVisible || m_FakeParent->m_fHibernateSecondsLeft > 0
|| m_FakeParent->EarlyAbortDraw())
{
return;
}
m_FakeParent->PreDraw();
fake_parent_partially_opaque= m_FakeParent->PartiallyOpaque();
}
this->PreDraw();
ASSERT( m_pTempState != NULL );
if( m_pTempState->diffuse[0].a > 0 || m_pTempState->diffuse[1].a > 0 || m_pTempState->diffuse[2].a > 0 || m_pTempState->diffuse[3].a > 0 || m_pTempState->glow.a > 0 ) // This Actor is not fully transparent
{
if(PartiallyOpaque() && fake_parent_partially_opaque)
{
if(m_FakeParent)
{
m_FakeParent->BeginDraw();
}
// call the most-derived versions
this->BeginDraw();
this->DrawPrimitives(); // call the most-derived version of DrawPrimitives();
this->EndDraw();
if(m_FakeParent)
{
m_FakeParent->EndDraw();
}
}
this->PostDraw();
if(m_FakeParent)
{
m_FakeParent->PostDraw();
}
m_pTempState = NULL;
}
@@ -1705,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<Actor>::check(L, 1);
p->SetFakeParent(fake);
}
COMMON_RETURN_SELF;
}
static int Draw( T* p, lua_State *L )
{
LUA->YieldLua();
@@ -1876,6 +1936,8 @@ public:
ADD_METHOD( GetName );
ADD_METHOD( GetParent );
ADD_METHOD( GetFakeParent );
ADD_METHOD( SetFakeParent );
ADD_METHOD( Draw );
}
+9
View File
@@ -232,6 +232,8 @@ public:
float aux;
};
// PartiallyOpaque broken out of Draw for reuse and clarity.
bool PartiallyOpaque();
/**
* @brief Calls multiple functions for drawing the Actors.
*
@@ -305,6 +307,9 @@ public:
* @return the Actor's lineage. */
RString GetLineage() const;
void SetFakeParent(Actor* mailman) { m_FakeParent= mailman; }
Actor* GetFakeParent() { return m_FakeParent; }
/**
* @brief Retrieve the Actor's x position.
* @return the Actor's x position. */
@@ -607,6 +612,10 @@ protected:
RString m_sName;
/** @brief the current parent of this Actor if it exists. */
Actor *m_pParent;
// m_FakeParent exists to provide a way to render the actor inside another's
// state without making that actor the parent. It's like having multiple
// parents. -Kyz
Actor* m_FakeParent;
/** @brief Some general information about the Tween. */
struct TweenInfo
+83
View File
@@ -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,12 @@ ActorMultiVertex::ActorMultiVertex()
_EffectMode = EffectMode_Normal;
_TextureMode = TextureMode_Modulate;
_splines.resize(num_vert_splines);
for(size_t i= 0; i < num_vert_splines; ++i)
{
_splines[i].redimension(3);
_splines[i].m_owned_by_actor= true;
}
}
ActorMultiVertex::~ActorMultiVertex()
@@ -78,6 +85,7 @@ ActorMultiVertex::ActorMultiVertex( const ActorMultiVertex &cpy ):
CPY( AMV_start );
CPY( _EffectMode );
CPY( _TextureMode );
CPY( _splines );
#undef CPY
if( cpy._Texture != NULL )
@@ -309,6 +317,61 @@ bool ActorMultiVertex::EarlyAbortDraw() const
return false;
}
void ActorMultiVertex::SetVertsFromSplinesInternal(size_t num_splines, size_t offset)
{
vector<RageSpriteVertex>& verts= AMV_DestTweenState().vertices;
size_t first= AMV_DestTweenState().FirstToDraw + offset;
size_t num_verts= AMV_DestTweenState().GetSafeNumToDraw(AMV_DestTweenState()._DrawMode, AMV_DestTweenState().NumToDraw) - offset;
vector<float> tper(num_splines, 0.0f);
float num_parts= static_cast<float>(num_verts) /
static_cast<float>(num_splines);
for(size_t i= 0; i < num_splines; ++i)
{
tper[i]= _splines[i].get_max_t() / num_parts;
}
for(size_t v= 0; v < num_verts; ++v)
{
vector<float> pos;
const int spi= v%num_splines;
_splines[spi].evaluate(static_cast<float>(v) * tper[spi], pos);
verts[v+first].p.x= pos[0];
verts[v+first].p.y= pos[1];
verts[v+first].p.z= pos[2];
}
}
void ActorMultiVertex::SetVertsFromSplines()
{
if(AMV_DestTweenState().vertices.empty()) { return; }
switch(AMV_DestTweenState()._DrawMode)
{
case DrawMode_Quads:
SetVertsFromSplinesInternal(4, 0);
break;
case DrawMode_QuadStrip:
case DrawMode_Strip:
SetVertsFromSplinesInternal(2, 0);
break;
case DrawMode_Fan:
// Skip the first vert because it is the center of the fan. -Kyz
SetVertsFromSplinesInternal(1, 1);
break;
case DrawMode_Triangles:
case DrawMode_SymmetricQuadStrip:
SetVertsFromSplinesInternal(3, 0);
break;
case DrawMode_LineStrip:
SetVertsFromSplinesInternal(1, 0);
break;
}
}
CubicSplineN* ActorMultiVertex::GetSpline(size_t i)
{
ASSERT(i < num_vert_splines);
return &(_splines[i]);
}
void ActorMultiVertex::SetCurrentTweenStart()
{
AMV_start= AMV_current;
@@ -638,6 +701,23 @@ public:
COMMON_RETURN_SELF;
}
static int GetSpline(T* p, lua_State* L)
{
size_t i= static_cast<size_t>(IArg(1)-1);
if(i >= ActorMultiVertex::num_vert_splines)
{
luaL_error(L, "Spline index must be greater than 0 and less than or equal to %zu.", ActorMultiVertex::num_vert_splines);
}
p->GetSpline(i)->PushSelf(L);
return 1;
}
static int SetVertsFromSplines(T* p, lua_State* L)
{
p->SetVertsFromSplines();
COMMON_RETURN_SELF;
}
static int SetTexture( T* p, lua_State *L )
{
RageTexture *Texture = Luna<RageTexture>::check(L, 1);
@@ -679,6 +759,9 @@ public:
ADD_METHOD( GetCurrFirstToDraw );
ADD_METHOD( GetCurrNumToDraw );
ADD_METHOD( GetSpline );
ADD_METHOD( SetVertsFromSplines );
// Copy from RageTexture
ADD_METHOD( SetTexture );
ADD_METHOD( GetTexture );
+14 -1
View File
@@ -1,7 +1,9 @@
/** @brief ActorMultiVertex - A texture created from multiple textures. */
#include "Actor.h"
#include "CubicSpline.h"
#include "RageDisplay.h"
#include "RageMath.h"
#include "RageTextureID.h"
enum DrawMode
@@ -26,6 +28,7 @@ class RageTexture;
class ActorMultiVertex: public Actor
{
public:
static const size_t num_vert_splines= 4;
ActorMultiVertex();
ActorMultiVertex( const ActorMultiVertex &cpy );
virtual ~ActorMultiVertex();
@@ -35,7 +38,9 @@ public:
struct AMV_TweenState
{
AMV_TweenState(): _DrawMode(DrawMode_Invalid), FirstToDraw(0), NumToDraw(-1), line_width(1.0f) {}
AMV_TweenState(): _DrawMode(DrawMode_Invalid), FirstToDraw(0),
NumToDraw(-1), line_width(1.0f)
{}
static void MakeWeightedAverage(AMV_TweenState& average_out, const AMV_TweenState& ts1, const AMV_TweenState& ts2, float percent_between);
bool operator==(const AMV_TweenState& other) const;
bool operator!=(const AMV_TweenState& other) const { return !operator==(other); }
@@ -102,6 +107,10 @@ public:
void SetVertexColor( int index , RageColor c );
void SetVertexCoords( int index , float TexCoordX , float TexCoordY );
inline void SetVertsFromSplinesInternal(size_t num_splines, size_t start_vert);
void SetVertsFromSplines();
CubicSplineN* GetSpline(size_t i);
virtual void PushSelf( lua_State *L );
private:
@@ -117,6 +126,10 @@ private:
EffectMode _EffectMode;
TextureMode _TextureMode;
// Four splines for controlling vert positions, because quads drawmode
// requires four. -Kyz
vector<CubicSplineN> _splines;
};
/**
+8
View File
@@ -19,6 +19,14 @@ public:
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.
*
+1037
View File
File diff suppressed because it is too large Load Diff
+111
View File
@@ -0,0 +1,111 @@
#ifndef CUBIC_SPLINE_H
#define CUBIC_SPLINE_H
#include <vector>
using std::vector;
struct lua_State;
struct CubicSpline
{
CubicSpline() :m_spatial_extent(0.0f) {}
void solve_looped();
void solve_straight();
void solve_polygonal();
void p_and_tfrac_from_t(float t, bool loop, size_t& p, float& tfrac) const;
float evaluate(float t, bool loop) const;
float evaluate_derivative(float t, bool loop) const;
float evaluate_second_derivative(float t, bool loop) const;
float evaluate_third_derivative(float t, bool loop) const;
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) const;
void set_point_and_coefficients(size_t i, float a, float b, float c, float d);
void get_point_and_coefficients(size_t i, float& a, float& b, float& c, float& d) const;
void resize(size_t s);
size_t size() const;
bool empty() const;
float m_spatial_extent;
private:
bool check_minimum_size();
void prep_inner(size_t last, vector<float>& results);
void set_results(size_t last, vector<float>& diagonals, vector<float>& results);
struct SplinePoint
{
float a, b, c, d;
};
vector<SplinePoint> m_points;
};
struct CubicSplineN
{
CubicSplineN()
:m_owned_by_actor(false), m_loop(false), m_polygonal(false), m_dirty(true)
{}
static void weighted_average(CubicSplineN& out, const CubicSplineN& from,
const CubicSplineN& to, float between);
void solve();
void evaluate(float t, vector<float>& v) const;
void evaluate_derivative(float t, vector<float>& v) const;
void evaluate_second_derivative(float t, vector<float>& v) const;
void evaluate_third_derivative(float t, vector<float>& v) const;
void set_point(size_t i, const vector<float>& v);
void set_coefficients(size_t i, const vector<float>& b,
const vector<float>& c, const vector<float>& d);
void get_coefficients(size_t i, vector<float>& b,
vector<float>& c, vector<float>& d);
void set_spatial_extent(size_t i, float extent);
float get_spatial_extent(size_t i);
void resize(size_t s);
size_t size() const;
void redimension(size_t d);
size_t dimension() const;
bool empty() const;
float get_max_t() const {
if(m_loop) { return static_cast<float>(size()); }
else { return static_cast<float>(size()-1); }
}
typedef vector<CubicSpline> spline_cont_t;
void set_loop(bool l);
bool get_loop() const;
void set_polygonal(bool p);
bool get_polygonal() const;
void set_dirty(bool d);
bool get_dirty() const;
bool m_owned_by_actor;
void PushSelf(lua_State* L);
private:
bool m_loop;
bool m_polygonal;
bool m_dirty;
spline_cont_t m_splines;
};
#endif
// Side note: Actually written between 2014/12/26 and 2014/12/28
/*
* Copyright (c) 2014-2015 Eric Reese
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+11 -14
View File
@@ -35,6 +35,16 @@ void GhostArrowRow::Load( const PlayerState* pPlayerState, float fYReverseOffset
}
}
void GhostArrowRow::SetColumnRenderers(vector<NoteColumnRenderer>& renderers)
{
ASSERT_M(renderers.size() == m_Ghost.size(), "Notefield has different number of columns than ghost row.");
for(size_t c= 0; c < m_Ghost.size(); ++c)
{
m_Ghost[c]->SetFakeParent(&(renderers[c]));
}
m_renderers= &renderers;
}
GhostArrowRow::~GhostArrowRow()
{
for( unsigned i = 0; i < m_Ghost.size(); ++i )
@@ -47,20 +57,7 @@ void GhostArrowRow::Update( float fDeltaTime )
for( unsigned c=0; c<m_Ghost.size(); c++ )
{
m_Ghost[c]->Update( fDeltaTime );
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 );
m_Ghost[c]->SetY( fY );
m_Ghost[c]->SetZ( fZ );
const float fRotation = ArrowEffects::ReceptorGetRotationZ( m_pPlayerState );
m_Ghost[c]->SetRotationZ( fRotation );
const float fZoom = ArrowEffects::GetZoom( m_pPlayerState );
m_Ghost[c]->SetZoom( fZoom );
(*m_renderers)[c].UpdateReceptorGhostStuff(m_Ghost[c]);
}
for( unsigned i = 0; i < m_bHoldShowing.size(); ++i )
+3
View File
@@ -4,6 +4,7 @@
#include "ActorFrame.h"
#include "GameConstantsAndTypes.h"
#include "NoteTypes.h"
#include "NoteDisplay.h"
class PlayerState;
/** @brief Row of GhostArrow Actors. */
@@ -15,6 +16,7 @@ public:
virtual void DrawPrimitives();
void Load( const PlayerState* pPlayerState, float fYReverseOffset );
void SetColumnRenderers(vector<NoteColumnRenderer>& renderers);
void DidTapNote( int iCol, TapNoteScore tns, bool bBright );
void DidHoldNote( int iCol, HoldNoteScore hns, bool bBright );
@@ -24,6 +26,7 @@ protected:
float m_fYReverseOffsetPixels;
const PlayerState* m_pPlayerState;
vector<NoteColumnRenderer> const* m_renderers;
vector<Actor *> m_Ghost;
vector<TapNoteSubType> m_bHoldShowing;
vector<TapNoteSubType> m_bLastHoldShowing;
+1
View File
@@ -516,6 +516,7 @@ RageFileDriverSlice.cpp RageFileDriverSlice.h \
RageFileDriverTimeout.cpp RageFileDriverTimeout.h
Rage = $(PCRE) $(Lua) $(jsoncpp) $(RageFile) $(RageSoundFileReaders) \
CubicSpline.cpp CubicSpline.h \
RageBitmapTexture.cpp RageBitmapTexture.h \
RageDisplay.cpp RageDisplay.h \
RageDisplay_OGL.cpp RageDisplay_OGL.h \
+838 -96
View File
File diff suppressed because it is too large Load Diff
+184 -21
View File
@@ -1,13 +1,17 @@
#ifndef NOTE_DISPLAY_H
#define NOTE_DISPLAY_H
#include "ActorFrame.h"
#include "CubicSpline.h"
#include "NoteData.h"
#include "PlayerNumber.h"
#include "GameInput.h"
class Actor;
class Sprite;
class Model;
class PlayerState;
class GhostArrowRow;
class ReceptorArrowRow;
struct TapNote;
struct HoldNoteResult;
struct NoteMetricCache_t;
@@ -83,6 +87,88 @@ enum ActiveType
#define FOREACH_ActiveType( i ) FOREACH_ENUM( ActiveType, i )
const RString &ActiveTypeToString( ActiveType at );
enum NoteColumnSplineMode
{
NCSM_Disabled,
NCSM_Offset,
NCSM_Position,
NUM_NoteColumnSplineMode,
NoteColumnSplineMode_Invalid
};
const RString& NoteColumnSplineModeToString(NoteColumnSplineMode ncsm);
LuaDeclareType(NoteColumnSplineMode);
// A little pod struct to carry the data the NoteField needs to pass to the
// NoteDisplay during rendering.
struct NoteFieldRenderArgs
{
const PlayerState* player_state; // to look up PlayerOptions
float reverse_offset_pixels;
ReceptorArrowRow* receptor_row;
GhostArrowRow* ghost_row;
const NoteData* note_data;
float first_beat;
float last_beat;
int first_row;
int last_row;
float draw_pixels_before_targets;
float draw_pixels_after_targets;
int* selection_begin_marker;
int* selection_end_marker;
float selection_glow;
float fail_fade;
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;
static void MakeWeightedAverage(NCSplineHandler& out,
const NCSplineHandler& from, const NCSplineHandler& to, float between);
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(const PlayerState* state,
float beat, float y_offset, float y_reverse_offset,
vector<float>& sp_pos, vector<float>& ae_pos) const;
void spae_zoom_for_beat(const PlayerState* state, float beat,
vector<float>& sp_zoom, vector<float>& ae_zoom) const;
void SetPRZForActor(Actor* actor,
const vector<float>& sp_pos, const vector<float>& ae_pos,
const vector<float>& sp_rot, const vector<float>& ae_rot,
const vector<float>& sp_zoom, const vector<float>& ae_zoom) const;
const NCSplineHandler* pos_handler;
const NCSplineHandler* rot_handler;
const NCSplineHandler* zoom_handler;
float song_beat;
int column;
};
/** @brief Draws TapNotes and HoldNotes. */
class NoteDisplay
{
@@ -94,6 +180,14 @@ public:
static void Update( float fDeltaTime );
bool IsOnScreen( float fBeat, int iCol, int iDrawDistanceAfterTargetsPixels, int iDrawDistanceBeforeTargetsPixels ) const;
bool DrawHoldsInRange(const NoteFieldRenderArgs& field_args,
const NoteColumnRenderArgs& column_args,
const vector<NoteData::TrackMap::const_iterator>& tap_set);
bool DrawTapsInRange(const NoteFieldRenderArgs& field_args,
const NoteColumnRenderArgs& column_args,
const vector<NoteData::TrackMap::const_iterator>& tap_set);
/**
* @brief Draw the TapNote onto the NoteField.
* @param tn the TapNote in question.
@@ -107,19 +201,16 @@ public:
* @param fDrawDistanceAfterTargetsPixels how much to draw after the receptors.
* @param fDrawDistanceBeforeTargetsPixels how much ot draw before the receptors.
* @param fFadeInPercentOfDrawFar when to start fading in. */
void DrawTap(const TapNote& tn, int iCol, float fBeat,
bool bOnSameRowAsHoldStart, bool bOnSameRowAsRollBeat,
bool bIsAddition, float fPercentFadeToFail,
float fReverseOffsetPixels,
float fDrawDistanceAfterTargetsPixels,
float fDrawDistanceBeforeTargetsPixels,
float fFadeInPercentOfDrawFar );
void DrawHold( const TapNote& tn, int iCol, int iRow, bool bIsBeingHeld, const HoldNoteResult &Result,
bool bIsAddition, float fPercentFadeToFail, float fReverseOffsetPixels, float fDrawDistanceAfterTargetsPixels, float fDrawDistanceBeforeTargetsPixels,
float fDrawDistanceBeforeTargetsPixels2, float fFadeInPercentOfDrawFar );
void DrawTap(const TapNote& tn, const NoteFieldRenderArgs& field_args,
const NoteColumnRenderArgs& column_args, float fBeat,
bool bOnSameRowAsHoldStart,
bool bOnSameRowAsRollBeat, bool bIsAddition, float fPercentFadeToFail);
void DrawHold(const TapNote& tn, const NoteFieldRenderArgs& field_args,
const NoteColumnRenderArgs& column_args, int iRow, bool bIsBeingHeld,
const HoldNoteResult &Result,
bool bIsAddition, float fPercentFadeToFail);
bool DrawHoldHeadForTapsOnSameRow() const;
bool DrawRollHeadForTapsOnSameRow() const;
private:
@@ -128,14 +219,23 @@ private:
Actor *GetHoldActor( NoteColorActor nca[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, int iCol, float fYOffset, float fBeat, bool bIsAddition, float fPercentFadeToFail,
float fReverseOffsetPixels, float fColorScale, float fDrawDistanceAfterTargetsPixels, float fDrawDistanceBeforeTargetsPixels, float fFadeInPercentOfDrawFar );
void DrawHoldBody( const TapNote& tn, int iCol, float fBeat, bool bIsBeingHeld, float fYHead, float fYTail, bool bIsAddition, float fPercentFadeToFail,
float fColorScale,
bool bGlow, float fDrawDistanceAfterTargetsPixels, float fDrawDistanceBeforeTargetsPixels, float fFadeInPercentOfDrawFar );
void DrawHoldPart( vector<Sprite*> &vpSpr, int iCol, int fYStep, float fPercentFadeToFail, float fColorScale, bool bGlow,
float fDrawDistanceAfterTargetsPixels, float fDrawDistanceBeforeTargetsPixels, float fFadeInPercentOfDrawFar, float fOverlappedTime,
float fYTop, float fYBottom, float fYStartPos, float fYEndPos, bool bWrapping, bool bAnchorToTop, bool bFlipTextureVertically );
void DrawActor(const TapNote& tn, Actor* pActor, NotePart part,
const NoteFieldRenderArgs& field_args,
const NoteColumnRenderArgs& column_args, float fYOffset, float fBeat,
bool bIsAddition, float fPercentFadeToFail, float fColorScale,
bool is_being_held);
void DrawHoldBody(const TapNote& tn, const NoteFieldRenderArgs& field_args,
const NoteColumnRenderArgs& column_args, float fBeat, bool bIsBeingHeld,
float fYHead, float fYTail,
bool bIsAddition, float fPercentFadeToFail, float fColorScale,
bool bGlow, float top_beat, float bottom_beat);
void DrawHoldPart(vector<Sprite*> &vpSpr,
const NoteFieldRenderArgs& field_args,
const NoteColumnRenderArgs& column_args, int fYStep,
float fPercentFadeToFail, float fColorScale, bool bGlow,
float fOverlappedTime, float fYTop, float fYBottom, float fYStartPos,
float fYEndPos, bool bWrapping, bool bAnchorToTop,
bool bFlipTextureVertically, float top_beat, float bottom_beat);
const PlayerState *m_pPlayerState; // to look up PlayerOptions
NoteMetricCache_t *cache;
@@ -152,9 +252,72 @@ private:
float m_fYReverseOffsetPixels;
};
// 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
// NoteDisplays that render the notes.
// NoteColumnRenderer is also used as a fake parent for the receptor and ghost
// 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
struct NoteColumnRenderer : public Actor
{
NoteDisplay* m_displays[PLAYER_INVALID+1];
NoteFieldRenderArgs* m_field_render_args;
NoteColumnRenderArgs m_column_render_args;
int m_column;
// 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 PushSelf(lua_State* L);
struct NCR_TweenState
{
NCR_TweenState();
NCSplineHandler m_pos_handler;
NCSplineHandler m_rot_handler;
NCSplineHandler m_zoom_handler;
static void MakeWeightedAverage(NCR_TweenState& out,
const NCR_TweenState& from, const NCR_TweenState& to, float between);
bool operator==(const NCR_TweenState& other) const;
bool operator!=(const NCR_TweenState& other) const { return !operator==(other); }
};
NCR_TweenState& NCR_DestTweenState()
{
if(NCR_Tweens.empty())
{ return NCR_current; }
else
{ return NCR_Tweens.back(); }
}
const NCR_TweenState& NCR_DestTweenState() const { return const_cast<NoteColumnRenderer*>(this)->NCR_DestTweenState(); }
virtual void SetCurrentTweenStart();
virtual void EraseHeadTween();
virtual void UpdatePercentThroughTween(float between);
virtual void BeginTweening(float time, ITween* interp);
virtual void StopTweening();
virtual void FinishTweening();
NCSplineHandler* GetPosHandler() { return &NCR_DestTweenState().m_pos_handler; }
NCSplineHandler* GetRotHandler() { return &NCR_DestTweenState().m_rot_handler; }
NCSplineHandler* GetZoomHandler() { return &NCR_DestTweenState().m_zoom_handler; }
private:
vector<NCR_TweenState> NCR_Tweens;
NCR_TweenState NCR_current;
NCR_TweenState NCR_start;
};
#endif
/**
* NoteColumnRenderer and associated spline stuff (c) Eric Reese 2014-2015
* @file
* @author Brian Bugh, Ben Nordstrom, Chris Danford, Steve Checkoway (c) 2001-2006
* @section LICENSE
+111 -225
View File
@@ -36,11 +36,8 @@ static ThemeMetric<float> FADE_FAIL_TIME( "NoteField", "FadeFailTime" );
static RString RoutineNoteSkinName( size_t i ) { return ssprintf("RoutineNoteSkinP%i",int(i+1)); }
static ThemeMetric1D<RString> ROUTINE_NOTESKIN( "NoteField", RoutineNoteSkinName, NUM_PLAYERS );
static bool FAST_NOTE_RENDERING_PREF_CACHED= false;
NoteField::NoteField()
{
FAST_NOTE_RENDERING_PREF_CACHED= PREFSMAN->m_FastNoteRendering;
m_pNoteData = NULL;
m_pCurDisplay = NULL;
@@ -64,9 +61,13 @@ NoteField::NoteField()
m_sprBeatBars.Load( THEME->GetPathG("NoteField","bars") );
m_sprBeatBars.StopAnimating();
// 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
m_FieldRenderArgs.selection_begin_marker= &m_iBeginMarker;
m_FieldRenderArgs.selection_end_marker= &m_iEndMarker;
m_iBeginMarker = m_iEndMarker = -1;
m_fPercentFadeToFail = -1;
m_FieldRenderArgs.fail_fade = -1;
m_StepCallback.SetFromNil();
m_SetPressedCallback.SetFromNil();
@@ -169,6 +170,8 @@ void NoteField::CacheAllUsedNoteSkins()
ASSERT_M( it != m_NoteDisplays.end(), sNoteSkinLower );
m_pDisplays[pn] = it->second;
}
InitColumnRenderers();
}
void NoteField::Init( const PlayerState* pPlayerState, float fYReverseOffsetPixels )
@@ -189,7 +192,7 @@ void NoteField::Load(
m_iDrawDistanceBeforeTargetsPixels = iDrawDistanceBeforeTargetsPixels;
ASSERT( m_iDrawDistanceBeforeTargetsPixels >= m_iDrawDistanceAfterTargetsPixels );
m_fPercentFadeToFail = -1;
m_FieldRenderArgs.fail_fade = -1;
//int i1 = m_pNoteData->GetNumTracks();
//int i2 = GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer;
@@ -240,6 +243,30 @@ void NoteField::Load(
ASSERT_M( it != m_NoteDisplays.end(), sNoteSkinLower );
m_pDisplays[pn] = it->second;
}
InitColumnRenderers();
}
void NoteField::InitColumnRenderers()
{
m_FieldRenderArgs.player_state= m_pPlayerState;
m_FieldRenderArgs.reverse_offset_pixels= m_fYReverseOffsetPixels;
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);
for(size_t ncr= 0; ncr < m_ColumnRenderers.size(); ++ncr)
{
FOREACH_EnabledPlayer(pn)
{
m_ColumnRenderers[ncr].m_displays[pn]= &(m_pDisplays[pn]->display[ncr]);
}
m_ColumnRenderers[ncr].m_displays[PLAYER_INVALID]= &(m_pCurDisplay->display[ncr]);
m_ColumnRenderers[ncr].m_column= ncr;
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_GhostArrowRow.SetColumnRenderers(m_ColumnRenderers);
}
void NoteField::Update( float fDeltaTime )
@@ -251,6 +278,11 @@ void NoteField::Update( float fDeltaTime )
ActorFrame::Update( fDeltaTime );
for(size_t c= 0; c < m_ColumnRenderers.size(); ++c)
{
m_ColumnRenderers[c].Update(fDeltaTime);
}
// update m_fBoardOffsetPixels, m_fCurrentBeatLastUpdate, m_fYPosCurrentBeatLastUpdate
const float fCurrentBeat = m_pPlayerState->GetDisplayedPosition().m_fSongBeat;
bool bTweeningOn = m_sprBoard->GetCurrentDiffuseAlpha() >= 0.98 && m_sprBoard->GetCurrentDiffuseAlpha() < 1.00; // HACK
@@ -276,11 +308,11 @@ void NoteField::Update( float fDeltaTime )
cur->m_ReceptorArrowRow.Update( fDeltaTime );
cur->m_GhostArrowRow.Update( fDeltaTime );
if( m_fPercentFadeToFail >= 0 )
m_fPercentFadeToFail = min( m_fPercentFadeToFail + fDeltaTime/FADE_FAIL_TIME, 1 );
if( m_FieldRenderArgs.fail_fade >= 0 )
m_FieldRenderArgs.fail_fade = min( m_FieldRenderArgs.fail_fade + fDeltaTime/FADE_FAIL_TIME, 1 );
// Update fade to failed
m_pCurDisplay->m_ReceptorArrowRow.SetFadeToFailPercent( m_fPercentFadeToFail );
m_pCurDisplay->m_ReceptorArrowRow.SetFadeToFailPercent( m_FieldRenderArgs.fail_fade );
NoteDisplay::Update( fDeltaTime );
/* Update all NoteDisplays. Hack: We need to call this once per frame, not
@@ -801,13 +833,11 @@ float FindLastDisplayedBeat( const PlayerState* pPlayerState, int iDrawDistanceB
return fLastBeatToDraw;
}
inline float NoteRowToVisibleBeat( const PlayerState *pPlayerState, int iRow )
{
return NoteRowToBeat(iRow);
}
bool NoteField::IsOnScreen( float fBeat, int iCol, int iDrawDistanceAfterTargetsPixels, int iDrawDistanceBeforeTargetsPixels ) const
{
// IMPORTANT: Do not modify this function without also modifying the
// version that is in NoteDisplay.cpp or coming up with a good way to
// merge them. -Kyz
// TRICKY: If boomerang is on, then ones in the range
// [iFirstRowToDraw,iLastRowToDraw] aren't necessarily visible.
// Test to see if this beat is visible before drawing.
@@ -834,41 +864,41 @@ void NoteField::DrawPrimitives()
const PlayerOptions &current_po = m_pPlayerState->m_PlayerOptions.GetCurrent();
// Adjust draw range depending on some effects
int iDrawDistanceAfterTargetsPixels = m_iDrawDistanceAfterTargetsPixels;
m_FieldRenderArgs.draw_pixels_after_targets= m_iDrawDistanceAfterTargetsPixels;
// HACK: If boomerang and centered are on, then we want to draw much
// earlier so that the notes don't pop on screen.
float fCenteredTimesBoomerang =
current_po.m_fScrolls[PlayerOptions::SCROLL_CENTERED] *
current_po.m_fAccels[PlayerOptions::ACCEL_BOOMERANG];
iDrawDistanceAfterTargetsPixels += int(SCALE( fCenteredTimesBoomerang, 0.f, 1.f, 0.f, -SCREEN_HEIGHT/2 ));
int iDrawDistanceBeforeTargetsPixels = m_iDrawDistanceBeforeTargetsPixels;
m_FieldRenderArgs.draw_pixels_after_targets += int(SCALE( fCenteredTimesBoomerang, 0.f, 1.f, 0.f, -SCREEN_HEIGHT/2 ));
m_FieldRenderArgs.draw_pixels_before_targets = m_iDrawDistanceBeforeTargetsPixels;
float fDrawScale = 1;
fDrawScale *= 1 + 0.5f * fabsf( current_po.m_fPerspectiveTilt );
fDrawScale *= 1 + fabsf( current_po.m_fEffects[PlayerOptions::EFFECT_MINI] );
iDrawDistanceAfterTargetsPixels = (int)(iDrawDistanceAfterTargetsPixels * fDrawScale);
iDrawDistanceBeforeTargetsPixels = (int)(iDrawDistanceBeforeTargetsPixels * fDrawScale);
m_FieldRenderArgs.draw_pixels_after_targets = (int)(m_FieldRenderArgs.draw_pixels_after_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
float fFirstBeatToDraw = FindFirstDisplayedBeat( m_pPlayerState, iDrawDistanceAfterTargetsPixels );
float fLastBeatToDraw = FindLastDisplayedBeat( m_pPlayerState, iDrawDistanceBeforeTargetsPixels );
float fFirstBeatToDraw = FindFirstDisplayedBeat( m_pPlayerState, m_FieldRenderArgs.draw_pixels_after_targets );
float fLastBeatToDraw = FindLastDisplayedBeat( m_pPlayerState, m_FieldRenderArgs.draw_pixels_before_targets );
m_pPlayerState->m_fLastDrawnBeat = fLastBeatToDraw;
const int iFirstRowToDraw = BeatToNoteRow(fFirstBeatToDraw);
const int iLastRowToDraw = BeatToNoteRow(fLastBeatToDraw);
m_FieldRenderArgs.first_row = BeatToNoteRow(fFirstBeatToDraw);
m_FieldRenderArgs.last_row = BeatToNoteRow(fLastBeatToDraw);
//LOG->Trace( "start = %f.1, end = %f.1", fFirstBeatToDraw-fSongBeat, fLastBeatToDraw-fSongBeat );
//LOG->Trace( "Drawing elements %d through %d", iFirstRowToDraw, iLastRowToDraw );
//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, iDrawDistanceAfterTargetsPixels, iDrawDistanceBeforeTargetsPixels ) )
#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
if( SHOW_BOARD )
{
DrawBoard( iDrawDistanceAfterTargetsPixels, iDrawDistanceBeforeTargetsPixels );
DrawBoard( m_FieldRenderArgs.draw_pixels_after_targets, m_FieldRenderArgs.draw_pixels_before_targets );
}
// Draw Receptors
@@ -891,7 +921,7 @@ void NoteField::DrawPrimitives()
for (i = 0; i < tSigs.size(); i++)
{
const TimeSignatureSegment *ts = ToTimeSignature(tSigs[i]);
int iSegmentEndRow = (i + 1 == tSigs.size()) ? iLastRowToDraw : tSigs[i+1]->GetRow();
int iSegmentEndRow = (i + 1 == tSigs.size()) ? m_FieldRenderArgs.last_row : tSigs[i+1]->GetRow();
// beat bars every 16th note
int iDrawBeatBarsEveryRows = BeatToNoteRow( ((float)ts->GetDen()) / 4 ) / 4;
@@ -934,7 +964,7 @@ void NoteField::DrawPrimitives()
for (i = 0; i < segs[SEGMENT_SCROLL]->size(); i++)
{
ScrollSegment *seg = ToScroll( segs[SEGMENT_SCROLL]->at(i) );
if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw )
if( seg->GetRow() >= m_FieldRenderArgs.first_row && seg->GetRow() <= m_FieldRenderArgs.last_row )
{
float fBeat = seg->GetBeat();
if( IS_ON_SCREEN(fBeat) )
@@ -946,7 +976,7 @@ void NoteField::DrawPrimitives()
for (i = 0; i < segs[SEGMENT_BPM]->size(); i++)
{
const BPMSegment *seg = ToBPM( segs[SEGMENT_BPM]->at(i) );
if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw )
if( seg->GetRow() >= m_FieldRenderArgs.first_row && seg->GetRow() <= m_FieldRenderArgs.last_row )
{
float fBeat = seg->GetBeat();
if( IS_ON_SCREEN(fBeat) )
@@ -958,7 +988,7 @@ void NoteField::DrawPrimitives()
for (i = 0; i < segs[SEGMENT_STOP]->size(); i++)
{
const StopSegment *seg = ToStop( segs[SEGMENT_STOP]->at(i) );
if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw )
if( seg->GetRow() >= m_FieldRenderArgs.first_row && seg->GetRow() <= m_FieldRenderArgs.last_row )
{
float fBeat = seg->GetBeat();
if( IS_ON_SCREEN(fBeat) )
@@ -970,7 +1000,7 @@ void NoteField::DrawPrimitives()
for (i = 0; i < segs[SEGMENT_DELAY]->size(); i++)
{
const DelaySegment *seg = ToDelay( segs[SEGMENT_DELAY]->at(i) );
if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw )
if( seg->GetRow() >= m_FieldRenderArgs.first_row && seg->GetRow() <= m_FieldRenderArgs.last_row )
{
float fBeat = seg->GetBeat();
if( IS_ON_SCREEN(fBeat) )
@@ -982,7 +1012,7 @@ void NoteField::DrawPrimitives()
for (i = 0; i < segs[SEGMENT_WARP]->size(); i++)
{
const WarpSegment *seg = ToWarp( segs[SEGMENT_WARP]->at(i) );
if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw )
if( seg->GetRow() >= m_FieldRenderArgs.first_row && seg->GetRow() <= m_FieldRenderArgs.last_row )
{
float fBeat = seg->GetBeat();
if( IS_ON_SCREEN(fBeat) )
@@ -994,7 +1024,7 @@ void NoteField::DrawPrimitives()
for (i = 0; i < segs[SEGMENT_TIME_SIG]->size(); i++)
{
const TimeSignatureSegment *seg = ToTimeSignature( segs[SEGMENT_TIME_SIG]->at(i) );
if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw )
if( seg->GetRow() >= m_FieldRenderArgs.first_row && seg->GetRow() <= m_FieldRenderArgs.last_row )
{
float fBeat = seg->GetBeat();
if( IS_ON_SCREEN(fBeat) )
@@ -1006,7 +1036,7 @@ void NoteField::DrawPrimitives()
for (i = 0; i < segs[SEGMENT_TICKCOUNT]->size(); i++)
{
const TickcountSegment *seg = ToTickcount( segs[SEGMENT_TICKCOUNT]->at(i) );
if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw )
if( seg->GetRow() >= m_FieldRenderArgs.first_row && seg->GetRow() <= m_FieldRenderArgs.last_row )
{
float fBeat = seg->GetBeat();
if( IS_ON_SCREEN(fBeat) )
@@ -1018,7 +1048,7 @@ void NoteField::DrawPrimitives()
for (i = 0; i < segs[SEGMENT_COMBO]->size(); i++)
{
const ComboSegment *seg = ToCombo( segs[SEGMENT_COMBO]->at(i) );
if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw )
if( seg->GetRow() >= m_FieldRenderArgs.first_row && seg->GetRow() <= m_FieldRenderArgs.last_row )
{
float fBeat = seg->GetBeat();
if( IS_ON_SCREEN(fBeat) )
@@ -1030,7 +1060,7 @@ void NoteField::DrawPrimitives()
for (i = 0; i < segs[SEGMENT_LABEL]->size(); i++)
{
const LabelSegment *seg = ToLabel( segs[SEGMENT_LABEL]->at(i) );
if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw )
if( seg->GetRow() >= m_FieldRenderArgs.first_row && seg->GetRow() <= m_FieldRenderArgs.last_row )
{
float fBeat = seg->GetBeat();
if( IS_ON_SCREEN(fBeat) )
@@ -1042,7 +1072,7 @@ void NoteField::DrawPrimitives()
for (i = 0; i < segs[SEGMENT_SPEED]->size(); i++)
{
const SpeedSegment *seg = ToSpeed( segs[SEGMENT_SPEED]->at(i) );
if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw )
if( seg->GetRow() >= m_FieldRenderArgs.first_row && seg->GetRow() <= m_FieldRenderArgs.last_row )
{
float fBeat = seg->GetBeat();
if( IS_ON_SCREEN(fBeat) )
@@ -1055,7 +1085,7 @@ void NoteField::DrawPrimitives()
for (i = 0; i < segs[SEGMENT_FAKE]->size(); i++)
{
const FakeSegment *seg = ToFake( segs[SEGMENT_FAKE]->at(i) );
if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw )
if( seg->GetRow() >= m_FieldRenderArgs.first_row && seg->GetRow() <= m_FieldRenderArgs.last_row )
{
float fBeat = seg->GetBeat();
if( IS_ON_SCREEN(fBeat) )
@@ -1075,8 +1105,8 @@ void NoteField::DrawPrimitives()
float fSecond = a->fStartSecond;
float fBeat = timing.GetBeatFromElapsedTime( fSecond );
if( BeatToNoteRow(fBeat) >= iFirstRowToDraw &&
BeatToNoteRow(fBeat) <= iLastRowToDraw)
if( BeatToNoteRow(fBeat) >= m_FieldRenderArgs.first_row &&
BeatToNoteRow(fBeat) <= m_FieldRenderArgs.last_row)
{
if( IS_ON_SCREEN(fBeat) )
DrawAttackText( fBeat, *a );
@@ -1094,8 +1124,8 @@ void NoteField::DrawPrimitives()
FOREACH_CONST(Attack, attacks, a)
{
float fBeat = timing.GetBeatFromElapsedTime(a->fStartSecond);
if (BeatToNoteRow(fBeat) >= iFirstRowToDraw &&
BeatToNoteRow(fBeat) <= iLastRowToDraw &&
if (BeatToNoteRow(fBeat) >= m_FieldRenderArgs.first_row &&
BeatToNoteRow(fBeat) <= m_FieldRenderArgs.last_row &&
IS_ON_SCREEN(fBeat))
{
this->DrawAttackText(fBeat, *a);
@@ -1179,20 +1209,20 @@ void NoteField::DrawPrimitives()
{
int iBegin = m_iBeginMarker;
int iEnd = m_iEndMarker;
CLAMP( iBegin, iFirstRowToDraw, iLastRowToDraw );
CLAMP( iEnd, iFirstRowToDraw, iLastRowToDraw );
CLAMP( iBegin, m_FieldRenderArgs.first_row, m_FieldRenderArgs.last_row );
CLAMP( iEnd, m_FieldRenderArgs.first_row, m_FieldRenderArgs.last_row );
DrawAreaHighlight( iBegin, iEnd );
}
else if( m_iBeginMarker != -1 )
{
if( m_iBeginMarker >= iFirstRowToDraw &&
m_iBeginMarker <= iLastRowToDraw )
if( m_iBeginMarker >= m_FieldRenderArgs.first_row &&
m_iBeginMarker <= m_FieldRenderArgs.last_row )
DrawMarkerBar( m_iBeginMarker );
}
else if( m_iEndMarker != -1 )
{
if( m_iEndMarker >= iFirstRowToDraw &&
m_iEndMarker <= iLastRowToDraw )
if( m_iEndMarker >= m_FieldRenderArgs.first_row &&
m_iEndMarker <= m_FieldRenderArgs.last_row )
DrawMarkerBar( m_iEndMarker );
}
}
@@ -1201,175 +1231,19 @@ void NoteField::DrawPrimitives()
// Draw the arrows in order of column. This minimizes texture switches and
// lets us draw in big batches.
float fSelectedRangeGlow = SCALE( RageFastCos(RageTimer::GetTimeSinceStartFast()*2), -1, 1, 0.1f, 0.3f );
const Style* pStyle = GAMESTATE->GetCurrentStyle();
ASSERT_M(m_pNoteData->GetNumTracks() == GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer,
ssprintf("NumTracks %d != ColsPerPlayer %d",m_pNoteData->GetNumTracks(),
GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer));
m_FieldRenderArgs.selection_glow= SCALE(
RageFastCos(RageTimer::GetTimeSinceStartFast()*2), -1, 1, 0.1f, 0.3f);
m_FieldRenderArgs.fade_before_targets= FADE_BEFORE_TARGETS_PERCENT;
for( int j=0; j<m_pNoteData->GetNumTracks(); j++ ) // for each arrow column
{
const int c = pStyle->m_iColumnDrawOrder[j];
bool bAnyUpcomingInThisCol = false;
// Draw all HoldNotes in this column (so that they appear under the tap notes)
{
NoteData::TrackMap::const_iterator begin, end;
m_pNoteData->GetTapNoteRangeInclusive( c, iFirstRowToDraw, iLastRowToDraw+1, begin, end );
for( ; begin != end; ++begin )
{
const TapNote &tn = begin->second; //m_pNoteData->GetTapNote(c, j);
if( tn.type != TapNoteType_HoldHead )
continue; // skip
const HoldNoteResult &Result = tn.HoldResult;
if( Result.hns == HNS_Held ) // if this HoldNote was completed
continue; // don't draw anything
int iStartRow = begin->first;
int iEndRow = iStartRow + tn.iDuration;
// TRICKY: If boomerang is on, then all notes in the range
// [iFirstRowToDraw,iLastRowToDraw] aren't necessarily visible.
// Test every note to make sure it's on screen before drawing
float fThrowAway;
bool bStartIsPastPeak = false;
bool bEndIsPastPeak = false;
float fStartYOffset = ArrowEffects::GetYOffset( m_pPlayerState, c, NoteRowToVisibleBeat(m_pPlayerState, iStartRow), fThrowAway, bStartIsPastPeak );
float fEndYOffset = ArrowEffects::GetYOffset( m_pPlayerState, c, NoteRowToVisibleBeat(m_pPlayerState, iEndRow), fThrowAway, bEndIsPastPeak );
bool bTailIsOnVisible = iDrawDistanceAfterTargetsPixels <= fEndYOffset && fEndYOffset <= iDrawDistanceBeforeTargetsPixels;
bool bHeadIsVisible = iDrawDistanceAfterTargetsPixels <= fStartYOffset && fStartYOffset <= iDrawDistanceBeforeTargetsPixels;
bool bStraddlingVisible = fStartYOffset <= iDrawDistanceAfterTargetsPixels && iDrawDistanceBeforeTargetsPixels <= fEndYOffset;
bool bStaddlingPeak = bStartIsPastPeak && !bEndIsPastPeak;
if( !(bTailIsOnVisible || bHeadIsVisible || bStraddlingVisible || bStaddlingPeak) )
{
//LOG->Trace( "skip drawing this hold." );
continue; // skip
}
bool bIsAddition = (tn.source == TapNoteSource_Addition);
bool bIsHopoPossible = (tn.bHopoPossible);
bool bUseAdditionColoring = bIsAddition || bIsHopoPossible;
const bool bHoldGhostShowing = tn.HoldResult.bActive && tn.HoldResult.fLife > 0;
const bool bIsHoldingNote = tn.HoldResult.bHeld;
if( bHoldGhostShowing )
m_pCurDisplay->m_GhostArrowRow.SetHoldShowing( c, tn );
ASSERT_M( NoteRowToBeat(iStartRow) > -2000, ssprintf("%i %i %i", iStartRow, iEndRow, c) );
bool bIsInSelectionRange = false;
if( m_iBeginMarker!=-1 && m_iEndMarker!=-1 )
bIsInSelectionRange = (m_iBeginMarker <= iStartRow && iEndRow < m_iEndMarker);
NoteDisplayCols *displayCols = tn.pn == PLAYER_INVALID ? m_pCurDisplay : m_pDisplays[tn.pn];
displayCols->display[c].DrawHold( tn, c, iStartRow, bIsHoldingNote, Result, bUseAdditionColoring, bIsInSelectionRange ? fSelectedRangeGlow : m_fPercentFadeToFail,
m_fYReverseOffsetPixels, (float) iDrawDistanceAfterTargetsPixels, (float) iDrawDistanceBeforeTargetsPixels, iDrawDistanceBeforeTargetsPixels, FADE_BEFORE_TARGETS_PERCENT );
bool bNoteIsUpcoming = NoteRowToBeat(iStartRow) > m_pPlayerState->GetDisplayedPosition().m_fSongBeat;
bAnyUpcomingInThisCol |= bNoteIsUpcoming;
}
}
// Draw all TapNotes in this column
// draw notes from furthest to closest
NoteData::TrackMap::const_iterator begin, end;
m_pNoteData->GetTapNoteRange( c, iFirstRowToDraw, iLastRowToDraw+1, begin, end );
for( ; begin != end; ++begin )
{
int q = begin->first;
const TapNote &tn = begin->second; //m_pNoteData->GetTapNote(c, q);
// Switch modified by Wolfman2000, tested by Saturn2888
// Fixes hold head overlapping issue, but not the rolls.
switch( tn.type )
{
case TapNoteType_Empty: // no note here
{
continue;
}
case TapNoteType_HoldHead:
{
//if (tn.subType == TapNoteSubType_Roll)
continue; // skip
}
default: break;
}
// Don't draw hidden (fully judged) steps.
if( tn.result.bHidden )
continue;
// TRICKY: If boomerang is on, then all notes in the range
// [iFirstRowToDraw,iLastRowToDraw] aren't necessarily visible.
// Test every note to make sure it's on screen before drawing.
if( !IsOnScreen( NoteRowToBeat(q), c, iDrawDistanceAfterTargetsPixels, iDrawDistanceBeforeTargetsPixels ) )
continue; // skip
ASSERT_M( NoteRowToBeat(q) > -2000, ssprintf("%i %i %i, %f %f", q, iLastRowToDraw,
iFirstRowToDraw, m_pPlayerState->GetDisplayedPosition().m_fSongBeat, m_pPlayerState->GetDisplayedPosition().m_fMusicSeconds) );
// See if there is a hold step that begins on this index.
// Only do this if the noteskin cares.
bool bHoldNoteBeginsOnThisBeat = false;
if( m_pCurDisplay->display[c].DrawHoldHeadForTapsOnSameRow() )
{
for( int c2=0; c2<m_pNoteData->GetNumTracks(); c2++ )
{
const TapNote &tmp = m_pNoteData->GetTapNote(c2, q);
if(tmp.type == TapNoteType_HoldHead &&
tmp.subType == TapNoteSubType_Hold)
{
bHoldNoteBeginsOnThisBeat = true;
break;
}
}
}
// do the same for a roll.
bool bRollNoteBeginsOnThisBeat = false;
if (m_pCurDisplay->display[c].DrawRollHeadForTapsOnSameRow() )
{
for( int c2=0; c2<m_pNoteData->GetNumTracks(); c2++ )
{
const TapNote &tmp = m_pNoteData->GetTapNote(c2, q);
if(tmp.type == TapNoteType_HoldHead &&
tmp.subType == TapNoteSubType_Roll)
{
bRollNoteBeginsOnThisBeat = true;
break;
}
}
}
bool bIsInSelectionRange = false;
if( m_iBeginMarker!=-1 && m_iEndMarker!=-1 )
bIsInSelectionRange = m_iBeginMarker<=q && q<m_iEndMarker;
bool bIsAddition = (tn.source == TapNoteSource_Addition);
bool bIsHopoPossible = (tn.bHopoPossible);
bool bUseAdditionColoring = bIsAddition || bIsHopoPossible;
NoteDisplayCols *displayCols = tn.pn == PLAYER_INVALID ? m_pCurDisplay : m_pDisplays[tn.pn];
displayCols->display[c].DrawTap(tn, c, NoteRowToVisibleBeat(m_pPlayerState, q),
bHoldNoteBeginsOnThisBeat, bRollNoteBeginsOnThisBeat,
bUseAdditionColoring, bIsInSelectionRange ? fSelectedRangeGlow : m_fPercentFadeToFail,
m_fYReverseOffsetPixels, iDrawDistanceAfterTargetsPixels, iDrawDistanceBeforeTargetsPixels,
FADE_BEFORE_TARGETS_PERCENT );
bool bNoteIsUpcoming = NoteRowToBeat(q) > m_pPlayerState->GetDisplayedPosition().m_fSongBeat;
bAnyUpcomingInThisCol |= bNoteIsUpcoming;
if(!FAST_NOTE_RENDERING_PREF_CACHED)
{
DISPLAY->ClearZBuffer();
}
}
cur->m_ReceptorArrowRow.SetNoteUpcoming( c, bAnyUpcomingInThisCol );
m_ColumnRenderers[c].Draw();
}
cur->m_GhostArrowRow.Draw();
@@ -1377,7 +1251,7 @@ void NoteField::DrawPrimitives()
void NoteField::FadeToFail()
{
m_fPercentFadeToFail = max( 0.0f, m_fPercentFadeToFail ); // 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
}
@@ -1523,10 +1397,10 @@ public:
} \
return 0; \
}
SET_CALLBACK_GENERIC(SetStepCallback, m_StepCallback);
SET_CALLBACK_GENERIC(SetSetPressedCallback, m_SetPressedCallback);
SET_CALLBACK_GENERIC(SetDidTapNoteCallback, m_DidTapNoteCallback);
SET_CALLBACK_GENERIC(SetDidHoldNoteCallback, m_DidHoldNoteCallback);
SET_CALLBACK_GENERIC(set_step_callback, m_StepCallback);
SET_CALLBACK_GENERIC(set_set_pressed_callback, m_SetPressedCallback);
SET_CALLBACK_GENERIC(set_did_tap_note_callback, m_DidTapNoteCallback);
SET_CALLBACK_GENERIC(set_did_hold_note_callback, m_DidHoldNoteCallback);
#undef SET_CALLBACK_GENERIC
static int check_column(lua_State* L, int index)
@@ -1542,7 +1416,7 @@ public:
return col;
}
static int Step(T* p, lua_State* L)
static int step(T* p, lua_State* L)
{
int col= check_column(L, 1);
TapNoteScore tns= Enum::Check<TapNoteScore>(L, 2);
@@ -1550,14 +1424,14 @@ public:
return 0;
}
static int SetPressed(T* p, lua_State* L)
static int set_pressed(T* p, lua_State* L)
{
int col= check_column(L, 1);
p->SetPressed(col, true);
return 0;
}
static int DidTapNote(T* p, lua_State* L)
static int did_tap_note(T* p, lua_State* L)
{
int col= check_column(L, 1);
TapNoteScore tns= Enum::Check<TapNoteScore>(L, 2);
@@ -1566,7 +1440,7 @@ public:
return 0;
}
static int DidHoldNote(T* p, lua_State* L)
static int did_hold_note(T* p, lua_State* L)
{
int col= check_column(L, 1);
HoldNoteScore hns= Enum::Check<HoldNoteScore>(L, 2);
@@ -1575,16 +1449,28 @@ public:
return 0;
}
static int get_column_actors(T* p, lua_State* L)
{
lua_createtable(L, p->m_ColumnRenderers.size(), 0);
for(size_t i= 0; i < p->m_ColumnRenderers.size(); ++i)
{
p->m_ColumnRenderers[i].PushSelf(L);
lua_rawseti(L, -2, i+1);
}
return 1;
}
LunaNoteField()
{
ADD_METHOD(SetStepCallback);
ADD_METHOD(SetSetPressedCallback);
ADD_METHOD(SetDidTapNoteCallback);
ADD_METHOD(SetDidHoldNoteCallback);
ADD_METHOD(Step);
ADD_METHOD(SetPressed);
ADD_METHOD(DidTapNote);
ADD_METHOD(DidHoldNote);
ADD_METHOD(set_step_callback);
ADD_METHOD(set_set_pressed_callback);
ADD_METHOD(set_did_tap_note_callback);
ADD_METHOD(set_did_hold_note_callback);
ADD_METHOD(step);
ADD_METHOD(set_pressed);
ADD_METHOD(did_tap_note);
ADD_METHOD(did_hold_note);
ADD_METHOD(get_column_actors);
}
};
+8 -2
View File
@@ -29,6 +29,8 @@ public:
int iDrawDistanceBeforeTargetsPixels );
virtual void Unload();
void InitColumnRenderers();
virtual void HandleMessage( const Message &msg );
// This is done automatically by Init(), but can be re-called explicitly if the
@@ -55,6 +57,10 @@ public:
int m_iBeginMarker, m_iEndMarker; // only used with MODE_EDIT
// m_ColumnRenderers belongs in the protected section, but it's here in
// public so that the Lua API can access it. -Kyz
vector<NoteColumnRenderer> m_ColumnRenderers;
protected:
void CacheNoteSkin( const RString &sNoteSkin );
void UncacheNoteSkin( const RString &sNoteSkin );
@@ -84,8 +90,6 @@ protected:
const NoteData *m_pNoteData;
float m_fPercentFadeToFail; // -1 if not fading to fail
const PlayerState* m_pPlayerState;
int m_iDrawDistanceAfterTargetsPixels; // this should be a negative number
int m_iDrawDistanceBeforeTargetsPixels; // this should be a positive number
@@ -101,6 +105,8 @@ protected:
~NoteDisplayCols() { delete [] display; }
};
NoteFieldRenderArgs m_FieldRenderArgs;
/* All loaded note displays, mapped by their name. */
map<RString, NoteDisplayCols *> m_NoteDisplays;
NoteDisplayCols *m_pCurDisplay;
+32
View File
@@ -5,6 +5,7 @@
*/
#include "global.h"
#include "RageLog.h"
#include "RageMath.h"
#include "RageTypes.h"
#include <float.h>
@@ -40,6 +41,22 @@ void RageVec3Normalize( RageVector3* pOut, const RageVector3* pV )
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 )
{
RageVector4 temp( pV->x, pV->y, pV->z, 1.0f ); // translate
@@ -313,6 +330,21 @@ void RageMatrixRotationXYZ( RageMatrix* pOut, float rX, float rY, float rZ )
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 )
{
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 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 RageVec3TransformNormal( RageVector3* pOut, const RageVector3* 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 RageMatrixRotationZ( RageMatrix* pOut, float fTheta );
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 RageQuatFromPRH(RageVector4* pOut, RageVector3 prh );
void RageMatrixFromQuat( RageMatrix* pOut, const RageVector4 q );
+6
View File
@@ -61,6 +61,12 @@ inline bool CLAMP( unsigned &x, unsigned l, unsigned h )
else if (x < l) { x = l; return true; }
return false;
}
inline bool CLAMP( size_t &x, size_t l, size_t h )
{
if (x > h) { x = h; return true; }
else if (x < l) { x = l; return true; }
return false;
}
inline bool CLAMP( float &x, float l, float h )
{
if (x > h) { x = h; return true; }
+11 -12
View File
@@ -30,6 +30,16 @@ void ReceptorArrowRow::Load( const PlayerState* pPlayerState, float fYReverseOff
}
}
void ReceptorArrowRow::SetColumnRenderers(vector<NoteColumnRenderer>& renderers)
{
ASSERT_M(renderers.size() == m_ReceptorArrow.size(), "Notefield has different number of columns than receptor row.");
for(size_t c= 0; c < m_ReceptorArrow.size(); ++c)
{
m_ReceptorArrow[c]->SetFakeParent(&(renderers[c]));
}
m_renderers= &renderers;
}
ReceptorArrowRow::~ReceptorArrowRow()
{
for( unsigned i = 0; i < m_ReceptorArrow.size(); ++i )
@@ -53,18 +63,7 @@ void ReceptorArrowRow::Update( float fDeltaTime )
m_ReceptorArrow[c]->SetBaseAlpha( fBaseAlpha );
// set arrow XYZ
float fX = ArrowEffects::GetXPos( m_pPlayerState, c, 0 );
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 );
m_ReceptorArrow[c]->SetY( fY );
m_ReceptorArrow[c]->SetZ( fZ );
const float fRotation = ArrowEffects::ReceptorGetRotationZ( m_pPlayerState );
m_ReceptorArrow[c]->SetRotationZ( fRotation );
const float fZoom = ArrowEffects::GetZoom( m_pPlayerState );
m_ReceptorArrow[c]->SetZoom( fZoom );
(*m_renderers)[c].UpdateReceptorGhostStuff(m_ReceptorArrow[c]);
}
}
+3
View File
@@ -4,6 +4,7 @@
#include "ReceptorArrow.h"
#include "ActorFrame.h"
#include "GameConstantsAndTypes.h"
#include "NoteDisplay.h"
class PlayerState;
/** @brief A row of ReceptorArrow objects. */
@@ -16,6 +17,7 @@ public:
virtual void DrawPrimitives();
void Load( const PlayerState* pPlayerState, float fYReverseOffset );
void SetColumnRenderers(vector<NoteColumnRenderer>& renderers);
void Step( int iCol, TapNoteScore score );
void SetPressed( int iCol );
@@ -28,6 +30,7 @@ protected:
float m_fYReverseOffsetPixels;
float m_fFadeToFailPercent;
vector<NoteColumnRenderer> const* m_renderers;
vector<ReceptorArrow *> m_ReceptorArrow;
};