1
0
forked from 0ad/0ad

Switch everything to 15.16-bit precision fixeds, to allow more accurate unit vectors and angles.

This was SVN commit r7497.
This commit is contained in:
Ykkrosh 2010-05-02 20:32:37 +00:00
parent ab45b4c787
commit f33706bf8b
48 changed files with 479 additions and 263 deletions

View File

@ -848,20 +848,18 @@ int CXMLReader::ReadEntities(XMBElement parent, double end_time)
{ {
XMBAttributeList attrs = setting.GetAttributes(); XMBAttributeList attrs = setting.GetAttributes();
Position = CFixedVector3D( Position = CFixedVector3D(
CFixed_23_8::FromFloat(CStr(attrs.GetNamedItem(at_x)).ToFloat()), fixed::FromString(CStr(attrs.GetNamedItem(at_x))),
CFixed_23_8::FromFloat(CStr(attrs.GetNamedItem(at_y)).ToFloat()), fixed::FromString(CStr(attrs.GetNamedItem(at_y))),
CFixed_23_8::FromFloat(CStr(attrs.GetNamedItem(at_z)).ToFloat())); fixed::FromString(CStr(attrs.GetNamedItem(at_z))));
// TODO: shouldn't use floats here
} }
// <orientation> // <orientation>
else if (element_name == el_orientation) else if (element_name == el_orientation)
{ {
XMBAttributeList attrs = setting.GetAttributes(); XMBAttributeList attrs = setting.GetAttributes();
Orientation = CFixedVector3D( Orientation = CFixedVector3D(
CFixed_23_8::FromFloat(CStr(attrs.GetNamedItem(at_x)).ToFloat()), fixed::FromString(CStr(attrs.GetNamedItem(at_x))),
CFixed_23_8::FromFloat(CStr(attrs.GetNamedItem(at_y)).ToFloat()), fixed::FromString(CStr(attrs.GetNamedItem(at_y))),
CFixed_23_8::FromFloat(CStr(attrs.GetNamedItem(at_z)).ToFloat())); fixed::FromString(CStr(attrs.GetNamedItem(at_z))));
// TODO: shouldn't use floats here
// TODO: what happens if some attributes are missing? // TODO: what happens if some attributes are missing?
} }
else else
@ -1008,7 +1006,7 @@ int CXMLReader::ReadOldEntities(XMBElement parent, double end_time)
entity_pos_t x = entity_pos_t::FromFloat(Position.X); entity_pos_t x = entity_pos_t::FromFloat(Position.X);
entity_pos_t z = entity_pos_t::FromFloat(Position.Z); entity_pos_t z = entity_pos_t::FromFloat(Position.Z);
cmpPos->JumpTo(x, z); cmpPos->JumpTo(x, z);
cmpPos->SetYRotation(CFixed_23_8::FromFloat(Orientation)); cmpPos->SetYRotation(entity_angle_t::FromFloat(Orientation));
} }
CmpPtr<ICmpOwnership> cmpOwner(*m_MapReader.pSimulation2, ent); CmpPtr<ICmpOwnership> cmpOwner(*m_MapReader.pSimulation2, ent);
@ -1099,7 +1097,7 @@ int CXMLReader::ReadNonEntities(XMBElement parent, double end_time)
entity_pos_t x = entity_pos_t::FromFloat(Position.X); // TODO: these should all be parsed as fixeds probably entity_pos_t x = entity_pos_t::FromFloat(Position.X); // TODO: these should all be parsed as fixeds probably
entity_pos_t z = entity_pos_t::FromFloat(Position.Z); entity_pos_t z = entity_pos_t::FromFloat(Position.Z);
cmpPos->JumpTo(x, z); cmpPos->JumpTo(x, z);
cmpPos->SetYRotation(CFixed_23_8::FromFloat(Orientation)); cmpPos->SetYRotation(entity_angle_t::FromFloat(Orientation));
} }
} }
} }

View File

@ -151,9 +151,9 @@ void CTerrain::CalcPositionFixed(ssize_t i, ssize_t j, CFixedVector3D& pos) cons
height = m_Heightmap[j*m_MapSize + i]; height = m_Heightmap[j*m_MapSize + i];
else else
height = 0; height = 0;
pos.X = CFixed_23_8::FromInt(i)*(int)CELL_SIZE; pos.X = fixed::FromInt(i) * (int)CELL_SIZE;
pos.Y = CFixed_23_8::FromInt(height)/(int)HEIGHT_UNITS_PER_METRE; pos.Y = fixed::FromInt(height) / (int)HEIGHT_UNITS_PER_METRE;
pos.Z = CFixed_23_8::FromInt(j)*(int)CELL_SIZE; pos.Z = fixed::FromInt(j) * (int)CELL_SIZE;
} }
@ -341,24 +341,30 @@ float CTerrain::GetExactGroundLevel(float x, float z) const
+ zf * ((1 - xf) * h01 + xf * h11))); + zf * ((1 - xf) * h01 + xf * h11)));
} }
CFixed_23_8 CTerrain::GetExactGroundLevelFixed(CFixed_23_8 x, CFixed_23_8 z) const fixed CTerrain::GetExactGroundLevelFixed(fixed x, fixed z) const
{ {
// Clamp to size-2 so we can use the tiles (xi,zi)-(xi+1,zi+1) // Clamp to size-2 so we can use the tiles (xi,zi)-(xi+1,zi+1)
const ssize_t xi = clamp((ssize_t)(x / (int)CELL_SIZE).ToInt_RoundToZero(), (ssize_t)0, m_MapSize-2); const ssize_t xi = clamp((ssize_t)(x / (int)CELL_SIZE).ToInt_RoundToZero(), (ssize_t)0, m_MapSize-2);
const ssize_t zi = clamp((ssize_t)(z / (int)CELL_SIZE).ToInt_RoundToZero(), (ssize_t)0, m_MapSize-2); const ssize_t zi = clamp((ssize_t)(z / (int)CELL_SIZE).ToInt_RoundToZero(), (ssize_t)0, m_MapSize-2);
const CFixed_23_8 one = CFixed_23_8::FromInt(1); const fixed one = fixed::FromInt(1);
const CFixed_23_8 xf = clamp((x / (int)CELL_SIZE) - CFixed_23_8::FromInt(xi), CFixed_23_8::FromInt(0), one); const fixed xf = clamp((x / (int)CELL_SIZE) - fixed::FromInt(xi), fixed::Zero(), one);
const CFixed_23_8 zf = clamp((z / (int)CELL_SIZE) - CFixed_23_8::FromInt(zi), CFixed_23_8::FromInt(0), one); const fixed zf = clamp((z / (int)CELL_SIZE) - fixed::FromInt(zi), fixed::Zero(), one);
u16 h00 = m_Heightmap[zi*m_MapSize + xi]; u16 h00 = m_Heightmap[zi*m_MapSize + xi];
u16 h01 = m_Heightmap[(zi+1)*m_MapSize + xi]; u16 h01 = m_Heightmap[(zi+1)*m_MapSize + xi];
u16 h10 = m_Heightmap[zi*m_MapSize + (xi+1)]; u16 h10 = m_Heightmap[zi*m_MapSize + (xi+1)];
u16 h11 = m_Heightmap[(zi+1)*m_MapSize + (xi+1)]; u16 h11 = m_Heightmap[(zi+1)*m_MapSize + (xi+1)];
// Intermediate scaling of xf, so we don't overflow in the multiplications below
// (h00 <= 65535, xf <= 1, max fixed is < 32768; divide by 2 here so xf1*h00 <= 32767.5)
const fixed xf0 = xf / 2;
const fixed xf1 = (one - xf) / 2;
// Linearly interpolate // Linearly interpolate
return ((one - zf).Multiply((one - xf) * h00 + xf * h10) return ((one - zf).Multiply(xf1 * h00 + xf0 * h10)
+ zf.Multiply((one - xf) * h01 + xf * h11)) / (int)HEIGHT_UNITS_PER_METRE; + zf.Multiply(xf1 * h01 + xf0 * h11)) / (int)(HEIGHT_UNITS_PER_METRE / 2);
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////

View File

@ -40,7 +40,7 @@ class CFixedVector3D;
const ssize_t CELL_SIZE = 4; const ssize_t CELL_SIZE = 4;
/// number of u16 height units per metre /// number of u16 height units per metre
const ssize_t HEIGHT_UNITS_PER_METRE = 731; // == int(256.0f/0.35f) const ssize_t HEIGHT_UNITS_PER_METRE = 732; // == approx int(256.0f/0.35f)
/// metres per u16 height unit /// metres per u16 height unit
const float HEIGHT_SCALE = 1.f / HEIGHT_UNITS_PER_METRE; const float HEIGHT_SCALE = 1.f / HEIGHT_UNITS_PER_METRE;
@ -80,7 +80,7 @@ public:
float GetVertexGroundLevel(ssize_t i, ssize_t j) const; float GetVertexGroundLevel(ssize_t i, ssize_t j) const;
float GetExactGroundLevel(float x, float z) const; float GetExactGroundLevel(float x, float z) const;
CFixed_23_8 GetExactGroundLevelFixed(CFixed_23_8 x, CFixed_23_8 z) const; fixed GetExactGroundLevelFixed(fixed x, fixed z) const;
float GetExactGroundLevel(const CVector2D& v) const; float GetExactGroundLevel(const CVector2D& v) const;
float GetSlope(float x, float z) const ; float GetSlope(float x, float z) const ;

View File

@ -100,25 +100,49 @@ public:
Set45Slope(terrain); Set45Slope(terrain);
SetHighPlateau(terrain, 20); SetHighPlateau(terrain, 20);
CFixed_23_8 ground; const double maxDelta = 0.0001;
ground = terrain.GetExactGroundLevelFixed(CFixed_23_8::FromFloat(0.f), CFixed_23_8::FromFloat(1.5f*CELL_SIZE)); fixed ground;
TS_ASSERT_DELTA(ground.ToFloat(), 100.f/HEIGHT_UNITS_PER_METRE, 0.01f);
ground = terrain.GetExactGroundLevelFixed(CFixed_23_8::FromFloat(0.5f*CELL_SIZE), CFixed_23_8::FromFloat(1.5f*CELL_SIZE)); ground = terrain.GetExactGroundLevelFixed(fixed::FromFloat(0.f), fixed::FromFloat(1.5f*CELL_SIZE));
TS_ASSERT_DELTA(ground.ToFloat(), 100.f/HEIGHT_UNITS_PER_METRE+0.5f*CELL_SIZE, 0.01f); TS_ASSERT_DELTA(ground.ToDouble(), 100.0/HEIGHT_UNITS_PER_METRE, maxDelta);
ground = terrain.GetExactGroundLevelFixed(CFixed_23_8::FromFloat(1.5f*CELL_SIZE), CFixed_23_8::FromFloat(1.5f*CELL_SIZE)); ground = terrain.GetExactGroundLevelFixed(fixed::FromFloat(0.5f*CELL_SIZE), fixed::FromFloat(1.5f*CELL_SIZE));
TS_ASSERT_DELTA(ground.ToFloat(), 100.f/HEIGHT_UNITS_PER_METRE+1.5f*CELL_SIZE, 0.01f); TS_ASSERT_DELTA(ground.ToDouble(), 100.0/HEIGHT_UNITS_PER_METRE+0.5*CELL_SIZE, maxDelta);
ground = terrain.GetExactGroundLevelFixed(CFixed_23_8::FromFloat(2.5f*CELL_SIZE), CFixed_23_8::FromFloat(1.5f*CELL_SIZE)); ground = terrain.GetExactGroundLevelFixed(fixed::FromFloat(1.5f*CELL_SIZE), fixed::FromFloat(1.5f*CELL_SIZE));
TS_ASSERT_DELTA(ground.ToFloat(), 100.f/HEIGHT_UNITS_PER_METRE+2.f*CELL_SIZE, 0.01f); TS_ASSERT_DELTA(ground.ToDouble(), 100.0/HEIGHT_UNITS_PER_METRE+1.5*CELL_SIZE, maxDelta);
ground = terrain.GetExactGroundLevelFixed(CFixed_23_8::FromFloat(3.5f*CELL_SIZE), CFixed_23_8::FromFloat(1.5f*CELL_SIZE)); ground = terrain.GetExactGroundLevelFixed(fixed::FromFloat(2.5f*CELL_SIZE), fixed::FromFloat(1.5f*CELL_SIZE));
TS_ASSERT_DELTA(ground.ToFloat(), 100.f/HEIGHT_UNITS_PER_METRE+11.f*CELL_SIZE, 0.01f); TS_ASSERT_DELTA(ground.ToDouble(), 100.0/HEIGHT_UNITS_PER_METRE+2.0*CELL_SIZE, maxDelta);
ground = terrain.GetExactGroundLevelFixed(CFixed_23_8::FromFloat(4.5f*CELL_SIZE), CFixed_23_8::FromFloat(1.5f*CELL_SIZE)); ground = terrain.GetExactGroundLevelFixed(fixed::FromFloat(3.5f*CELL_SIZE), fixed::FromFloat(1.5f*CELL_SIZE));
TS_ASSERT_DELTA(ground.ToFloat(), 100.f/HEIGHT_UNITS_PER_METRE+20.f*CELL_SIZE, 0.01f); TS_ASSERT_DELTA(ground.ToDouble(), 100.0/HEIGHT_UNITS_PER_METRE+11.0*CELL_SIZE, maxDelta);
ground = terrain.GetExactGroundLevelFixed(fixed::FromFloat(4.5f*CELL_SIZE), fixed::FromFloat(1.5f*CELL_SIZE));
TS_ASSERT_DELTA(ground.ToDouble(), 100.0/HEIGHT_UNITS_PER_METRE+20.0*CELL_SIZE, maxDelta);
}
void test_GetExactGroundLevelFixed_max()
{
CTerrain terrain;
terrain.Initialize(4, NULL);
SetVertex(terrain, 0, 0, 65535);
SetVertex(terrain, 0, 1, 65535);
SetVertex(terrain, 1, 0, 65535);
SetVertex(terrain, 1, 1, 65535);
const double maxDelta = 0.003;
int p = 255;
for (int zi = 0; zi < p; ++zi)
{
for (int xi = 0; xi < p; ++xi)
{
fixed ground = terrain.GetExactGroundLevelFixed(fixed::FromFloat(xi/(float)p*CELL_SIZE), fixed::FromFloat(zi/(float)p*CELL_SIZE));
TS_ASSERT_DELTA(ground.ToDouble(), 65535.0/HEIGHT_UNITS_PER_METRE, maxDelta);
}
}
} }
void test_CalcNormal() void test_CalcNormal()

View File

@ -172,7 +172,7 @@ std::vector<entity_id_t> PickFriendlyEntitiesInRect(void* UNUSED(cbdata), int x0
CFixedVector3D GetTerrainAtPoint(void* UNUSED(cbdata), int x, int y) CFixedVector3D GetTerrainAtPoint(void* UNUSED(cbdata), int x, int y)
{ {
CVector3D pos = g_Game->GetView()->GetCamera()->GetWorldCoordinates(x, y, false); CVector3D pos = g_Game->GetView()->GetCamera()->GetWorldCoordinates(x, y, false);
return CFixedVector3D(CFixed_23_8::FromFloat(pos.X), CFixed_23_8::FromFloat(pos.Y), CFixed_23_8::FromFloat(pos.Z)); return CFixedVector3D(fixed::FromFloat(pos.X), fixed::FromFloat(pos.Y), fixed::FromFloat(pos.Z));
} }
std::wstring SetCursor(void* UNUSED(cbdata), std::wstring name) std::wstring SetCursor(void* UNUSED(cbdata), std::wstring name)

View File

@ -19,32 +19,46 @@
#include "Fixed.h" #include "Fixed.h"
// Based on http://www.dspguru.com/dsp/tricks/fixed-point-atan2-with-self-normalization #include "ps/CStr.h"
CFixed_23_8 atan2_approx(CFixed_23_8 y, CFixed_23_8 x)
template<>
CFixed_15_16 CFixed_15_16::FromString(const CStr8& s)
{ {
CFixed_23_8 zero; return FromDouble(s.ToDouble()); // TODO: shouldn't use floats here
}
template<>
CFixed_15_16 CFixed_15_16::FromString(const CStrW& s)
{
return FromDouble(s.ToDouble()); // TODO: shouldn't use floats here
}
// Based on http://www.dspguru.com/dsp/tricks/fixed-point-atan2-with-self-normalization
CFixed_15_16 atan2_approx(CFixed_15_16 y, CFixed_15_16 x)
{
CFixed_15_16 zero;
// Special case to avoid division-by-zero // Special case to avoid division-by-zero
if (x.IsZero() && y.IsZero()) if (x.IsZero() && y.IsZero())
return zero; return zero;
CFixed_23_8 c1; CFixed_15_16 c1;
c1.SetInternalValue(201); // pi/4 << 8 c1.SetInternalValue(51472); // pi/4 << 16
CFixed_23_8 c2; CFixed_15_16 c2;
c2.SetInternalValue(603); // 3*pi/4 << 8 c2.SetInternalValue(154415); // 3*pi/4 << 16
CFixed_23_8 abs_y = y.Absolute(); CFixed_15_16 abs_y = y.Absolute();
CFixed_23_8 angle; CFixed_15_16 angle;
if (x >= zero) if (x >= zero)
{ {
CFixed_23_8 r = (x - abs_y) / (x + abs_y); CFixed_15_16 r = (x - abs_y) / (x + abs_y);
angle = c1 - c1.Multiply(r); angle = c1 - c1.Multiply(r);
} }
else else
{ {
CFixed_23_8 r = (x + abs_y) / (abs_y - x); CFixed_15_16 r = (x + abs_y) / (abs_y - x);
angle = c2 - c1.Multiply(r); angle = c2 - c1.Multiply(r);
} }
@ -55,17 +69,15 @@ CFixed_23_8 atan2_approx(CFixed_23_8 y, CFixed_23_8 x)
} }
template<> template<>
CFixed_23_8 CFixed_23_8::Pi() CFixed_15_16 CFixed_15_16::Pi()
{ {
return CFixed_23_8(804); // = pi * 256 return CFixed_15_16(205887); // = pi << 16
} }
void sincos_approx(CFixed_23_8 a, CFixed_23_8& sin_out, CFixed_23_8& cos_out) void sincos_approx(CFixed_15_16 a, CFixed_15_16& sin_out, CFixed_15_16& cos_out)
{ {
// XXX: mustn't use floating-point here - need a fixed-point emulation // XXX: mustn't use floating-point here - need a fixed-point emulation
// TODO: it's stupid doing sin/cos with 8-bit precision - we ought to have a CFixed_16_16 instead sin_out = CFixed_15_16::FromDouble(sin(a.ToDouble()));
cos_out = CFixed_15_16::FromDouble(cos(a.ToDouble()));
sin_out = CFixed_23_8::FromDouble(sin(a.ToDouble()));
cos_out = CFixed_23_8::FromDouble(cos(a.ToDouble()));
} }

View File

@ -21,6 +21,9 @@
#include "lib/types.h" #include "lib/types.h"
#include "maths/Sqrt.h" #include "maths/Sqrt.h"
class CStr8;
class CStrW;
#ifndef NDEBUG #ifndef NDEBUG
#define USE_FIXED_OVERFLOW_CHECKS #define USE_FIXED_OVERFLOW_CHECKS
#endif // NDEBUG #endif // NDEBUG
@ -90,12 +93,9 @@ inline T round_away_from_zero(float value)
} }
/** /**
* A simple fixed-point number class, with no fancy features * A simple fixed-point number class.
* like overflow checking or anything. (It has very few basic
* features too, and needs to be substantially improved before
* it'll be of much use.)
* *
* Use CFixed_23_8 rather than using this class directly. * Use 'fixed' rather than using this class directly.
*/ */
template<typename T, T max_t, int total_bits, int int_bits, int fract_bits_, int fract_pow2> template<typename T, T max_t, int total_bits, int int_bits, int fract_bits_, int fract_pow2>
class CFixed class CFixed
@ -122,6 +122,7 @@ public:
{ {
return CFixed(n << fract_bits); return CFixed(n << fract_bits);
} }
static CFixed FromFloat(float n) static CFixed FromFloat(float n)
{ {
if (!isfinite(n)) if (!isfinite(n))
@ -129,6 +130,7 @@ public:
float scaled = n * fract_pow2; float scaled = n * fract_pow2;
return CFixed(round_away_from_zero<T>(scaled)); return CFixed(round_away_from_zero<T>(scaled));
} }
static CFixed FromDouble(double n) static CFixed FromDouble(double n)
{ {
if (!isfinite(n)) if (!isfinite(n))
@ -137,14 +139,19 @@ public:
return CFixed(round_away_from_zero<T>(scaled)); return CFixed(round_away_from_zero<T>(scaled));
} }
static CFixed FromString(const CStr8& s);
static CFixed FromString(const CStrW& s);
float ToFloat() const float ToFloat() const
{ {
return value / (float)fract_pow2; return value / (float)fract_pow2;
} }
double ToDouble() const double ToDouble() const
{ {
return value / (double)fract_pow2; return value / (double)fract_pow2;
} }
int ToInt_RoundToZero() const int ToInt_RoundToZero() const
{ {
if (value > 0) if (value > 0)
@ -220,7 +227,7 @@ public:
return CFixed(value * n); return CFixed(value * n);
} }
/// Divide by an integer. Must not have n == 0. Cannot overflow. /// Divide by an integer. Must not have n == 0. Cannot overflow unless n == -1.
CFixed operator/(int n) const CFixed operator/(int n) const
{ {
CheckDivisionOverflow(T, value, n, L"Overflow in CFixed::operator/(int n)") CheckDivisionOverflow(T, value, n, L"Overflow in CFixed::operator/(int n)")
@ -242,6 +249,16 @@ public:
return CFixed((T)t); return CFixed((T)t);
} }
/**
* Compute this*m/d. Must not have d == 0. Won't overflow if the result can be represented as a CFixed.
*/
CFixed MulDiv(CFixed m, CFixed d) const
{
i64 t = ((i64)value * (i64)m.value) / (i64)d.value;
CheckCastOverflow(t, T, L"Overflow in CFixed::Multiply(CFixed n)", L"Underflow in CFixed::Multiply(CFixed n)")
return CFixed((T)t);
}
CFixed Sqrt() const CFixed Sqrt() const
{ {
if (value <= 0) if (value <= 0)
@ -257,16 +274,24 @@ private:
}; };
/** /**
* A fixed-point number class with 1-bit sign, 23-bit integral part, 8-bit fractional part. * A fixed-point number class with 1-bit sign, 15-bit integral part, 16-bit fractional part.
*/ */
typedef CFixed<i32, (i32)0x7fffffff, 32, 23, 8, 256> CFixed_23_8; typedef CFixed<i32, (i32)0x7fffffff, 32, 15, 16, 65536> CFixed_15_16;
/**
* Default fixed-point type used by the engine.
*/
typedef CFixed_15_16 fixed;
/** /**
* Inaccurate approximation of atan2 over fixed-point numbers. * Inaccurate approximation of atan2 over fixed-point numbers.
* Maximum error is almost 0.08 radians (4.5 degrees). * Maximum error is almost 0.08 radians (4.5 degrees).
*/ */
CFixed_23_8 atan2_approx(CFixed_23_8 y, CFixed_23_8 x); CFixed_15_16 atan2_approx(CFixed_15_16 y, CFixed_15_16 x);
void sincos_approx(CFixed_23_8 a, CFixed_23_8& sin_out, CFixed_23_8& cos_out); /**
* Compute sin(a) and cos(a).
*/
void sincos_approx(CFixed_15_16 a, CFixed_15_16& sin_out, CFixed_15_16& cos_out);
#endif // INCLUDED_FIXED #endif // INCLUDED_FIXED

View File

@ -23,9 +23,6 @@
class CFixedVector2D class CFixedVector2D
{ {
private:
typedef CFixed_23_8 fixed;
public: public:
fixed X, Y; fixed X, Y;
@ -122,8 +119,6 @@ public:
/** /**
* Normalize the vector so that length is close to 1. * Normalize the vector so that length is close to 1.
* If length is 0, does nothing. * If length is 0, does nothing.
* WARNING: The fixed-point numbers only have 8-bit fractional parts, so
* a normalized vector will be very imprecise.
*/ */
void Normalize() void Normalize()
{ {
@ -141,19 +136,11 @@ public:
*/ */
void Normalize(fixed n) void Normalize(fixed n)
{ {
if (n.IsZero())
{
X = Y = fixed::FromInt(0);
return;
}
fixed l = Length(); fixed l = Length();
// TODO: work out whether this is giving decent precision if (!l.IsZero())
fixed d = l / n;
if (!d.IsZero())
{ {
X = X / d; X = X.MulDiv(n, l);
Y = Y / d; Y = Y.MulDiv(n, l);
} }
} }

View File

@ -23,9 +23,6 @@
class CFixedVector3D class CFixedVector3D
{ {
private:
typedef CFixed_23_8 fixed;
public: public:
fixed X, Y, Z; fixed X, Y, Z;
@ -39,6 +36,12 @@ public:
return (X == v.X && Y == v.Y && Z == v.Z); return (X == v.X && Y == v.Y && Z == v.Z);
} }
/// Vector inequality
bool operator!=(const CFixedVector3D& v) const
{
return (X != v.X || Y != v.Y || Z != v.Z);
}
/// Vector addition /// Vector addition
CFixedVector3D operator+(const CFixedVector3D& v) const CFixedVector3D operator+(const CFixedVector3D& v) const
{ {
@ -102,8 +105,6 @@ public:
/** /**
* Normalize the vector so that length is close to 1. * Normalize the vector so that length is close to 1.
* If length is 0, does nothing. * If length is 0, does nothing.
* WARNING: The fixed-point numbers only have 8-bit fractional parts, so
* a normalized vector will be very imprecise.
*/ */
void Normalize() void Normalize()
{ {
@ -122,20 +123,12 @@ public:
*/ */
void Normalize(fixed n) void Normalize(fixed n)
{ {
if (n.IsZero())
{
X = Y = Z = fixed::FromInt(0);
return;
}
fixed l = Length(); fixed l = Length();
// TODO: work out whether this is giving decent precision if (!l.IsZero())
fixed d = l / n;
if (!d.IsZero())
{ {
X = X / d; X = X.MulDiv(n, l);
Y = Y / d; Y = Y.MulDiv(n, l);
Z = Z / d; Z = Z.MulDiv(n, l);
} }
} }

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games. /* Copyright (C) 2010 Wildfire Games.
* This file is part of 0 A.D. * This file is part of 0 A.D.
* *
* 0 A.D. is free software: you can redistribute it and/or modify * 0 A.D. is free software: you can redistribute it and/or modify
@ -25,18 +25,18 @@ class TestFixed : public CxxTest::TestSuite
public: public:
void test_basic() void test_basic()
{ {
CFixed_23_8 a = CFixed_23_8::FromInt(1234567); fixed a = fixed::FromInt(12345);
TS_ASSERT_EQUALS((a + a).ToDouble(), 2469134); TS_ASSERT_EQUALS((a + a).ToDouble(), 24690);
TS_ASSERT_EQUALS((a - a).ToDouble(), 0); TS_ASSERT_EQUALS((a - a).ToDouble(), 0);
TS_ASSERT_EQUALS((-a).ToDouble(), -1234567); TS_ASSERT_EQUALS((-a).ToDouble(), -12345);
TS_ASSERT_EQUALS((a / a).ToDouble(), 1); TS_ASSERT_EQUALS((a / a).ToDouble(), 1);
TS_ASSERT_EQUALS((a / 127).ToDouble(), 9721); TS_ASSERT_EQUALS((a / 823).ToDouble(), 15);
TS_ASSERT_EQUALS((a * 2).ToDouble(), 2469134); TS_ASSERT_EQUALS((a * 2).ToDouble(), 24690);
} }
void test_FromInt() void test_FromInt()
{ {
CFixed_23_8 a = CFixed_23_8::FromInt(123); fixed a = fixed::FromInt(123);
TS_ASSERT_EQUALS(a.ToFloat(), 123.0f); TS_ASSERT_EQUALS(a.ToFloat(), 123.0f);
TS_ASSERT_EQUALS(a.ToDouble(), 123.0); TS_ASSERT_EQUALS(a.ToDouble(), 123.0);
TS_ASSERT_EQUALS(a.ToInt_RoundToZero(), 123); TS_ASSERT_EQUALS(a.ToInt_RoundToZero(), 123);
@ -44,97 +44,95 @@ public:
void test_FromFloat() void test_FromFloat()
{ {
CFixed_23_8 a = CFixed_23_8::FromFloat(123.125f); fixed a = fixed::FromFloat(123.125f);
TS_ASSERT_EQUALS(a.ToFloat(), 123.125f); TS_ASSERT_EQUALS(a.ToFloat(), 123.125f);
TS_ASSERT_EQUALS(a.ToDouble(), 123.125); TS_ASSERT_EQUALS(a.ToDouble(), 123.125);
CFixed_23_8 b = CFixed_23_8::FromFloat(-123.125f); fixed b = fixed::FromFloat(-123.125f);
TS_ASSERT_EQUALS(b.ToFloat(), -123.125f); TS_ASSERT_EQUALS(b.ToFloat(), -123.125f);
TS_ASSERT_EQUALS(b.ToDouble(), -123.125); TS_ASSERT_EQUALS(b.ToDouble(), -123.125);
CFixed_23_8 c = CFixed_23_8::FromFloat(INFINITY); fixed c = fixed::FromFloat(INFINITY);
TS_ASSERT_EQUALS(c.GetInternalValue(), (i32)0); TS_ASSERT_EQUALS(c.GetInternalValue(), (i32)0);
CFixed_23_8 d = CFixed_23_8::FromFloat(-INFINITY); fixed d = fixed::FromFloat(-INFINITY);
TS_ASSERT_EQUALS(d.GetInternalValue(), (i32)0); TS_ASSERT_EQUALS(d.GetInternalValue(), (i32)0);
CFixed_23_8 e = CFixed_23_8::FromFloat(NAN); fixed e = fixed::FromFloat(NAN);
TS_ASSERT_EQUALS(e.GetInternalValue(), (i32)0); TS_ASSERT_EQUALS(e.GetInternalValue(), (i32)0);
} }
void test_FromDouble() void test_FromDouble()
{ {
CFixed_23_8 a = CFixed_23_8::FromDouble(123.125); fixed a = fixed::FromDouble(123.125);
TS_ASSERT_EQUALS(a.ToFloat(), 123.125f); TS_ASSERT_EQUALS(a.ToFloat(), 123.125f);
TS_ASSERT_EQUALS(a.ToDouble(), 123.125); TS_ASSERT_EQUALS(a.ToDouble(), 123.125);
CFixed_23_8 b = CFixed_23_8::FromDouble(-123.125); fixed b = fixed::FromDouble(-123.125);
TS_ASSERT_EQUALS(b.ToFloat(), -123.125f); TS_ASSERT_EQUALS(b.ToFloat(), -123.125f);
TS_ASSERT_EQUALS(b.ToDouble(), -123.125); TS_ASSERT_EQUALS(b.ToDouble(), -123.125);
CFixed_23_8 c = CFixed_23_8::FromDouble(INFINITY); fixed c = fixed::FromDouble(INFINITY);
TS_ASSERT_EQUALS(c.GetInternalValue(), (i32)0); TS_ASSERT_EQUALS(c.GetInternalValue(), (i32)0);
CFixed_23_8 d = CFixed_23_8::FromDouble(-INFINITY); fixed d = fixed::FromDouble(-INFINITY);
TS_ASSERT_EQUALS(d.GetInternalValue(), (i32)0); TS_ASSERT_EQUALS(d.GetInternalValue(), (i32)0);
CFixed_23_8 e = CFixed_23_8::FromDouble(NAN); fixed e = fixed::FromDouble(NAN);
TS_ASSERT_EQUALS(e.GetInternalValue(), (i32)0); TS_ASSERT_EQUALS(e.GetInternalValue(), (i32)0);
} }
void test_FromFloat_Rounding() void test_FromFloat_Rounding()
{ {
double eps = pow(2.0, -8.0); double eps = pow(2.0, -16.0);
TS_ASSERT_EQUALS(CFixed_23_8::FromFloat(eps).ToDouble(), eps); TS_ASSERT_EQUALS(fixed::FromFloat(eps).ToDouble(), eps);
TS_ASSERT_EQUALS(CFixed_23_8::FromFloat(eps * 0.5625).ToDouble(), eps); TS_ASSERT_EQUALS(fixed::FromFloat(eps * 0.5625).ToDouble(), eps);
TS_ASSERT_EQUALS(CFixed_23_8::FromFloat(eps * 0.5).ToDouble(), eps); TS_ASSERT_EQUALS(fixed::FromFloat(eps * 0.5).ToDouble(), eps);
TS_ASSERT_EQUALS(CFixed_23_8::FromFloat(eps * 0.4375).ToDouble(), 0.0); TS_ASSERT_EQUALS(fixed::FromFloat(eps * 0.4375).ToDouble(), 0.0);
TS_ASSERT_EQUALS(CFixed_23_8::FromFloat(-eps).ToDouble(), -eps); TS_ASSERT_EQUALS(fixed::FromFloat(-eps).ToDouble(), -eps);
TS_ASSERT_EQUALS(CFixed_23_8::FromFloat(-eps * 0.5625).ToDouble(), -eps); TS_ASSERT_EQUALS(fixed::FromFloat(-eps * 0.5625).ToDouble(), -eps);
TS_ASSERT_EQUALS(CFixed_23_8::FromFloat(-eps * 0.5).ToDouble(), -eps); TS_ASSERT_EQUALS(fixed::FromFloat(-eps * 0.5).ToDouble(), -eps);
TS_ASSERT_EQUALS(CFixed_23_8::FromFloat(-eps * 0.4375).ToDouble(), 0.0); TS_ASSERT_EQUALS(fixed::FromFloat(-eps * 0.4375).ToDouble(), 0.0);
} }
void test_RoundToZero() void test_RoundToZero()
{ {
TS_ASSERT_EQUALS(CFixed_23_8::FromFloat(10.f).ToInt_RoundToZero(), 10); TS_ASSERT_EQUALS(fixed::FromFloat(10.f).ToInt_RoundToZero(), 10);
TS_ASSERT_EQUALS(CFixed_23_8::FromFloat(10.1f).ToInt_RoundToZero(), 10); TS_ASSERT_EQUALS(fixed::FromFloat(10.1f).ToInt_RoundToZero(), 10);
TS_ASSERT_EQUALS(CFixed_23_8::FromFloat(10.5f).ToInt_RoundToZero(), 10); TS_ASSERT_EQUALS(fixed::FromFloat(10.5f).ToInt_RoundToZero(), 10);
TS_ASSERT_EQUALS(CFixed_23_8::FromFloat(10.99f).ToInt_RoundToZero(), 10); TS_ASSERT_EQUALS(fixed::FromFloat(10.99f).ToInt_RoundToZero(), 10);
TS_ASSERT_EQUALS(CFixed_23_8::FromFloat(0.1f).ToInt_RoundToZero(), 0); TS_ASSERT_EQUALS(fixed::FromFloat(0.1f).ToInt_RoundToZero(), 0);
TS_ASSERT_EQUALS(CFixed_23_8::FromFloat(0.0f).ToInt_RoundToZero(), 0); TS_ASSERT_EQUALS(fixed::FromFloat(0.0f).ToInt_RoundToZero(), 0);
TS_ASSERT_EQUALS(CFixed_23_8::FromFloat(-0.1f).ToInt_RoundToZero(), 0); TS_ASSERT_EQUALS(fixed::FromFloat(-0.1f).ToInt_RoundToZero(), 0);
TS_ASSERT_EQUALS(CFixed_23_8::FromFloat(-0.99f).ToInt_RoundToZero(), 0); TS_ASSERT_EQUALS(fixed::FromFloat(-0.99f).ToInt_RoundToZero(), 0);
TS_ASSERT_EQUALS(CFixed_23_8::FromFloat(-1.0f).ToInt_RoundToZero(), -1); TS_ASSERT_EQUALS(fixed::FromFloat(-1.0f).ToInt_RoundToZero(), -1);
TS_ASSERT_EQUALS(CFixed_23_8::FromFloat(-2.0f).ToInt_RoundToZero(), -2); TS_ASSERT_EQUALS(fixed::FromFloat(-2.0f).ToInt_RoundToZero(), -2);
TS_ASSERT_EQUALS(CFixed_23_8::FromFloat(-2.5f).ToInt_RoundToZero(), -2); TS_ASSERT_EQUALS(fixed::FromFloat(-2.5f).ToInt_RoundToZero(), -2);
TS_ASSERT_EQUALS(CFixed_23_8::FromFloat(-2.99f).ToInt_RoundToZero(), -2); TS_ASSERT_EQUALS(fixed::FromFloat(-2.99f).ToInt_RoundToZero(), -2);
} }
// TODO: test all the arithmetic operators // TODO: test all the arithmetic operators
void test_Sqrt() void test_Sqrt()
{ {
TS_ASSERT_EQUALS(CFixed_23_8::FromDouble(1.0).Sqrt().ToDouble(), 1.0); TS_ASSERT_EQUALS(fixed::FromDouble(1.0).Sqrt().ToDouble(), 1.0);
TS_ASSERT_EQUALS(CFixed_23_8::FromDouble(1000000.0).Sqrt().ToDouble(), 1000.0); TS_ASSERT_EQUALS(fixed::FromDouble(32400.0).Sqrt().ToDouble(), 180.0);
TS_ASSERT_EQUALS(CFixed_23_8::FromDouble(0.0625).Sqrt().ToDouble(), 0.25); TS_ASSERT_EQUALS(fixed::FromDouble(0.0625).Sqrt().ToDouble(), 0.25);
TS_ASSERT_EQUALS(CFixed_23_8::FromDouble(-1.0).Sqrt().ToDouble(), 0.0); TS_ASSERT_EQUALS(fixed::FromDouble(-1.0).Sqrt().ToDouble(), 0.0);
TS_ASSERT_EQUALS(CFixed_23_8::FromDouble(0.0).Sqrt().ToDouble(), 0.0); TS_ASSERT_EQUALS(fixed::FromDouble(0.0).Sqrt().ToDouble(), 0.0);
} }
void test_Atan2() void test_Atan2()
{ {
typedef CFixed_23_8 f;
// Special cases from atan2 man page: // Special cases from atan2 man page:
TS_ASSERT_DELTA(atan2_approx(f::FromInt(0), f::FromInt(-1)).ToDouble(), M_PI, 0.01); TS_ASSERT_DELTA(atan2_approx(fixed::FromInt(0), fixed::FromInt(-1)).ToDouble(), M_PI, 0.01);
TS_ASSERT_EQUALS(atan2_approx(f::FromInt(0), f::FromInt(+1)).ToDouble(), 0); TS_ASSERT_EQUALS(atan2_approx(fixed::FromInt(0), fixed::FromInt(+1)).ToDouble(), 0);
TS_ASSERT_DELTA(atan2_approx(f::FromInt(-1), f::FromInt(0)).ToDouble(), -M_PI_2, 0.01); TS_ASSERT_DELTA(atan2_approx(fixed::FromInt(-1), fixed::FromInt(0)).ToDouble(), -M_PI_2, 0.01);
TS_ASSERT_DELTA(atan2_approx(f::FromInt(+1), f::FromInt(0)).ToDouble(), +M_PI_2, 0.01); TS_ASSERT_DELTA(atan2_approx(fixed::FromInt(+1), fixed::FromInt(0)).ToDouble(), +M_PI_2, 0.01);
TS_ASSERT_EQUALS(atan2_approx(f::FromInt(0), f::FromInt(0)).ToDouble(), 0); TS_ASSERT_EQUALS(atan2_approx(fixed::FromInt(0), fixed::FromInt(0)).ToDouble(), 0);
// Test that it approximately matches libc's atan2 // Test that it approximately matches libc's atan2
#define T(y, x) TS_ASSERT_DELTA(atan2_approx(f::FromDouble(y), f::FromDouble(x)).ToDouble(), atan2((double)(y), (double)(x)), 0.078) #define T(y, x) TS_ASSERT_DELTA(atan2_approx(fixed::FromDouble(y), fixed::FromDouble(x)).ToDouble(), atan2((double)(y), (double)(x)), 0.072)
// Quadrants // Quadrants
T(1, 1); T(1, 1);

View File

@ -0,0 +1,143 @@
/* Copyright (C) 2010 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "lib/self_test.h"
#include "maths/FixedVector2D.h"
#define TS_ASSERT_VEC_EQUALS(v, x, y) \
TS_ASSERT_EQUALS(v.X.ToDouble(), x); \
TS_ASSERT_EQUALS(v.Y.ToDouble(), y);
#define TS_ASSERT_VEC_DELTA(v, x, y, delta) \
TS_ASSERT_DELTA(v.X.ToDouble(), x, delta); \
TS_ASSERT_DELTA(v.Y.ToDouble(), y, delta);
class TestFixedVector2D : public CxxTest::TestSuite
{
public:
void test_basic()
{
CFixedVector2D v1 (fixed::FromInt(1), fixed::FromInt(2));
CFixedVector2D v2 (fixed::FromInt(10), fixed::FromInt(20));
CFixedVector2D v3;
TS_ASSERT_VEC_EQUALS(v1, 1, 2);
TS_ASSERT_VEC_EQUALS(v2, 10, 20);
TS_ASSERT_VEC_EQUALS(v3, 0, 0);
v3 = v1 + v2;
TS_ASSERT_VEC_EQUALS(v3, 11, 22);
v3 += v2;
TS_ASSERT_VEC_EQUALS(v3, 21, 42);
v3 -= v2;
TS_ASSERT_VEC_EQUALS(v3, 11, 22);
v3 = v1 - v2;
TS_ASSERT_VEC_EQUALS(v3, -9, -18);
v3 = -v3;
TS_ASSERT_VEC_EQUALS(v3, 9, 18);
}
void test_Length()
{
CFixedVector2D v1 (fixed::FromInt(3), fixed::FromInt(4));
TS_ASSERT_EQUALS(v1.Length().ToDouble(), 5.0);
fixed max;
max.SetInternalValue((i32)0x7fffffff);
CFixedVector2D v2 (max, fixed::FromInt(0));
TS_ASSERT_EQUALS(v2.Length().ToDouble(), max.ToDouble());
fixed large;
large.SetInternalValue((i32)((double)0x7fffffff/sqrt(2.0))); // largest value that shouldn't cause overflow
CFixedVector2D v3 (large, large);
TS_ASSERT_DELTA(v3.Length().ToDouble(), sqrt(2.0)*large.ToDouble(), 0.01);
}
void test_Normalize()
{
CFixedVector2D v0 (fixed::FromInt(0), fixed::FromInt(0));
v0.Normalize();
TS_ASSERT_VEC_EQUALS(v0, 0.0, 0.0);
CFixedVector2D v1 (fixed::FromInt(3), fixed::FromInt(4));
v1.Normalize();
TS_ASSERT_VEC_DELTA(v1, 3.0/5.0, 4.0/5.0, 0.01);
fixed max;
max.SetInternalValue((i32)0x7fffffff);
CFixedVector2D v2 (max, fixed::FromInt(0));
v2.Normalize();
TS_ASSERT_VEC_EQUALS(v2, 1.0, 0.0);
fixed large;
large.SetInternalValue((i32)((double)0x7fffffff/sqrt(2.0))); // largest value that shouldn't cause overflow
CFixedVector2D v3 (large, large);
v3.Normalize();
TS_ASSERT_VEC_DELTA(v3, 1.0/sqrt(2.0), 1.0/sqrt(2.0), 0.01);
}
void test_NormalizeTo()
{
{
CFixedVector2D v (fixed::FromInt(0), fixed::FromInt(0));
v.Normalize(fixed::FromInt(1));
TS_ASSERT_VEC_EQUALS(v, 0.0, 0.0);
}
{
CFixedVector2D v (fixed::FromInt(3), fixed::FromInt(4));
v.Normalize(fixed::FromInt(0));
TS_ASSERT_VEC_EQUALS(v, 0.0, 0.0);
}
{
CFixedVector2D v (fixed::FromInt(3), fixed::FromInt(4));
v.Normalize(fixed::FromInt(1));
TS_ASSERT_VEC_DELTA(v, 3.0/5.0, 4.0/5.0, 0.01);
}
{
CFixedVector2D v (fixed::FromInt(3000), fixed::FromInt(4000));
v.Normalize(fixed::FromInt(1));
TS_ASSERT_VEC_DELTA(v, 3.0/5.0, 4.0/5.0, 0.01);
}
{
CFixedVector2D v (fixed::FromInt(3), fixed::FromInt(4));
v.Normalize(fixed::FromInt(100));
TS_ASSERT_VEC_DELTA(v, 300.0/5.0, 400.0/5.0, 0.02);
}
{
CFixedVector2D v (fixed::FromInt(3), fixed::FromInt(4));
v.Normalize(fixed::FromInt(1)/100);
TS_ASSERT_VEC_DELTA(v, 3.0/500.0, 4.0/500.0, 0.0001);
}
{
CFixedVector2D v (fixed::FromInt(3), fixed::FromInt(4));
v.Normalize(fixed::FromInt(1)/10000);
TS_ASSERT_VEC_DELTA(v, 3.0/50000.0, 4.0/50000.0, 0.0001);
}
}
void test_Dot()
{
CFixedVector2D v1 (fixed::FromInt(5), fixed::FromInt(6));
CFixedVector2D v2 (fixed::FromInt(8), fixed::FromInt(-9));
TS_ASSERT_EQUALS(v1.Dot(v2).ToDouble(), 5*8 + 6*-9);
}
};

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games. /* Copyright (C) 2010 Wildfire Games.
* This file is part of 0 A.D. * This file is part of 0 A.D.
* *
* 0 A.D. is free software: you can redistribute it and/or modify * 0 A.D. is free software: you can redistribute it and/or modify
@ -24,9 +24,13 @@
TS_ASSERT_EQUALS(v.Y.ToDouble(), y); \ TS_ASSERT_EQUALS(v.Y.ToDouble(), y); \
TS_ASSERT_EQUALS(v.Z.ToDouble(), z); TS_ASSERT_EQUALS(v.Z.ToDouble(), z);
#define TS_ASSERT_VEC_DELTA(v, x, y, z, delta) \
TS_ASSERT_DELTA(v.X.ToDouble(), x, delta); \
TS_ASSERT_DELTA(v.Y.ToDouble(), y, delta); \
TS_ASSERT_DELTA(v.Z.ToDouble(), z, delta);
class TestFixedVector3D : public CxxTest::TestSuite class TestFixedVector3D : public CxxTest::TestSuite
{ {
typedef CFixed_23_8 fixed;
public: public:
void test_basic() void test_basic()
{ {
@ -68,31 +72,68 @@ public:
{ {
CFixedVector3D v0 (fixed::FromInt(0), fixed::FromInt(0), fixed::FromInt(0)); CFixedVector3D v0 (fixed::FromInt(0), fixed::FromInt(0), fixed::FromInt(0));
v0.Normalize(); v0.Normalize();
TS_ASSERT_EQUALS(v0.X.ToDouble(), 0.0); TS_ASSERT_VEC_EQUALS(v0, 0.0, 0.0, 0.0);
TS_ASSERT_EQUALS(v0.Y.ToDouble(), 0.0);
TS_ASSERT_EQUALS(v0.Z.ToDouble(), 0.0);
CFixedVector3D v1 (fixed::FromInt(3), fixed::FromInt(4), fixed::FromInt(12)); CFixedVector3D v1 (fixed::FromInt(3), fixed::FromInt(4), fixed::FromInt(12));
v1.Normalize(); v1.Normalize();
TS_ASSERT_DELTA(v1.X.ToDouble(), 3.0/13.0, 0.01); TS_ASSERT_VEC_DELTA(v1, 3.0/13.0, 4.0/13.0, 12.0/13.0, 0.01);
TS_ASSERT_DELTA(v1.Y.ToDouble(), 4.0/13.0, 0.01);
TS_ASSERT_DELTA(v1.Z.ToDouble(), 12.0/13.0, 0.01);
fixed max; fixed max;
max.SetInternalValue((i32)0x7fffffff); max.SetInternalValue((i32)0x7fffffff);
CFixedVector3D v2 (max, fixed::FromInt(0), fixed::FromInt(0)); CFixedVector3D v2 (max, fixed::FromInt(0), fixed::FromInt(0));
v2.Normalize(); v2.Normalize();
TS_ASSERT_EQUALS(v2.X.ToDouble(), 1.0); TS_ASSERT_VEC_EQUALS(v2, 1.0, 0.0, 0.0);
TS_ASSERT_EQUALS(v2.Y.ToDouble(), 0.0);
TS_ASSERT_EQUALS(v2.Z.ToDouble(), 0.0);
fixed large; fixed large;
large.SetInternalValue((i32)((double)0x7fffffff/sqrt(3.0))+1); // largest value that shouldn't cause overflow large.SetInternalValue((i32)((double)0x7fffffff/sqrt(3.0))+1); // largest value that shouldn't cause overflow
CFixedVector3D v3 (large, large, large); CFixedVector3D v3 (large, large, large);
v3.Normalize(); v3.Normalize();
TS_ASSERT_DELTA(v3.X.ToDouble(), 1.0/sqrt(3.0), 0.01); TS_ASSERT_VEC_DELTA(v3, 1.0/sqrt(3.0), 1.0/sqrt(3.0), 1.0/sqrt(3.0), 0.01);
TS_ASSERT_DELTA(v3.Y.ToDouble(), 1.0/sqrt(3.0), 0.01); }
TS_ASSERT_DELTA(v3.Z.ToDouble(), 1.0/sqrt(3.0), 0.01);
void test_NormalizeTo()
{
{
CFixedVector3D v (fixed::FromInt(0), fixed::FromInt(0), fixed::FromInt(0));
v.Normalize(fixed::FromInt(1));
TS_ASSERT_VEC_EQUALS(v, 0.0, 0.0, 0.0);
}
{
CFixedVector3D v (fixed::FromInt(3), fixed::FromInt(4), fixed::FromInt(12));
v.Normalize(fixed::FromInt(0));
TS_ASSERT_VEC_EQUALS(v, 0.0, 0.0, 0.0);
}
{
CFixedVector3D v (fixed::FromInt(3), fixed::FromInt(4), fixed::FromInt(12));
v.Normalize(fixed::FromInt(1));
TS_ASSERT_VEC_DELTA(v, 3.0/13.0, 4.0/13.0, 12.0/13.0, 0.01);
}
{
CFixedVector3D v (fixed::FromInt(3000), fixed::FromInt(4000), fixed::FromInt(12000));
v.Normalize(fixed::FromInt(1));
TS_ASSERT_VEC_DELTA(v, 3.0/13.0, 4.0/13.0, 12.0/13.0, 0.01);
}
{
CFixedVector3D v (fixed::FromInt(3), fixed::FromInt(4), fixed::FromInt(12));
v.Normalize(fixed::FromInt(100));
TS_ASSERT_VEC_DELTA(v, 300.0/13.0, 400.0/13.0, 1200.0/13.0, 0.01);
}
{
CFixedVector3D v (fixed::FromInt(3), fixed::FromInt(4), fixed::FromInt(12));
v.Normalize(fixed::FromInt(1)/100);
TS_ASSERT_VEC_DELTA(v, 3.0/1300.0, 4.0/1300.0, 12.0/1300.0, 0.0001);
}
{
CFixedVector3D v (fixed::FromInt(3), fixed::FromInt(4), fixed::FromInt(12));
v.Normalize(fixed::FromInt(1)/10000);
TS_ASSERT_VEC_DELTA(v, 3.0/130000.0, 4.0/130000.0, 12.0/130000.0, 0.0001);
}
} }
void test_Cross() void test_Cross()
@ -100,9 +141,7 @@ public:
CFixedVector3D v1 (fixed::FromInt(5), fixed::FromInt(6), fixed::FromInt(7)); CFixedVector3D v1 (fixed::FromInt(5), fixed::FromInt(6), fixed::FromInt(7));
CFixedVector3D v2 (fixed::FromInt(8), fixed::FromInt(9), fixed::FromInt(-10)); CFixedVector3D v2 (fixed::FromInt(8), fixed::FromInt(9), fixed::FromInt(-10));
CFixedVector3D v3 = v1.Cross(v2); CFixedVector3D v3 = v1.Cross(v2);
TS_ASSERT_EQUALS(v3.X.ToDouble(), 6*-10 - 7*9); TS_ASSERT_VEC_EQUALS(v3, 6*-10 - 7*9, 7*8 - 5*-10, 5*9 - 8*6);
TS_ASSERT_EQUALS(v3.Y.ToDouble(), 7*8 - 5*-10);
TS_ASSERT_EQUALS(v3.Z.ToDouble(), 5*9 - 8*6);
} }
void test_Dot() void test_Dot()

View File

@ -113,10 +113,10 @@ public:
convert_to<const char*>("test", "\"test\""); convert_to<const char*>("test", "\"test\"");
convert_to<const char*>(s1.c_str(), "\"t\""); convert_to<const char*>(s1.c_str(), "\"t\"");
roundtrip<CFixed_23_8>(CFixed_23_8::FromInt(0), "0"); roundtrip<fixed>(fixed::FromInt(0), "0");
roundtrip<CFixed_23_8>(CFixed_23_8::FromInt(123), "123"); roundtrip<fixed>(fixed::FromInt(123), "123");
roundtrip<CFixed_23_8>(CFixed_23_8::FromInt(-123), "-123"); roundtrip<fixed>(fixed::FromInt(-123), "-123");
roundtrip<CFixed_23_8>(CFixed_23_8::FromDouble(123.25), "123.25"); roundtrip<fixed>(fixed::FromDouble(123.25), "123.25");
} }
void test_integers() void test_integers()

View File

@ -49,12 +49,12 @@ class CMessageUpdate : public CMessage
public: public:
DEFAULT_MESSAGE_IMPL(Update) DEFAULT_MESSAGE_IMPL(Update)
CMessageUpdate(CFixed_23_8 turnLength) : CMessageUpdate(fixed turnLength) :
turnLength(turnLength) turnLength(turnLength)
{ {
} }
CFixed_23_8 turnLength; fixed turnLength;
}; };
class CMessageInterpolate : public CMessage class CMessageInterpolate : public CMessage
@ -151,12 +151,12 @@ class CMessageMotionChanged : public CMessage
public: public:
DEFAULT_MESSAGE_IMPL(MotionChanged) DEFAULT_MESSAGE_IMPL(MotionChanged)
CMessageMotionChanged(CFixed_23_8 speed) : CMessageMotionChanged(fixed speed) :
speed(speed) speed(speed)
{ {
} }
CFixed_23_8 speed; // metres per second, or 0 if not moving fixed speed; // metres per second, or 0 if not moving
}; };
#endif // INCLUDED_MESSAGETYPES #endif // INCLUDED_MESSAGETYPES

View File

@ -148,7 +148,7 @@ bool CSimulation2Impl::Update(float frameTime)
if (m_DeltaTime >= 0.0) if (m_DeltaTime >= 0.0)
{ {
double turnLength = TURN_LENGTH / 1000.0; double turnLength = TURN_LENGTH / 1000.0;
CFixed_23_8 turnLengthFixed = CFixed_23_8::FromInt(TURN_LENGTH) / 1000; fixed turnLengthFixed = fixed::FromInt(TURN_LENGTH) / 1000;
m_DeltaTime -= turnLength; m_DeltaTime -= turnLength;
CMessageTurnStart msgTurnStart; CMessageTurnStart msgTurnStart;

View File

@ -36,9 +36,9 @@ public:
DEFAULT_COMPONENT_ALLOCATOR(Footprint) DEFAULT_COMPONENT_ALLOCATOR(Footprint)
EShape m_Shape; EShape m_Shape;
CFixed_23_8 m_Size0; // width/radius entity_pos_t m_Size0; // width/radius
CFixed_23_8 m_Size1; // height/radius entity_pos_t m_Size1; // height/radius
CFixed_23_8 m_Height; entity_pos_t m_Height;
static std::string GetSchema() static std::string GetSchema()
{ {
@ -90,7 +90,7 @@ public:
{ {
// Error - pick some default // Error - pick some default
m_Shape = CIRCLE; m_Shape = CIRCLE;
m_Size0 = m_Size1 = CFixed_23_8::FromInt(1); m_Size0 = m_Size1 = entity_pos_t::FromInt(1);
} }
m_Height = paramNode.GetChild("Height").ToFixed(); m_Height = paramNode.GetChild("Height").ToFixed();
@ -109,7 +109,7 @@ public:
Init(context, paramNode); Init(context, paramNode);
} }
virtual void GetShape(EShape& shape, CFixed_23_8& size0, CFixed_23_8& size1, CFixed_23_8& height) virtual void GetShape(EShape& shape, entity_pos_t& size0, entity_pos_t& size1, entity_pos_t& height)
{ {
shape = m_Shape; shape = m_Shape;
size0 = m_Size0; size0 = m_Size0;
@ -124,7 +124,7 @@ public:
// because the footprint might be inside the obstruction, but it hopefully gives us a nicer // because the footprint might be inside the obstruction, but it hopefully gives us a nicer
// shape.) // shape.)
CFixedVector3D error(CFixed_23_8::FromInt(-1), CFixed_23_8::FromInt(-1), CFixed_23_8::FromInt(-1)); CFixedVector3D error(fixed::FromInt(-1), fixed::FromInt(-1), fixed::FromInt(-1));
CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), GetEntityId()); CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), GetEntityId());
if (cmpPosition.null() || !cmpPosition->IsInWorld()) if (cmpPosition.null() || !cmpPosition->IsInWorld())
@ -142,14 +142,14 @@ public:
// else zero // else zero
// The spawn point should be far enough from this footprint to fit the unit, plus a little gap // The spawn point should be far enough from this footprint to fit the unit, plus a little gap
CFixed_23_8 clearance = spawnedRadius + entity_pos_t::FromInt(2); entity_pos_t clearance = spawnedRadius + entity_pos_t::FromInt(2);
CFixedVector3D initialPos = cmpPosition->GetPosition(); CFixedVector3D initialPos = cmpPosition->GetPosition();
entity_angle_t initialAngle = cmpPosition->GetRotation().Y; entity_angle_t initialAngle = cmpPosition->GetRotation().Y;
if (m_Shape == CIRCLE) if (m_Shape == CIRCLE)
{ {
CFixed_23_8 radius = m_Size0 + clearance; entity_pos_t radius = m_Size0 + clearance;
// Try equally-spaced points around the circle, starting from the front and expanding outwards in alternating directions // Try equally-spaced points around the circle, starting from the front and expanding outwards in alternating directions
const ssize_t numPoints = 31; const ssize_t numPoints = 31;
@ -157,10 +157,10 @@ public:
{ {
entity_angle_t angle = initialAngle + (entity_angle_t::Pi()*2).Multiply(entity_angle_t::FromInt(i)/(int)numPoints); entity_angle_t angle = initialAngle + (entity_angle_t::Pi()*2).Multiply(entity_angle_t::FromInt(i)/(int)numPoints);
CFixed_23_8 s, c; fixed s, c;
sincos_approx(angle, s, c); sincos_approx(angle, s, c);
CFixedVector3D pos (initialPos.X + s.Multiply(radius), CFixed_23_8::Zero(), initialPos.Z + c.Multiply(radius)); CFixedVector3D pos (initialPos.X + s.Multiply(radius), fixed::Zero(), initialPos.Z + c.Multiply(radius));
SkipTagObstructionFilter filter(spawned); // ignore collisions with the spawned entity SkipTagObstructionFilter filter(spawned); // ignore collisions with the spawned entity
if (!cmpObstructionManager->TestUnitShape(filter, pos.X, pos.Z, spawnedRadius)) if (!cmpObstructionManager->TestUnitShape(filter, pos.X, pos.Z, spawnedRadius))
@ -169,7 +169,7 @@ public:
} }
else else
{ {
CFixed_23_8 s, c; fixed s, c;
sincos_approx(initialAngle, s, c); sincos_approx(initialAngle, s, c);
for (size_t edge = 0; edge < 4; ++edge) for (size_t edge = 0; edge < 4; ++edge)
@ -179,7 +179,7 @@ public:
// Compute the direction and length of the current edge // Compute the direction and length of the current edge
CFixedVector2D dir; CFixedVector2D dir;
CFixed_23_8 sx, sy; fixed sx, sy;
switch (edge) switch (edge)
{ {
case 0: case 0:
@ -214,7 +214,7 @@ public:
SkipTagObstructionFilter filter(spawned); // ignore collisions with the spawned entity SkipTagObstructionFilter filter(spawned); // ignore collisions with the spawned entity
if (!cmpObstructionManager->TestUnitShape(filter, pos.X, pos.Y, spawnedRadius)) if (!cmpObstructionManager->TestUnitShape(filter, pos.X, pos.Y, spawnedRadius))
return CFixedVector3D(pos.X, CFixed_23_8::Zero(), pos.Y); // this position is okay, so return it return CFixedVector3D(pos.X, fixed::Zero(), pos.Y); // this position is okay, so return it
} }
} }
} }

View File

@ -72,17 +72,17 @@ public:
{ {
case MT_Update: case MT_Update:
{ {
CFixed_23_8 dt = static_cast<const CMessageUpdate&> (msg).turnLength; fixed dt = static_cast<const CMessageUpdate&> (msg).turnLength;
Move(context, dt); Move(context, dt);
break; break;
} }
} }
} }
void Move(const CSimContext& context, CFixed_23_8 dt); void Move(const CSimContext& context, fixed dt);
}; };
void CCmpMotionBall::Move(const CSimContext& context, CFixed_23_8 dt) void CCmpMotionBall::Move(const CSimContext& context, fixed dt)
{ {
CmpPtr<ICmpPosition> cmpPosition(context, GetEntityId()); CmpPtr<ICmpPosition> cmpPosition(context, GetEntityId());
if (cmpPosition.null()) if (cmpPosition.null())

View File

@ -138,7 +138,7 @@ public:
virtual tag_t AddStaticShape(entity_pos_t x, entity_pos_t z, entity_angle_t a, entity_pos_t w, entity_pos_t h) virtual tag_t AddStaticShape(entity_pos_t x, entity_pos_t z, entity_angle_t a, entity_pos_t w, entity_pos_t h)
{ {
CFixed_23_8 s, c; fixed s, c;
sincos_approx(a, s, c); sincos_approx(a, s, c);
CFixedVector2D u(c, -s); CFixedVector2D u(c, -s);
CFixedVector2D v(s, c); CFixedVector2D v(s, c);
@ -162,7 +162,7 @@ public:
} }
else else
{ {
CFixed_23_8 s, c; fixed s, c;
sincos_approx(a, s, c); sincos_approx(a, s, c);
CFixedVector2D u(c, -s); CFixedVector2D u(c, -s);
CFixedVector2D v(s, c); CFixedVector2D v(s, c);
@ -304,7 +304,7 @@ bool CCmpObstructionManager::TestStaticShape(const IObstructionTestFilter& filte
{ {
PROFILE("TestStaticShape"); PROFILE("TestStaticShape");
CFixed_23_8 s, c; fixed s, c;
sincos_approx(a, s, c); sincos_approx(a, s, c);
CFixedVector2D u(c, -s); CFixedVector2D u(c, -s);
CFixedVector2D v(s, c); CFixedVector2D v(s, c);

View File

@ -53,8 +53,6 @@
class CCmpPathfinder; class CCmpPathfinder;
struct PathfindTile; struct PathfindTile;
typedef CFixed_23_8 fixed;
/** /**
* Terrain overlay for pathfinder debugging. * Terrain overlay for pathfinder debugging.
* Renders a representation of the most recent pathfinding operation. * Renders a representation of the most recent pathfinding operation.
@ -494,12 +492,12 @@ typedef PriorityQueueList<std::pair<u16, u16>, u32> PriorityQueue;
static u32 CalculateHeuristic(u16 i, u16 j, u16 iGoal, u16 jGoal, u16 rGoal) static u32 CalculateHeuristic(u16 i, u16 j, u16 iGoal, u16 jGoal, u16 rGoal)
{ {
#ifdef USE_DIAGONAL_MOVEMENT #ifdef USE_DIAGONAL_MOVEMENT
CFixedVector2D pos (CFixed_23_8::FromInt(i), CFixed_23_8::FromInt(j)); CFixedVector2D pos (fixed::FromInt(i), fixed::FromInt(j));
CFixedVector2D goal (CFixed_23_8::FromInt(iGoal), CFixed_23_8::FromInt(jGoal)); CFixedVector2D goal (fixed::FromInt(iGoal), fixed::FromInt(jGoal));
CFixed_23_8 dist = (pos - goal).Length(); fixed dist = (pos - goal).Length();
// TODO: the heuristic could match the costs better - it's not really Euclidean movement // TODO: the heuristic could match the costs better - it's not really Euclidean movement
CFixed_23_8 rdist = dist - CFixed_23_8::FromInt(rGoal); fixed rdist = dist - fixed::FromInt(rGoal);
rdist = rdist.Absolute(); rdist = rdist.Absolute();
return (rdist * (int)g_CostPerTile).ToInt_RoundToZero(); return (rdist * (int)g_CostPerTile).ToInt_RoundToZero();

View File

@ -89,12 +89,12 @@ public:
} }
} }
virtual void LaunchProjectileAtEntity(entity_id_t source, entity_id_t target, CFixed_23_8 speed, CFixed_23_8 gravity) virtual void LaunchProjectileAtEntity(entity_id_t source, entity_id_t target, fixed speed, fixed gravity)
{ {
LaunchProjectile(source, CFixedVector3D(), target, speed, gravity); LaunchProjectile(source, CFixedVector3D(), target, speed, gravity);
} }
virtual void LaunchProjectileAtPoint(entity_id_t source, CFixedVector3D target, CFixed_23_8 speed, CFixed_23_8 gravity) virtual void LaunchProjectileAtPoint(entity_id_t source, CFixedVector3D target, fixed speed, fixed gravity)
{ {
LaunchProjectile(source, target, INVALID_ENTITY, speed, gravity); LaunchProjectile(source, target, INVALID_ENTITY, speed, gravity);
} }
@ -113,7 +113,7 @@ private:
std::vector<Projectile> m_Projectiles; std::vector<Projectile> m_Projectiles;
void LaunchProjectile(entity_id_t source, CFixedVector3D targetPoint, entity_id_t targetEnt, CFixed_23_8 speed, CFixed_23_8 gravity); void LaunchProjectile(entity_id_t source, CFixedVector3D targetPoint, entity_id_t targetEnt, fixed speed, fixed gravity);
void AdvanceProjectile(const CSimContext& context, Projectile& projectile, float dt, float frameOffset); void AdvanceProjectile(const CSimContext& context, Projectile& projectile, float dt, float frameOffset);
@ -124,7 +124,7 @@ private:
REGISTER_COMPONENT_TYPE(ProjectileManager) REGISTER_COMPONENT_TYPE(ProjectileManager)
void CCmpProjectileManager::LaunchProjectile(entity_id_t source, CFixedVector3D targetPoint, entity_id_t targetEnt, CFixed_23_8 speed, CFixed_23_8 gravity) void CCmpProjectileManager::LaunchProjectile(entity_id_t source, CFixedVector3D targetPoint, entity_id_t targetEnt, fixed speed, fixed gravity)
{ {
if (!GetSimContext().HasUnitManager()) if (!GetSimContext().HasUnitManager())
return; // do nothing if graphics are disabled return; // do nothing if graphics are disabled

View File

@ -59,7 +59,7 @@ public:
// Template state: // Template state:
CFixed_23_8 m_Speed; // in units per second fixed m_Speed; // in units per second
entity_pos_t m_Radius; entity_pos_t m_Radius;
// Dynamic state: // Dynamic state:
@ -138,12 +138,12 @@ public:
{ {
case MT_Update: case MT_Update:
{ {
CFixed_23_8 dt = static_cast<const CMessageUpdate&> (msg).turnLength; fixed dt = static_cast<const CMessageUpdate&> (msg).turnLength;
if (m_State == STOPPING) if (m_State == STOPPING)
{ {
m_State = IDLE; m_State = IDLE;
CMessageMotionChanged msg(CFixed_23_8::FromInt(0)); CMessageMotionChanged msg(fixed::Zero());
context.GetComponentManager().PostMessage(GetEntityId(), msg); context.GetComponentManager().PostMessage(GetEntityId(), msg);
} }
@ -160,7 +160,7 @@ public:
} }
} }
virtual CFixed_23_8 GetSpeed() virtual fixed GetSpeed()
{ {
return m_Speed; return m_Speed;
} }
@ -191,7 +191,7 @@ private:
/** /**
* Do the per-turn movement and other updates * Do the per-turn movement and other updates
*/ */
void Move(CFixed_23_8 dt); void Move(fixed dt);
void StopAndFaceGoal(CFixedVector2D pos); void StopAndFaceGoal(CFixedVector2D pos);
@ -276,7 +276,7 @@ bool CCmpUnitMotion::CheckMovement(CFixedVector2D pos, CFixedVector2D target)
return true; return true;
} }
void CCmpUnitMotion::Move(CFixed_23_8 dt) void CCmpUnitMotion::Move(fixed dt)
{ {
PROFILE("Move"); PROFILE("Move");
@ -292,7 +292,7 @@ void CCmpUnitMotion::Move(CFixed_23_8 dt)
// We want to move (at most) m_Speed*dt units from pos towards the next waypoint // We want to move (at most) m_Speed*dt units from pos towards the next waypoint
while (dt > CFixed_23_8::FromInt(0)) while (dt > fixed::Zero())
{ {
CFixedVector2D target(m_ShortTargetX, m_ShortTargetZ); CFixedVector2D target(m_ShortTargetX, m_ShortTargetZ);
CFixedVector2D offset = target - pos; CFixedVector2D offset = target - pos;
@ -302,10 +302,10 @@ void CCmpUnitMotion::Move(CFixed_23_8 dt)
cmpPosition->TurnTo(angle); cmpPosition->TurnTo(angle);
// Work out how far we can travel in dt // Work out how far we can travel in dt
CFixed_23_8 maxdist = m_Speed.Multiply(dt); fixed maxdist = m_Speed.Multiply(dt);
// If the target is close, we can move there directly // If the target is close, we can move there directly
CFixed_23_8 offsetLength = offset.Length(); fixed offsetLength = offset.Length();
if (offsetLength <= maxdist) if (offsetLength <= maxdist)
{ {
if (!CheckMovement(pos, target)) if (!CheckMovement(pos, target))

View File

@ -230,7 +230,7 @@ public:
m_Unit->SetAnimationSync(actiontime, repeattime); m_Unit->SetAnimationSync(actiontime, repeattime);
} }
virtual void SetShadingColour(CFixed_23_8 r, CFixed_23_8 g, CFixed_23_8 b, CFixed_23_8 a) virtual void SetShadingColour(fixed r, fixed g, fixed b, fixed a)
{ {
if (!m_Unit) if (!m_Unit)
return; return;

View File

@ -22,6 +22,6 @@
#include "simulation2/system/InterfaceScripted.h" #include "simulation2/system/InterfaceScripted.h"
BEGIN_INTERFACE_WRAPPER(ProjectileManager) BEGIN_INTERFACE_WRAPPER(ProjectileManager)
DEFINE_INTERFACE_METHOD_4("LaunchProjectileAtEntity", void, ICmpProjectileManager, LaunchProjectileAtEntity, entity_id_t, entity_id_t, CFixed_23_8, CFixed_23_8) DEFINE_INTERFACE_METHOD_4("LaunchProjectileAtEntity", void, ICmpProjectileManager, LaunchProjectileAtEntity, entity_id_t, entity_id_t, fixed, fixed)
DEFINE_INTERFACE_METHOD_4("LaunchProjectileAtPoint", void, ICmpProjectileManager, LaunchProjectileAtPoint, entity_id_t, CFixedVector3D, CFixed_23_8, CFixed_23_8) DEFINE_INTERFACE_METHOD_4("LaunchProjectileAtPoint", void, ICmpProjectileManager, LaunchProjectileAtPoint, entity_id_t, CFixedVector3D, fixed, fixed)
END_INTERFACE_WRAPPER(ProjectileManager) END_INTERFACE_WRAPPER(ProjectileManager)

View File

@ -38,7 +38,7 @@ public:
* @param speed horizontal speed in m/s * @param speed horizontal speed in m/s
* @param gravity gravitational acceleration in m/s^2 (determines the height of the ballistic curve) * @param gravity gravitational acceleration in m/s^2 (determines the height of the ballistic curve)
*/ */
virtual void LaunchProjectileAtEntity(entity_id_t source, entity_id_t target, CFixed_23_8 speed, CFixed_23_8 gravity) = 0; virtual void LaunchProjectileAtEntity(entity_id_t source, entity_id_t target, fixed speed, fixed gravity) = 0;
/** /**
* Launch a projectile from entity @p source to point @p target. * Launch a projectile from entity @p source to point @p target.
@ -47,7 +47,7 @@ public:
* @param speed horizontal speed in m/s * @param speed horizontal speed in m/s
* @param gravity gravitational acceleration in m/s^2 (determines the height of the ballistic curve) * @param gravity gravitational acceleration in m/s^2 (determines the height of the ballistic curve)
*/ */
virtual void LaunchProjectileAtPoint(entity_id_t source, CFixedVector3D target, CFixed_23_8 speed, CFixed_23_8 gravity) = 0; virtual void LaunchProjectileAtPoint(entity_id_t source, CFixedVector3D target, fixed speed, fixed gravity) = 0;
DECLARE_INTERFACE_TYPE(ProjectileManager) DECLARE_INTERFACE_TYPE(ProjectileManager)
}; };

View File

@ -25,6 +25,6 @@ BEGIN_INTERFACE_WRAPPER(UnitMotion)
DEFINE_INTERFACE_METHOD_2("MoveToPoint", bool, ICmpUnitMotion, MoveToPoint, entity_pos_t, entity_pos_t) DEFINE_INTERFACE_METHOD_2("MoveToPoint", bool, ICmpUnitMotion, MoveToPoint, entity_pos_t, entity_pos_t)
DEFINE_INTERFACE_METHOD_3("IsInAttackRange", bool, ICmpUnitMotion, IsInAttackRange, entity_id_t, entity_pos_t, entity_pos_t) DEFINE_INTERFACE_METHOD_3("IsInAttackRange", bool, ICmpUnitMotion, IsInAttackRange, entity_id_t, entity_pos_t, entity_pos_t)
DEFINE_INTERFACE_METHOD_3("MoveToAttackRange", bool, ICmpUnitMotion, MoveToAttackRange, entity_id_t, entity_pos_t, entity_pos_t) DEFINE_INTERFACE_METHOD_3("MoveToAttackRange", bool, ICmpUnitMotion, MoveToAttackRange, entity_id_t, entity_pos_t, entity_pos_t)
DEFINE_INTERFACE_METHOD_0("GetSpeed", CFixed_23_8, ICmpUnitMotion, GetSpeed) DEFINE_INTERFACE_METHOD_0("GetSpeed", fixed, ICmpUnitMotion, GetSpeed)
DEFINE_INTERFACE_METHOD_1("SetDebugOverlay", void, ICmpUnitMotion, SetDebugOverlay, bool) DEFINE_INTERFACE_METHOD_1("SetDebugOverlay", void, ICmpUnitMotion, SetDebugOverlay, bool)
END_INTERFACE_WRAPPER(UnitMotion) END_INTERFACE_WRAPPER(UnitMotion)

View File

@ -61,9 +61,9 @@ public:
virtual bool MoveToAttackRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) = 0; virtual bool MoveToAttackRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) = 0;
/** /**
* Get the default speed that this unit will have when walking. * Get the default speed that this unit will have when walking, in metres per second.
*/ */
virtual CFixed_23_8 GetSpeed() = 0; virtual fixed GetSpeed() = 0;
/** /**
* Toggle the rendering of debug info. * Toggle the rendering of debug info.

View File

@ -24,5 +24,5 @@
BEGIN_INTERFACE_WRAPPER(Visual) BEGIN_INTERFACE_WRAPPER(Visual)
DEFINE_INTERFACE_METHOD_4("SelectAnimation", void, ICmpVisual, SelectAnimation, std::string, bool, float, std::wstring) DEFINE_INTERFACE_METHOD_4("SelectAnimation", void, ICmpVisual, SelectAnimation, std::string, bool, float, std::wstring)
DEFINE_INTERFACE_METHOD_2("SetAnimationSync", void, ICmpVisual, SetAnimationSync, float, float) DEFINE_INTERFACE_METHOD_2("SetAnimationSync", void, ICmpVisual, SetAnimationSync, float, float)
DEFINE_INTERFACE_METHOD_4("SetShadingColour", void, ICmpVisual, SetShadingColour, CFixed_23_8, CFixed_23_8, CFixed_23_8, CFixed_23_8) DEFINE_INTERFACE_METHOD_4("SetShadingColour", void, ICmpVisual, SetShadingColour, fixed, fixed, fixed, fixed)
END_INTERFACE_WRAPPER(Visual) END_INTERFACE_WRAPPER(Visual)

View File

@ -82,7 +82,7 @@ public:
* @param b blue component, expected range [0, 1] * @param b blue component, expected range [0, 1]
* @param a alpha component, expected range [0, 1] * @param a alpha component, expected range [0, 1]
*/ */
virtual void SetShadingColour(CFixed_23_8 r, CFixed_23_8 g, CFixed_23_8 b, CFixed_23_8 a) = 0; virtual void SetShadingColour(fixed r, fixed g, fixed b, fixed a) = 0;
DECLARE_INTERFACE_TYPE(Visual) DECLARE_INTERFACE_TYPE(Visual)
}; };

View File

@ -32,7 +32,7 @@ public:
static CFixedVector3D fixedvec(int x, int y, int z) static CFixedVector3D fixedvec(int x, int y, int z)
{ {
return CFixedVector3D(CFixed_23_8::FromInt(x), CFixed_23_8::FromInt(y), CFixed_23_8::FromInt(z)); return CFixedVector3D(fixed::FromInt(x), fixed::FromInt(y), fixed::FromInt(z));
} }
void test_basic() void test_basic()

View File

@ -27,10 +27,10 @@ using namespace Geometry;
bool Geometry::PointIsInSquare(CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize) bool Geometry::PointIsInSquare(CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize)
{ {
CFixed_23_8 du = point.Dot(u); fixed du = point.Dot(u);
if (-halfSize.X <= du && du <= halfSize.X) if (-halfSize.X <= du && du <= halfSize.X)
{ {
CFixed_23_8 dv = point.Dot(v); fixed dv = point.Dot(v);
if (-halfSize.Y <= dv && dv <= halfSize.Y) if (-halfSize.Y <= dv && dv <= halfSize.Y)
return true; return true;
} }
@ -45,7 +45,7 @@ CFixedVector2D Geometry::GetHalfBoundingBox(CFixedVector2D u, CFixedVector2D v,
); );
} }
Geometry::fixed Geometry::DistanceToSquare(CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize) fixed Geometry::DistanceToSquare(CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize)
{ {
/* /*
* Relative to its own coordinate system, we have a square like: * Relative to its own coordinate system, we have a square like:

View File

@ -30,8 +30,6 @@ class CFixedVector2D;
namespace Geometry namespace Geometry
{ {
typedef CFixed_23_8 fixed;
bool PointIsInSquare(CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize); bool PointIsInSquare(CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize);
CFixedVector2D GetHalfBoundingBox(CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize); CFixedVector2D GetHalfBoundingBox(CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize);

View File

@ -33,12 +33,11 @@
/** /**
* Positions and distances (measured in metres) * Positions and distances (measured in metres)
*/ */
typedef CFixed_23_8 entity_pos_t; typedef CFixed_15_16 entity_pos_t;
/** /**
* Rotations (measured in radians) * Rotations (measured in radians)
*/ */
typedef CFixed_23_8 entity_angle_t; typedef CFixed_15_16 entity_angle_t;
// TODO: use a type with more precision
#endif // INCLUDED_POSITION #endif // INCLUDED_POSITION

View File

@ -171,17 +171,17 @@ template<> jsval ScriptInterface::ToJSVal<CColor>(JSContext* cx, CColor const& v
return OBJECT_TO_JSVAL(obj); return OBJECT_TO_JSVAL(obj);
} }
template<> bool ScriptInterface::FromJSVal<CFixed_23_8>(JSContext* cx, jsval v, CFixed_23_8& out) template<> bool ScriptInterface::FromJSVal<fixed>(JSContext* cx, jsval v, fixed& out)
{ {
jsdouble ret; jsdouble ret;
if (!JS_ValueToNumber(cx, v, &ret)) if (!JS_ValueToNumber(cx, v, &ret))
return false; return false;
out = CFixed_23_8::FromDouble(ret); out = fixed::FromDouble(ret);
// TODO: ought to check that this conversion is consistent and portable // TODO: ought to check that this conversion is consistent and portable
return true; return true;
} }
template<> jsval ScriptInterface::ToJSVal<CFixed_23_8>(JSContext* cx, const CFixed_23_8& val) template<> jsval ScriptInterface::ToJSVal<fixed>(JSContext* cx, const fixed& val)
{ {
jsval rval = JSVAL_VOID; jsval rval = JSVAL_VOID;
JS_NewNumberValue(cx, val.ToDouble(), &rval); // ignore return value JS_NewNumberValue(cx, val.ToDouble(), &rval); // ignore return value

View File

@ -74,7 +74,7 @@ jsval CMessageUpdate::ToJSVal(ScriptInterface& scriptInterface) const
CMessage* CMessageUpdate::FromJSVal(ScriptInterface& scriptInterface, jsval val) CMessage* CMessageUpdate::FromJSVal(ScriptInterface& scriptInterface, jsval val)
{ {
FROMJSVAL_SETUP(); FROMJSVAL_SETUP();
GET_MSG_PROPERTY(CFixed_23_8, turnLength); GET_MSG_PROPERTY(fixed, turnLength);
return new CMessageUpdate(turnLength); return new CMessageUpdate(turnLength);
} }
@ -168,7 +168,7 @@ jsval CMessageMotionChanged::ToJSVal(ScriptInterface& scriptInterface) const
CMessage* CMessageMotionChanged::FromJSVal(ScriptInterface& scriptInterface, jsval val) CMessage* CMessageMotionChanged::FromJSVal(ScriptInterface& scriptInterface, jsval val)
{ {
FROMJSVAL_SETUP(); FROMJSVAL_SETUP();
GET_MSG_PROPERTY(CFixed_23_8, speed); GET_MSG_PROPERTY(fixed, speed);
return new CMessageMotionChanged(speed); return new CMessageMotionChanged(speed);
} }

View File

@ -80,7 +80,7 @@ void CBinarySerializer::PutNumber(const char* name, double value)
Put(name, (const u8*)&value, sizeof(double)); Put(name, (const u8*)&value, sizeof(double));
} }
void CBinarySerializer::PutNumber(const char* name, CFixed_23_8 value) void CBinarySerializer::PutNumber(const char* name, fixed value)
{ {
PutNumber(name, value.GetInternalValue()); PutNumber(name, value.GetInternalValue());
} }

View File

@ -39,7 +39,7 @@ protected:
virtual void PutNumber(const char* name, uint32_t value); virtual void PutNumber(const char* name, uint32_t value);
virtual void PutNumber(const char* name, float value); virtual void PutNumber(const char* name, float value);
virtual void PutNumber(const char* name, double value); virtual void PutNumber(const char* name, double value);
virtual void PutNumber(const char* name, CFixed_23_8 value); virtual void PutNumber(const char* name, fixed value);
virtual void PutBool(const char* name, bool value); virtual void PutBool(const char* name, bool value);
virtual void PutString(const char* name, const std::string& value); virtual void PutString(const char* name, const std::string& value);
virtual void PutScriptVal(const char* name, jsval value); virtual void PutScriptVal(const char* name, jsval value);

View File

@ -102,7 +102,7 @@ void CDebugSerializer::PutNumber(const char* name, double value)
m_Stream << INDENT << name << ": " << canonfloat(value) << "\n"; m_Stream << INDENT << name << ": " << canonfloat(value) << "\n";
} }
void CDebugSerializer::PutNumber(const char* name, CFixed_23_8 value) void CDebugSerializer::PutNumber(const char* name, fixed value)
{ {
m_Stream << INDENT << name << ": " << canonfloat(value.ToDouble()) << "\n"; m_Stream << INDENT << name << ": " << canonfloat(value.ToDouble()) << "\n";
} }

View File

@ -46,7 +46,7 @@ protected:
virtual void PutNumber(const char* name, uint32_t value); virtual void PutNumber(const char* name, uint32_t value);
virtual void PutNumber(const char* name, float value); virtual void PutNumber(const char* name, float value);
virtual void PutNumber(const char* name, double value); virtual void PutNumber(const char* name, double value);
virtual void PutNumber(const char* name, CFixed_23_8 value); virtual void PutNumber(const char* name, fixed value);
virtual void PutBool(const char* name, bool value); virtual void PutBool(const char* name, bool value);
virtual void PutString(const char* name, const std::string& value); virtual void PutString(const char* name, const std::string& value);
virtual void PutScriptVal(const char* name, jsval value); virtual void PutScriptVal(const char* name, jsval value);

View File

@ -83,7 +83,7 @@ void IDeserializer::NumberDouble_Unbounded(double& out)
Get((u8*)&out, sizeof(double)); Get((u8*)&out, sizeof(double));
} }
void IDeserializer::NumberFixed_Unbounded(CFixed_23_8& out) void IDeserializer::NumberFixed_Unbounded(fixed& out)
{ {
int32_t n; int32_t n;
NumberI32_Unbounded(n); NumberI32_Unbounded(n);

View File

@ -45,7 +45,7 @@ public:
virtual void NumberU32_Unbounded(uint32_t& out); virtual void NumberU32_Unbounded(uint32_t& out);
virtual void NumberFloat_Unbounded(float& out); virtual void NumberFloat_Unbounded(float& out);
virtual void NumberDouble_Unbounded(double& out); virtual void NumberDouble_Unbounded(double& out);
virtual void NumberFixed_Unbounded(CFixed_23_8& out); virtual void NumberFixed_Unbounded(fixed& out);
virtual void Bool(bool& out); virtual void Bool(bool& out);
virtual void StringASCII(std::string& out, uint32_t minlength, uint32_t maxlength); virtual void StringASCII(std::string& out, uint32_t minlength, uint32_t maxlength);
virtual void String(std::wstring& out, uint32_t minlength, uint32_t maxlength); virtual void String(std::wstring& out, uint32_t minlength, uint32_t maxlength);

View File

@ -71,7 +71,7 @@ void ISerializer::NumberDouble_Unbounded(const char* name, double value)
PutNumber(name, value); PutNumber(name, value);
} }
void ISerializer::NumberFixed_Unbounded(const char* name, CFixed_23_8 value) void ISerializer::NumberFixed_Unbounded(const char* name, fixed value)
{ {
PutNumber(name, value); PutNumber(name, value);
} }

View File

@ -149,7 +149,7 @@ public:
void NumberU32_Unbounded(const char* name, uint32_t value); ///< @copydoc NumberU8_Unbounded void NumberU32_Unbounded(const char* name, uint32_t value); ///< @copydoc NumberU8_Unbounded
void NumberFloat_Unbounded(const char* name, float value); ///< @copydoc NumberU8_Unbounded void NumberFloat_Unbounded(const char* name, float value); ///< @copydoc NumberU8_Unbounded
void NumberDouble_Unbounded(const char* name, double value); ///< @copydoc NumberU8_Unbounded void NumberDouble_Unbounded(const char* name, double value); ///< @copydoc NumberU8_Unbounded
void NumberFixed_Unbounded(const char* name, CFixed_23_8 value); ///< @copydoc NumberU8_Unbounded void NumberFixed_Unbounded(const char* name, fixed value); ///< @copydoc NumberU8_Unbounded
/** /**
* Serialize a boolean. * Serialize a boolean.
@ -205,7 +205,7 @@ protected:
virtual void PutNumber(const char* name, uint32_t value) = 0; virtual void PutNumber(const char* name, uint32_t value) = 0;
virtual void PutNumber(const char* name, float value) = 0; virtual void PutNumber(const char* name, float value) = 0;
virtual void PutNumber(const char* name, double value) = 0; virtual void PutNumber(const char* name, double value) = 0;
virtual void PutNumber(const char* name, CFixed_23_8 value) = 0; virtual void PutNumber(const char* name, fixed value) = 0;
virtual void PutBool(const char* name, bool value) = 0; virtual void PutBool(const char* name, bool value) = 0;
virtual void PutString(const char* name, const std::string& value) = 0; virtual void PutString(const char* name, const std::string& value) = 0;
virtual void PutScriptVal(const char* name, jsval value) = 0; virtual void PutScriptVal(const char* name, jsval value) = 0;

View File

@ -164,12 +164,12 @@ public:
virtual CFixedVector3D CalcNormal(entity_pos_t UNUSED(x), entity_pos_t UNUSED(z)) virtual CFixedVector3D CalcNormal(entity_pos_t UNUSED(x), entity_pos_t UNUSED(z))
{ {
return CFixedVector3D(CFixed_23_8::FromInt(0), CFixed_23_8::FromInt(1), CFixed_23_8::FromInt(0)); return CFixedVector3D(fixed::FromInt(0), fixed::FromInt(1), fixed::FromInt(0));
} }
virtual entity_pos_t GetGroundLevel(entity_pos_t UNUSED(x), entity_pos_t UNUSED(z)) virtual entity_pos_t GetGroundLevel(entity_pos_t UNUSED(x), entity_pos_t UNUSED(z))
{ {
return CFixed_23_8::FromInt(50); return entity_pos_t::FromInt(50);
} }
virtual float GetGroundLevel(float UNUSED(x), float UNUSED(z)) virtual float GetGroundLevel(float UNUSED(x), float UNUSED(z))

View File

@ -19,6 +19,7 @@
#include "ParamNode.h" #include "ParamNode.h"
#include "ps/CStr.h"
#include "ps/XML/Xeromyces.h" #include "ps/XML/Xeromyces.h"
#include <sstream> #include <sstream>
@ -135,14 +136,9 @@ int CParamNode::ToInt() const
return ret; return ret;
} }
CFixed_23_8 CParamNode::ToFixed() const fixed CParamNode::ToFixed() const
{ {
double ret = 0.0; return fixed::FromString(CStrW(m_Value));
std::wstringstream strm;
strm << m_Value;
strm >> ret;
return CFixed_23_8::FromDouble(ret);
// TODO: this shouldn't use floating point types
} }
bool CParamNode::ToBool() const bool CParamNode::ToBool() const

View File

@ -151,7 +151,7 @@ public:
/** /**
* Parses the content of this node as a fixed-point number * Parses the content of this node as a fixed-point number
*/ */
CFixed_23_8 ToFixed() const; fixed ToFixed() const;
/** /**
* Parses the content of this node as a boolean ("true" == true, anything else == false) * Parses the content of this node as a boolean ("true" == true, anything else == false)

View File

@ -155,7 +155,7 @@ public:
man.AddComponent(ent4, CID_Test2A, noParam); man.AddComponent(ent4, CID_Test2A, noParam);
CMessageTurnStart msg1; CMessageTurnStart msg1;
CMessageUpdate msg2(CFixed_23_8::FromInt(100)); CMessageUpdate msg2(fixed::FromInt(100));
CMessageInterpolate msg3(0, 0); CMessageInterpolate msg3(0, 0);
TS_ASSERT_EQUALS(static_cast<ICmpTest1*> (man.QueryInterface(ent1, IID_Test1))->GetX(), 11000); TS_ASSERT_EQUALS(static_cast<ICmpTest1*> (man.QueryInterface(ent1, IID_Test1))->GetX(), 11000);
@ -252,7 +252,7 @@ public:
TS_ASSERT_EQUALS(static_cast<ICmpTest2*> (man.QueryInterface(ent3, IID_Test2))->GetX(), 201000); TS_ASSERT_EQUALS(static_cast<ICmpTest2*> (man.QueryInterface(ent3, IID_Test2))->GetX(), 201000);
CMessageTurnStart msg1; CMessageTurnStart msg1;
CMessageUpdate msg2(CFixed_23_8::FromInt(25)); CMessageUpdate msg2(fixed::FromInt(25));
man.BroadcastMessage(msg1); man.BroadcastMessage(msg1);

View File

@ -44,7 +44,7 @@ public:
serialize.NumberU32_Unbounded("u32", (unsigned)-123); serialize.NumberU32_Unbounded("u32", (unsigned)-123);
serialize.NumberFloat_Unbounded("float", 1e+30f); serialize.NumberFloat_Unbounded("float", 1e+30f);
serialize.NumberDouble_Unbounded("double", 1e+300); serialize.NumberDouble_Unbounded("double", 1e+300);
serialize.NumberFixed_Unbounded("fixed", CFixed_23_8::FromFloat(1234.5f)); serialize.NumberFixed_Unbounded("fixed", fixed::FromFloat(1234.5f));
serialize.Bool("t", true); serialize.Bool("t", true);
serialize.Bool("f", false); serialize.Bool("f", false);
@ -93,11 +93,11 @@ public:
serialize.NumberDouble_Unbounded("x", 1e-10); serialize.NumberDouble_Unbounded("x", 1e-10);
serialize.NumberDouble_Unbounded("x", 1e100); serialize.NumberDouble_Unbounded("x", 1e100);
serialize.NumberDouble_Unbounded("x", 1e-100); serialize.NumberDouble_Unbounded("x", 1e-100);
serialize.NumberFixed_Unbounded("x", CFixed_23_8::FromDouble(1e6)); serialize.NumberFixed_Unbounded("x", fixed::FromDouble(1e4));
TS_ASSERT_STR_EQUALS(stream.str(), TS_ASSERT_STR_EQUALS(stream.str(),
"x: 10000\nx: 0.0001\nx: 100000\nx: 1e-05\nx: 1e+06\nx: 1e-06\nx: 1e+10\nx: 1e-10\n" "x: 10000\nx: 0.0001\nx: 100000\nx: 1e-05\nx: 1e+06\nx: 1e-06\nx: 1e+10\nx: 1e-10\n"
"x: 10000\nx: 0.0001\nx: 100000\nx: 1e-05\nx: 1e+06\nx: 1e-06\nx: 1e+10\nx: 1e-10\nx: 1e+100\nx: 1e-100\n" "x: 10000\nx: 0.0001\nx: 100000\nx: 1e-05\nx: 1e+06\nx: 1e-06\nx: 1e+10\nx: 1e-10\nx: 1e+100\nx: 1e-100\n"
"x: 1e+06\n" "x: 10000\n"
); );
} }
@ -169,7 +169,7 @@ public:
uint32_t u32v; uint32_t u32v;
float flt; float flt;
double dbl; double dbl;
CFixed_23_8 fixed; fixed fxd;
bool bl; bool bl;
std::string str; std::string str;
std::wstring wstr; std::wstring wstr;
@ -185,8 +185,8 @@ public:
TS_ASSERT_EQUALS(flt, 1e+30f); TS_ASSERT_EQUALS(flt, 1e+30f);
deserialize.NumberDouble_Unbounded(dbl); deserialize.NumberDouble_Unbounded(dbl);
TS_ASSERT_EQUALS(dbl, 1e+300); TS_ASSERT_EQUALS(dbl, 1e+300);
deserialize.NumberFixed_Unbounded(fixed); deserialize.NumberFixed_Unbounded(fxd);
TS_ASSERT_EQUALS(fixed.ToDouble(), 1234.5); TS_ASSERT_EQUALS(fxd.ToDouble(), 1234.5);
deserialize.Bool(bl); deserialize.Bool(bl);
TS_ASSERT_EQUALS(bl, true); TS_ASSERT_EQUALS(bl, true);

View File

@ -921,7 +921,7 @@ BEGIN_COMMAND(RotateObject)
if (cmpPos.null()) if (cmpPos.null())
return; return;
cmpPos->SetYRotation(CFixed_23_8::FromFloat(angle)); cmpPos->SetYRotation(entity_angle_t::FromFloat(angle));
} }
else else
{ {