Files
itgmania212121/src/ActorMultiVertex.cpp
T

716 lines
17 KiB
C++

#include "global.h"
#include <cassert>
#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<ActorMultiVertex>
{
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<EffectMode>(L, 1);
p->SetEffectMode( em );
return 0;
}
static int SetTextureMode( T* p, lua_State *L )
{
TextureMode tm = Enum::Check<TextureMode>(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<DrawMode>(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<RageTexture>::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.
*/