#include "global.h" #include #include "ActorMultiVertex.h" #include "RageTextureManager.h" #include "XmlFile.h" #include "RageLog.h" #include "RageDisplay.h" #include "RageTexture.h" #include "RageUtil.h" #include "ActorUtil.h" #include "Foreach.h" #include "LuaBinding.h" #include "LuaManager.h" #include "LocalizedString.h" static const char *DrawModeNames[] = { "Quads", "QuadStrip", "Fan", "Strip", "Triangles", "LineStrip", "SymmetricQuadStrip", }; static const int DrawModeVertexGroupSizes[] = { 4, // Quads 2, // QuadStrip 1, // Fan 1, // Strip 3, // Triangles 1, // LineStrip 3, // SymmetricQuadStrip 1, // NUM_DrawMode 1, // DrawMode_Invalid }; static const int DrawModeMinimumToDraw[] = { 4, // Quads 4, // QuadStrip 3, // Fan 3, // Strip 3, // Triangles 2, // LineStrip 6, // SymmetricQuadStrip 1, // NUM_DrawMode 1, // DrawMode_Invalid }; XToString( DrawMode ); XToLocalizedString( DrawMode ); LuaXType( DrawMode ); REGISTER_ACTOR_CLASS( ActorMultiVertex ); ActorMultiVertex::ActorMultiVertex() { // Use blank texture by default. RageTextureID ID = TEXTUREMAN->GetDefaultTextureID(); _Texture = TEXTUREMAN->LoadTexture( ID ); _EffectMode = EffectMode_Normal; _TextureMode = TextureMode_Modulate; } ActorMultiVertex::~ActorMultiVertex() { UnloadTexture(); } ActorMultiVertex::ActorMultiVertex( const ActorMultiVertex &cpy ): Actor( cpy ) { #define CPY(a) a = cpy.a CPY( AMV_Tweens ); CPY( AMV_current ); CPY( AMV_start ); CPY( _EffectMode ); CPY( _TextureMode ); #undef CPY if( cpy._Texture != NULL ) { _Texture = TEXTUREMAN->CopyTexture( cpy._Texture ); } else { _Texture = NULL; } } void ActorMultiVertex::LoadFromNode( const XNode* Node ) { RString path; Node->GetAttrValue( "Texture", path ); if( !path.empty() && !TEXTUREMAN->IsTextureRegistered( RageTextureID(path) ) ) { ActorUtil::GetAttrPath( Node, "Texture", path ); } if( !path.empty() ) { LoadFromTexture( path ); } Actor::LoadFromNode( Node ); } void ActorMultiVertex::SetTexture( RageTexture *Texture ) { if( _Texture != Texture ) { UnloadTexture(); _Texture = Texture; } } void ActorMultiVertex::LoadFromTexture( RageTextureID ID ) { RageTexture *Texture = NULL; if( _Texture && _Texture->GetID() == ID ) { return; } Texture = TEXTUREMAN->LoadTexture( ID ); SetTexture( Texture ); } void ActorMultiVertex::UnloadTexture() { if( _Texture != NULL ) { TEXTUREMAN->UnloadTexture( _Texture ); _Texture = NULL; } } void ActorMultiVertex::SetNumVertices( size_t n ) { if( n == 0 ) { for( size_t i = 0; i < AMV_Tweens.size(); ++i ) { AMV_Tweens[i].vertices.clear(); } AMV_current.vertices.clear(); AMV_start.vertices.clear(); } else { for( size_t i = 0; i < AMV_Tweens.size(); ++i ) { AMV_Tweens[i].vertices.resize( n ); } AMV_current.vertices.resize( n ); AMV_start.vertices.resize( n ); } } void ActorMultiVertex::AddVertex() { for( size_t i = 0; i < AMV_Tweens.size(); ++i ) { AMV_Tweens[i].vertices.push_back( RageSpriteVertex() ); } AMV_current.vertices.push_back( RageSpriteVertex() ); AMV_start.vertices.push_back( RageSpriteVertex() ); } void ActorMultiVertex::AddVertices( int Add ) { int size = AMV_DestTweenState().vertices.size(); size += Add; for( size_t i = 0; i < AMV_Tweens.size(); ++i ) { AMV_Tweens[i].vertices.resize( size ); } AMV_current.vertices.resize( size ); AMV_start.vertices.resize( size ); } void ActorMultiVertex::SetVertexPos( int index, float x, float y, float z ) { AMV_DestTweenState().vertices[index].p = RageVector3( x, y, z ); } void ActorMultiVertex::SetVertexColor( int index, RageColor c ) { AMV_DestTweenState().vertices[index].c = c; } void ActorMultiVertex::SetVertexCoords( int index, float TexCoordX, float TexCoordY ) { AMV_DestTweenState().vertices[index].t = RageVector2( TexCoordX, TexCoordY ); } void ActorMultiVertex::DrawPrimitives() { Actor::SetGlobalRenderStates(); // set Actor-specified render states DISPLAY->ClearAllTextures(); DISPLAY->SetTexture( TextureUnit_1, _Texture->GetTexHandle() ); Actor::SetTextureRenderStates(); DISPLAY->SetEffectMode( _EffectMode ); // set temporary diffuse and glow static AMV_TweenState TS; AMV_TempState = &TS; TS = AMV_current; // skip if no change or fully transparent if( m_pTempState->diffuse[0] != RageColor(1, 1, 1, 1) && m_pTempState->diffuse[0].a > 0 ) { for( size_t i=0; i < TS.vertices.size(); i++ ) { // RageVColor * RageColor TS.vertices[i].c.b *= m_pTempState->diffuse[0].b; TS.vertices[i].c.r *= m_pTempState->diffuse[0].r; TS.vertices[i].c.g *= m_pTempState->diffuse[0].g; TS.vertices[i].c.a *= m_pTempState->diffuse[0].a; } } // Draw diffuse pass. if( m_pTempState->diffuse[0].a > 0 ) { DISPLAY->SetTextureMode( TextureUnit_1, _TextureMode ); DrawInternal( AMV_TempState ); } // Draw the glow pass if( m_pTempState->glow.a > 0 ) { for( size_t i=0; i < TS.vertices.size(); i++ ) { TS.vertices[i].c = m_pTempState->glow; } DISPLAY->SetTextureMode( TextureUnit_1, TextureMode_Glow ); DrawInternal( AMV_TempState ); } } void ActorMultiVertex::DrawInternal( const AMV_TweenState *TS ) { int FirstToDraw = TS->FirstToDraw; int NumToDraw = TS->GetSafeNumToDraw(TS->_DrawMode, TS->NumToDraw); if( NumToDraw == 0 ) { // Nothing to draw. return; } switch( TS->_DrawMode ) { case DrawMode_Quads: { DISPLAY->DrawQuads( &TS->vertices[FirstToDraw], NumToDraw ); break; } case DrawMode_QuadStrip: { DISPLAY->DrawQuadStrip( &TS->vertices[FirstToDraw], NumToDraw ); break; } case DrawMode_Fan: { DISPLAY->DrawFan( &TS->vertices[FirstToDraw], NumToDraw ); break; } case DrawMode_Strip: { DISPLAY->DrawStrip( &TS->vertices[FirstToDraw], NumToDraw ); break; } case DrawMode_Triangles: { DISPLAY->DrawTriangles( &TS->vertices[FirstToDraw], NumToDraw ); break; } case DrawMode_LineStrip: { DISPLAY->DrawLineStrip( &TS->vertices[FirstToDraw], NumToDraw, TS->line_width ); break; } case DrawMode_SymmetricQuadStrip: { DISPLAY->DrawSymmetricQuadStrip( &TS->vertices[FirstToDraw], NumToDraw ); break; } } DISPLAY->SetEffectMode( EffectMode_Normal ); } bool ActorMultiVertex::EarlyAbortDraw() const { if( AMV_current.FirstToDraw >= (int) AMV_current.vertices.size() || AMV_current._DrawMode >= NUM_DrawMode ) { return true; } return false; } void ActorMultiVertex::SetCurrentTweenStart() { AMV_start= AMV_current; } void ActorMultiVertex::EraseHeadTween() { AMV_current= AMV_Tweens[0]; AMV_Tweens.erase(AMV_Tweens.begin()); } void ActorMultiVertex::UpdatePercentThroughTween( float PercentThroughTween ) { AMV_TweenState::MakeWeightedAverage( AMV_current, AMV_start, AMV_Tweens[0], PercentThroughTween ); } void ActorMultiVertex::BeginTweening( float time, ITween *pTween ) { Actor::BeginTweening( time, pTween ); if( AMV_Tweens.size() >= 1 ) // if there was already a TS on the stack { AMV_Tweens.push_back( AMV_Tweens.back() ); } else { AMV_Tweens.push_back( AMV_current ); } } void ActorMultiVertex::StopTweening() { AMV_Tweens.clear(); Actor::StopTweening(); } void ActorMultiVertex::FinishTweening() { if( !AMV_Tweens.empty() ) { AMV_current = AMV_DestTweenState(); } Actor::FinishTweening(); } void ActorMultiVertex::AMV_TweenState::SetDrawState( DrawMode dm, int first, int num ) { if(first >= (int)vertices.size() && vertices.size() > 0) { LuaHelpers::ReportScriptErrorFmt("ActorMultiVertex:SetDrawState: FirstToDraw > vertices.size(), %d > %u", FirstToDraw + 1, (unsigned int)vertices.size() ); return; } int safe_num= GetSafeNumToDraw( dm, num ); if( num != safe_num && num != -1 ) { LuaHelpers::ReportScriptErrorFmt("ActorMultiVertex:SetDrawState: NumToDraw %d is not valid for %u vertices with DrawMode %s", num, (unsigned int)vertices.size(), DrawModeNames[dm] ); return; } _DrawMode= dm; FirstToDraw= first; NumToDraw= num; } void ActorMultiVertex::AMV_TweenState::MakeWeightedAverage(AMV_TweenState& average_out, const AMV_TweenState& ts1, const AMV_TweenState& ts2, float percent_between) { average_out.line_width= lerp(percent_between, ts1.line_width, ts2.line_width); for(size_t v= 0; v < average_out.vertices.size(); ++v) { WeightedAvergeOfRSVs(average_out.vertices[v], ts1.vertices[v], ts2.vertices[v], percent_between); } } int ActorMultiVertex::AMV_TweenState::GetSafeNumToDraw( DrawMode dm, int num ) const { int max = vertices.size() - FirstToDraw; // NumToDraw == -1 draws all vertices if( num == -1 || num > max ) { num = max; } num -= ( num % DrawModeVertexGroupSizes[dm]); if( num < DrawModeMinimumToDraw[dm]) { num = 0; } return num; } // lua start #include "LuaBinding.h" /** @brief Allow Lua to have access to the ActorMultiVertex. */ class LunaActorMultiVertex: public Luna { public: static int SetNumVertices( T* p, lua_State *L ) { p->SetNumVertices( IArg(1) ); return 0; } static int GetNumVertices( T* p, lua_State *L ) { lua_pushnumber( L, p->GetNumVertices() ); return 1; } static void SetVertexFromStack(T* p, lua_State* L, size_t VertexIndex, int DataStackIndex) { // Use the number of arguments to determine which property a table is for if(lua_type(L, DataStackIndex) != LUA_TTABLE) { LuaHelpers::ReportScriptErrorFmt("ActorMultiVertex::SetVertex: non-table parameter supplied. Table of tables of vertex data expected."); return; } size_t NumDataParts = lua_objlen(L, DataStackIndex); for(size_t i = 0; i < NumDataParts; ++i) { lua_pushnumber(L, i+1); lua_gettable(L, DataStackIndex); int DataPieceIndex = lua_gettop(L); size_t DataPieceElements = lua_objlen(L, DataPieceIndex); if(lua_type(L, DataPieceIndex) != LUA_TTABLE) { LuaHelpers::ReportScriptErrorFmt( "ActorMultiVertex::SetVertex: non-table parameter %u supplied inside table of parameters, table expected.", (unsigned int)i ); return; } int pushes = 1; if(DataPieceElements == 2) { pushes += 2; lua_rawgeti(L, DataPieceIndex, 1); float x= FArg(-1); lua_rawgeti(L, DataPieceIndex, 2); float y= FArg(-1); p->SetVertexCoords(VertexIndex, x, y); } else if(DataPieceElements == 3) { pushes += 3; lua_rawgeti(L, DataPieceIndex, 1); float x= FArg(-1); lua_rawgeti(L, DataPieceIndex, 2); float y= FArg(-1); lua_rawgeti(L, DataPieceIndex, 3); float z= FArg(-1); p->SetVertexPos(VertexIndex, x, y, z); } else if(DataPieceElements == 4) { // RageColor pops the things it pushes onto the stack, so we don't need to. RageColor c; // Does not use FromStackCompat because we are not compatible with passing a color in non-table form. c.FromStack(L, DataPieceIndex); p->SetVertexColor(VertexIndex, c); } else { LuaHelpers::ReportScriptErrorFmt( "ActorMultiVertex::SetVertex: Parameter %u has %u elements supplied. 2, 3, or 4 expected.", (unsigned int)i, (unsigned int)DataPieceElements ); } // Avoid a stack underflow by only popping the amount we pushed. lua_pop(L, pushes); } return; } static int SetVertex(T* p, lua_State* L) { // Indices from Lua are one-indexed. -1 to adjust. int Index = IArg(1)-1; if( Index < 0 ) { LuaHelpers::ReportScriptErrorFmt( "ActorMultiVertex::SetVertex: index %d provided, cannot set Index < 1", Index+1 ); return 0; } else if( Index == (int) p->GetNumVertices() ) { p->AddVertices( 1 ); } else if( Index > (int) p->GetNumVertices() ) { LuaHelpers::ReportScriptErrorFmt( "ActorMultiVertex::SetVertex: Cannot set vertex %d if there is no vertex %d, only %u vertices.", Index+1 , Index, (unsigned int)p->GetNumVertices() ); return 0; } SetVertexFromStack(p, L, Index, lua_gettop(L)); return 0; } static int SetVertices(T* p, lua_State* L) { int First = 0; int StackIndex = lua_gettop(L); // Allow the user to just pass a table without specifying a starting point. if(lua_type(L, 1) == LUA_TNUMBER) { // Indices from Lua are one-indexed. -1 to adjust. First = IArg(1)-1; if( First < 0 ) { LuaHelpers::ReportScriptErrorFmt( "ActorMultiVertex::SetVertices: index %d provided, cannot set Index < 1", First+1 ); return 0; } } int Last = First + lua_objlen(L, StackIndex ); if( Last > (int) p->GetNumVertices()) { p->AddVertices( Last - p->GetNumVertices() ); } for(int n = First; n < Last; ++n) { lua_pushnumber(L, n-First+1); lua_gettable(L, StackIndex); SetVertexFromStack(p, L, n, lua_gettop(L)); lua_pop(L, 1); } return 0; } static int SetEffectMode( T* p, lua_State *L ) { EffectMode em = Enum::Check(L, 1); p->SetEffectMode( em ); return 0; } static int SetTextureMode( T* p, lua_State *L ) { TextureMode tm = Enum::Check(L, 1); p->SetTextureMode( tm ); return 0; } static int SetLineWidth( T* p, lua_State *L ) { float Width = FArg(1); if( Width < 0 ) { LuaHelpers::ReportScriptErrorFmt( "ActorMultiVertex::SetLineWidth: cannot set negative width." ); return 0; } p->SetLineWidth(Width); return 0; } static int SetDrawState( T* p, lua_State* L ) { DrawMode dm= p->GetDestDrawMode(); int first= p->GetDestFirstToDraw(); int num= p->GetDestNumToDraw(); int ArgsIndex= 1; if( !lua_istable(L, ArgsIndex) ) { LuaHelpers::ReportScriptErrorFmt( "ActorMultiVertex:SetDrawState: Table expected, something else recieved. Doing nothing."); return 0; } // Fetch the draw mode, if provided. lua_getfield(L, ArgsIndex, "Mode"); if( !lua_isnil(L, -1) ) { dm= Enum::Check(L, -1); } lua_pop(L, 1); // Fetch FirstToDraw, if provided. lua_getfield(L, ArgsIndex, "First"); if( !lua_isnil(L, -1) ) { // Indices from Lua are one-indexed. -1 to adjust. first= IArg(-1)-1; } lua_pop(L, 1); // Fetch NumToDraw, if provided. lua_getfield(L, ArgsIndex, "Num"); if( !lua_isnil(L, -1) ) { num= IArg(-1); } lua_pop(L, 1); p->SetDrawState(dm, first, num); return 0; } static int GetDestDrawMode( T* p, lua_State* L ) { Enum::Push(L, p->GetDestDrawMode()); return 1; } static int GetDestFirstToDraw( T* p, lua_State* L ) { // Indices in Lua are one-indexed. +1 to adjust. lua_pushnumber(L, p->GetDestFirstToDraw()+1); return 1; } static int GetDestNumToDraw( T* p, lua_State* L ) { lua_pushnumber(L, p->GetDestNumToDraw()); return 1; } static int GetCurrDrawMode( T* p, lua_State* L ) { Enum::Push(L, p->GetCurrDrawMode()); return 1; } static int GetCurrFirstToDraw( T* p, lua_State* L ) { // Indices in Lua are one-indexed. +1 to adjust. lua_pushnumber(L, p->GetCurrFirstToDraw()+1); return 1; } static int GetCurrNumToDraw( T* p, lua_State* L ) { lua_pushnumber(L, p->GetCurrNumToDraw()); return 1; } static int LoadTexture( T* p, lua_State *L ) { if( lua_isnil(L, 1) ) { p->UnloadTexture(); } else { RageTextureID ID( SArg(1) ); p->LoadFromTexture( ID ); } return 0; } static int SetTexture( T* p, lua_State *L ) { RageTexture *Texture = Luna::check(L, 1); Texture = TEXTUREMAN->CopyTexture( Texture ); p->SetTexture( Texture ); return 0; } static int GetTexture(T* p, lua_State *L) { RageTexture *texture = p->GetTexture(); if(texture != NULL) { texture->PushSelf(L); } else { lua_pushnil(L); } return 1; } LunaActorMultiVertex() { ADD_METHOD( SetVertex ); ADD_METHOD( SetVertices ); ADD_METHOD( SetEffectMode ); ADD_METHOD( SetTextureMode ); ADD_METHOD( SetLineWidth ); ADD_METHOD( SetDrawState ); ADD_METHOD( SetNumVertices ); ADD_METHOD( GetNumVertices ); ADD_METHOD( GetDestDrawMode ); ADD_METHOD( GetDestFirstToDraw ); ADD_METHOD( GetDestNumToDraw ); ADD_METHOD( GetCurrDrawMode ); ADD_METHOD( GetCurrFirstToDraw ); ADD_METHOD( GetCurrNumToDraw ); // Copy from RageTexture ADD_METHOD( SetTexture ); ADD_METHOD( GetTexture ); // Load from file path ADD_METHOD( LoadTexture ); } }; LUA_REGISTER_DERIVED_CLASS( ActorMultiVertex, Actor ) /* * (c) 2014 Matthew Gardner and 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. */