Damage Calc Speedup

02/26/2015 17:18 Mognakor#1
Scheint noch niemand gefunden zu haben oder es stört einfach niemanden.

float CMover::GetDamageMultiplier( ATTACK_INFO* pInfo )
Code:
const int MAX_OVER_ATK = 16;
nDelta = min( nDelta, (MAX_OVER_ATK-1) ); 

const double pi = 3.1415926535;
double radian = ( pi * nDelta ) / (float)(MAX_OVER_ATK * 2);
factor *= (float)cos( radian );
Jedesmal einen cosinus berechnen und dazu noch eine Fließkommamultiplikation und Division ist wirklich unsexy, das geht einfacher, kürzer und effizienter.

Code:
const int MAX_OVER_ATK = 16;
nDelta = min( nDelta, (MAX_OVER_ATK-1) ); 

static const float DeltaFactor[MAX_OVER_ATK]=
{1.0f,
0.995f,0.981f,0.957f,0.924f,0.882f,
0.831f,0.773f,0.707f,0.634f,0.556f,
0.471f,0.383f,0.290f,0.195f,0.098f
};

factor *= DeltaFactor[ nDelta ];
Anstatt unnötigen Berechnungen haben wir nun einen einzigen Array-Zugriff.

Rechenpower haben wir heutzutage zwar genug aber das ist kein Grund sie zu verschwenden.
02/26/2015 22:42 Wanetrain#2
Quote:
Originally Posted by Mognakor View Post
Scheint noch niemand gefunden zu haben oder es stört einfach niemanden.

float CMover::GetDamageMultiplier( ATTACK_INFO* pInfo )
Code:
const int MAX_OVER_ATK = 16;
nDelta = min( nDelta, (MAX_OVER_ATK-1) ); 

const double pi = 3.1415926535;
double radian = ( pi * nDelta ) / (float)(MAX_OVER_ATK * 2);
factor *= (float)cos( radian );
Jedesmal einen cosinus berechnen und dazu noch eine Fließkommamultiplikation und Division ist wirklich unsexy, das geht einfacher, kürzer und effizienter.

Code:
const int MAX_OVER_ATK = 16;
nDelta = min( nDelta, (MAX_OVER_ATK-1) ); 

static const float DeltaFactor[MAX_OVER_ATK]=
{1.0f,
0.995f,0.981f,0.957f,0.924f,0.882f,
0.773f,0.707f,0.634f,0.556f,0.471f,
0.383f,0.290f,0.290f,0.195f,0.098f};

factor *= DeltaFactor[ nDelta ];
Anstatt unnötigen Berechnungen haben wir nun einen einzigen Array-Zugriff.

Rechenpower haben wir heutzutage zwar genug aber das ist kein Grund sie zu verschwenden.
Sehr guter ansatz! Find ich super das jemand mal so etwas macht!

Aber wenn ich dich fragen darf, wieso nicht einfach ein Random von 0.000 bis 1.0? Dürfte eigentlich auch alles für einen machen. Man könnte man aber auch einfach die derzeitige MS nehmen, was aber evtl. Probleme auf sich werfen kann.

MFG.
02/26/2015 23:01 Mognakor#3
Quote:
Originally Posted by Wanetrain View Post
Sehr guter ansatz! Find ich super das jemand mal so etwas macht!

Aber wenn ich dich fragen darf, wieso nicht einfach ein Random von 0.000 bis 1.0? Dürfte eigentlich auch alles für einen machen. Man könnte man aber auch einfach die derzeitige MS nehmen, was aber evtl. Probleme auf sich werfen kann.

MFG.
Weil hier die Schadensverringerung durch den Levelunterschied berechnet wird.

In der Funktion steht:
Code:
int nDelta = pInfo->pDefender->GetLevel() - pInfo->pAttacker->GetLevel();
02/27/2015 19:59 flyffstuff#4
Is this so damage graphic appears faster and doesnt seem delayed?
02/28/2015 00:43 Mognakor#5
Quote:
Originally Posted by flyffstuff View Post
Is this so damage graphic appears faster and doesnt seem delayed?
It speeds up the calculation by replacing expensive calculations by a simple look-up table.
02/28/2015 01:36 lolxdflyx3#6
Quote:
Originally Posted by Mognakor View Post
Quote:
static const float DeltaFactor[MAX_OVER_ATK]=
{1.0f,
0.995f,0.981f,0.957f,0.924f,0.882f,
0.773f,0.707f,0.634f,0.556f,0.471f,
0.383f,0.290f,0.290f,0.195f,0.098f};

...verrechnet?
02/28/2015 01:41 LingRay#7
Here's another version. Values are precalculated on start-up. The advantage is that you can modify the table size or the calculation itself without having to manually recalculate the look-up table.

Code:
const uint32 MAX_OVER_ATK = 16;

template<uint32 Index>
struct MaxOverAtkDelta : MaxOverAtkDelta<Index - 1>{
	static_assert(Index < (MAX_OVER_ATK + 1), "MaxOverAtkDelta<Index> must be < (MAX_OVER_ATK + 1)");

	const float Delta = std::cos((3.1415926535f * (Index - 1)) / (static_cast<float>(MAX_OVER_ATK)* 2.0f));
};

template<>
struct MaxOverAtkDelta<0>
{
};

float deltaFactor(uint32 delta)
{
	static_assert(sizeof(MaxOverAtkDelta<MAX_OVER_ATK>) == MAX_OVER_ATK * sizeof(float), "MaxOverAtkDelta is not aligned correctly. Unable to cast to float*");

	static const MaxOverAtkDelta<MAX_OVER_ATK> factors;
	delta = min(delta, (MAX_OVER_ATK - 1));
	return reinterpret_cast<const float*>(&factors)[delta];
}
02/28/2015 01:53 Mognakor#8
Quote:
Originally Posted by lolxdflyx3 View Post

...verrechnet?
Thx

OP updated.

Quote:
Originally Posted by LingRay View Post
Here's another version. Values are precalculated on start-up. The advantage is that you can modify the table size or the calculation itself without having to manually recalculate the look-up table.

Code:
const uint32 MAX_OVER_ATK = 16;

template<uint32 Index>
struct MaxOverAtkDelta : MaxOverAtkDelta<Index - 1>{
	static_assert(Index < (MAX_OVER_ATK + 1), "MaxOverAtkDelta<Index> must be < (MAX_OVER_ATK + 1)");

	const float Delta = std::cos((3.1415926535f * (Index - 1)) / (static_cast<float>(MAX_OVER_ATK)* 2.0f));
};

template<>
struct MaxOverAtkDelta<0>
{
};

float deltaFactor(uint32 delta)
{
	static_assert(sizeof(MaxOverAtkDelta<MAX_OVER_ATK>) == MAX_OVER_ATK * sizeof(float), "MaxOverAtkDelta is not aligned correctly. Unable to cast to float*");

	static const MaxOverAtkDelta<MAX_OVER_ATK> factors;
	delta = min(delta, (MAX_OVER_ATK - 1));
	return reinterpret_cast<const float*>(&factors)[delta];
}
It's allready late, but it really seems like you are having 1 instance of your struct for MAX_OVER_ATK and then refer it to then de-refer it by the array operator. Further afaik template classes are generated at compile time so the argument in the <> brackets are determined at compile time, thus you would have to insert 0...MAX_OVER_ATK-1 for each instance.

The general idea of generating the table at start-up with a function is ofc. possible but i don't see why we would need a template except for making things far more complicated than needed. If i recall right the newer c++ standards even make compile time generation of look-up tables possible by applying the right keywords to the function.
02/28/2015 03:46 LingRay#9
Quote:
Originally Posted by Mognakor View Post
It's allready late, but it really seems like you are having 1 instance of your struct for MAX_OVER_ATK and then refer it to then de-refer it by the array operator. Further afaik template classes are generated at compile time so the argument in the <> brackets are determined at compile time, thus you would have to insert 0...MAX_OVER_ATK-1 for each instance.
I do not need to do that. MaxOverAtkDelta inherits itself. The MaxOverAtkDelta<0> specialization prevents a recursive loop. Each instance generates its own Delta value with a decreasing Index. Due to the way classes and base classes are stored in memory we can simply cast the root class to a float and by using delta as offset we get the correct value.
This translates to only two lines of assembly:
Code:
mov eax,dword ptr [delta]  
fld dword ptr [eax*4+offset]
Quote:
Originally Posted by Mognakor View Post
The general idea of generating the table at start-up with a function is ofc. possible but i don't see why we would need a template except for making things far more complicated than needed. If i recall right the newer c++ standards even make compile time generation of look-up tables possible by applying the right keywords to the function.
Templates make the code compact and dynamic. If you want to change MAX_OVER_ATK you only need to modify the const. Everything else is done dynamically.
02/28/2015 13:33 Mognakor#10
However you still only have 1 instance of your struct and only specify 2 template arguments (MAX_OVER_ATK and 0). I'm not sure if the other templates will get stored and relying on storage of classes in memory seems dangerous.

There is no need for templates at all, all you need is a array of floats with the right size and a function you call at start-up, for example when the masquerade.prj is read. It has the same functionality is more safe and less confusing. In addition if you want it to be read-only you can create a getter-Function that returns const float* const, the compiler will optimize the call away but the type safety stays.
02/28/2015 17:09 flyffstuff#11
Quote:
Originally Posted by Mognakor View Post
It speeds up the calculation by replacing expensive calculations by a simple look-up table.
Now in English? Why do developers want to make things sound so overcomplicated.. :confused:
02/28/2015 17:24 LingRay#12
Quote:
Originally Posted by Mognakor View Post
However you still only have 1 instance of your struct and only specify 2 template arguments (MAX_OVER_ATK and 0). I'm not sure if the other templates will get stored and relying on storage of classes in memory seems dangerous.
I'm not sure if you understand how templates work correctly. Nor what you actually mean with stored. The
Code:
MaxOverAtkDelta<Index - 1>
will generate base classes until the <0> specialization is reached. That's it.

Quote:
Originally Posted by Mognakor View Post
There is no need for templates at all, all you need is a array of floats with the right size and a function you call at start-up, for example when the masquerade.prj is read. It has the same functionality is more safe and less confusing. In addition if you want it to be read-only you can create a getter-Function that returns const float* const, the compiler will optimize the call away but the type safety stays.
I honestly don't see how a simple template like this is confusing to anyone.
I would not put any read calls into other functions in entirely different files so the code stays dynamic and modular.
02/28/2015 19:11 Mognakor#13
Did you test your code and does it work?

I'm rarely using templates so i'm not completly sure what this line
Code:
struct MaxOverAtkDelta : MaxOverAtkDelta<Index - 1>
will do. Obviously it's some recursive initilization but i don't see how it would generate more objects.

In case it works you still need to put a const float Delta=... into the template with <0>.
02/28/2015 21:17 LingRay#14
Quote:
Originally Posted by Mognakor View Post
Did you test your code and does it work?
Tested with VS2013 Ultimate. Works fine.
Quote:
Originally Posted by Mognakor View Post
I'm rarely using templates so i'm not completly sure what this line
Code:
struct MaxOverAtkDelta : MaxOverAtkDelta<Index - 1>
will do. Obviously it's some recursive initilization but i don't see how it would generate more objects.
Index is decremented by one. Since this changes the original number a new template of MaxOverAtkDelta is created. This is repeated until <0> is reached which is specialized to not have any base class.

Quote:
Originally Posted by Mognakor View Post
In case it works you still need to put a const float Delta=... into the template with <0>.
No. That's not required because Index starts at MAX_OVER_ATK. As you can see I use (Index - 1) for the calculation of Delta. If I'd put the same code into the <0> specialization I would have a negative value.
03/08/2015 07:39 xMootie#15
Talk about micro optimization...