Importing code changes for q3map2 from Rambetter-math-fix-experiments branch
into trunk. Right now all the new code that fixes problems is turned off. There are three new #defines in q3map2.h: EXPERIMENTAL_HIGH_PRECISION_MATH_Q3MAP2_FIXES, EXPERIMENTAL_SNAP_NORMAL_FIX, and EXPERIMENTAL_SNAP_PLANE_FIX. All of these are currently set to 0, which means don't enable that new code. You can easily edit these to be 1 in order to enable the new code. There are very very minor changes to the code even with these three #defines disabled. They are as follows. - In PlaneEqual() in map.c, now considering deltas equal to given epsilon values as "far enough to be different". Previously, the '<=' operation was used, now '<' is being used. - In FindFloatPlane() in map.c, considering delta equal to distanceEpsilon (for plane distance) to be sufficiently far away. Before, delta had to be strictly greater than distanceEpsilon. - VectorNormalize() in mathlib.c is more accurate now. This change itself causes at least one regression test to succeed. The previous implementation of VectorNormalize() caused excessive errors to be introduced due to sloppy arithmetic. Note, the epsilon changes account for the possibility that the epsilons are set to 0.0 on the command-line. git-svn-id: svn://svn.icculus.org/gtkradiant/GtkRadiant/trunk@416 8a3a26a2-13c4-0310-b231-cf6edde360e5
This commit is contained in:
@@ -24,6 +24,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
// mathlib.h
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
|
||||
#include "bytebool.h"
|
||||
|
||||
@@ -37,6 +38,12 @@ typedef vec_t vec3_t[3];
|
||||
typedef vec_t vec5_t[5];
|
||||
typedef vec_t vec4_t[4];
|
||||
|
||||
// Smallest positive value for vec_t such that 1.0 + VEC_SMALLEST_EPSILON_AROUND_ONE != 1.0.
|
||||
// In the case of 32 bit floats (which is almost certainly the case), it's 0.00000011921.
|
||||
// Don't forget that your epsilons should depend on the possible range of values,
|
||||
// because for example adding VEC_SMALLEST_EPSILON_AROUND_ONE to 1024.0 will have no effect.
|
||||
#define VEC_SMALLEST_EPSILON_AROUND_ONE FLT_EPSILON
|
||||
|
||||
#define SIDE_FRONT 0
|
||||
#define SIDE_ON 2
|
||||
#define SIDE_BACK 1
|
||||
@@ -75,6 +82,9 @@ qboolean VectorCompare (vec3_t v1, vec3_t v2);
|
||||
|
||||
#define Q_rint(in) ((vec_t)floor(in+0.5))
|
||||
|
||||
qboolean VectorIsOnAxis(vec3_t v);
|
||||
qboolean VectorIsOnAxialPlane(vec3_t v);
|
||||
|
||||
vec_t VectorLength(vec3_t v);
|
||||
|
||||
void VectorMA( const vec3_t va, vec_t scale, const vec3_t vb, vec3_t vc );
|
||||
@@ -298,6 +308,50 @@ vec_t ray_intersect_point(const ray_t *ray, const vec3_t point, vec_t epsilon, v
|
||||
/*! return true if triangle intersects ray... dist = dist from intersection point to ray-origin */
|
||||
vec_t ray_intersect_triangle(const ray_t *ray, qboolean bCullBack, const vec3_t vert0, const vec3_t vert1, const vec3_t vert2);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Below is double-precision math stuff. This was initially needed by the new
|
||||
// "base winding" code in q3map2 brush processing in order to fix the famous
|
||||
// "disappearing triangles" issue. These definitions can be used wherever extra
|
||||
// precision is needed.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef double vec_accu_t;
|
||||
typedef vec_accu_t vec3_accu_t[3];
|
||||
|
||||
// Smallest positive value for vec_accu_t such that 1.0 + VEC_ACCU_SMALLEST_EPSILON_AROUND_ONE != 1.0.
|
||||
// In the case of 64 bit doubles (which is almost certainly the case), it's 0.00000000000000022204.
|
||||
// Don't forget that your epsilons should depend on the possible range of values,
|
||||
// because for example adding VEC_ACCU_SMALLEST_EPSILON_AROUND_ONE to 1024.0 will have no effect.
|
||||
#define VEC_ACCU_SMALLEST_EPSILON_AROUND_ONE DBL_EPSILON
|
||||
|
||||
vec_accu_t VectorLengthAccu(const vec3_accu_t v);
|
||||
|
||||
// I have a feeling it may be safer to break these #define functions out into actual functions
|
||||
// in order to avoid accidental loss of precision. For example, say you call
|
||||
// VectorScaleAccu(vec3_t, vec_t, vec3_accu_t). The scale would take place in 32 bit land
|
||||
// and the result would be cast to 64 bit, which would cause total loss of precision when
|
||||
// scaling by a large factor.
|
||||
//#define DotProductAccu(x, y) ((x)[0] * (y)[0] + (x)[1] * (y)[1] + (x)[2] * (y)[2])
|
||||
//#define VectorSubtractAccu(a, b, c) ((c)[0] = (a)[0] - (b)[0], (c)[1] = (a)[1] - (b)[1], (c)[2] = (a)[2] - (b)[2])
|
||||
//#define VectorAddAccu(a, b, c) ((c)[0] = (a)[0] + (b)[0], (c)[1] = (a)[1] + (b)[1], (c)[2] = (a)[2] + (b)[2])
|
||||
//#define VectorCopyAccu(a, b) ((b)[0] = (a)[0], (b)[1] = (a)[1], (b)[2] = (a)[2])
|
||||
//#define VectorScaleAccu(a, b, c) ((c)[0] = (b) * (a)[0], (c)[1] = (b) * (a)[1], (c)[2] = (b) * (a)[2])
|
||||
//#define CrossProductAccu(a, b, c) ((c)[0] = (a)[1] * (b)[2] - (a)[2] * (b)[1], (c)[1] = (a)[2] * (b)[0] - (a)[0] * (b)[2], (c)[2] = (a)[0] * (b)[1] - (a)[1] * (b)[0])
|
||||
//#define Q_rintAccu(in) ((vec_accu_t) floor(in + 0.5))
|
||||
|
||||
vec_accu_t DotProductAccu(const vec3_accu_t a, const vec3_accu_t b);
|
||||
void VectorSubtractAccu(const vec3_accu_t a, const vec3_accu_t b, vec3_accu_t out);
|
||||
void VectorAddAccu(const vec3_accu_t a, const vec3_accu_t b, vec3_accu_t out);
|
||||
void VectorCopyAccu(const vec3_accu_t in, vec3_accu_t out);
|
||||
void VectorScaleAccu(const vec3_accu_t in, vec_accu_t scaleFactor, vec3_accu_t out);
|
||||
void CrossProductAccu(const vec3_accu_t a, const vec3_accu_t b, vec3_accu_t out);
|
||||
vec_accu_t Q_rintAccu(vec_accu_t val);
|
||||
|
||||
void VectorCopyAccuToRegular(const vec3_accu_t in, vec3_t out);
|
||||
void VectorCopyRegularToAccu(const vec3_t in, vec3_accu_t out);
|
||||
vec_accu_t VectorNormalizeAccu(const vec3_accu_t in, vec3_accu_t out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -26,6 +26,54 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
vec3_t vec3_origin = {0.0f,0.0f,0.0f};
|
||||
|
||||
/*
|
||||
================
|
||||
VectorIsOnAxis
|
||||
================
|
||||
*/
|
||||
qboolean VectorIsOnAxis(vec3_t v)
|
||||
{
|
||||
int i, zeroComponentCount;
|
||||
|
||||
zeroComponentCount = 0;
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
if (v[i] == 0.0)
|
||||
{
|
||||
zeroComponentCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (zeroComponentCount > 1)
|
||||
{
|
||||
// The zero vector will be on axis.
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
VectorIsOnAxialPlane
|
||||
================
|
||||
*/
|
||||
qboolean VectorIsOnAxialPlane(vec3_t v)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
if (v[i] == 0.0)
|
||||
{
|
||||
// The zero vector will be on axial plane.
|
||||
return qtrue;
|
||||
}
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
MakeNormalVectors
|
||||
@@ -127,21 +175,30 @@ void _VectorCopy (vec3_t in, vec3_t out)
|
||||
}
|
||||
|
||||
vec_t VectorNormalize( const vec3_t in, vec3_t out ) {
|
||||
vec_t length, ilength;
|
||||
|
||||
length = (vec_t)sqrt (in[0]*in[0] + in[1]*in[1] + in[2]*in[2]);
|
||||
// The sqrt() function takes double as an input and returns double as an
|
||||
// output according the the man pages on Debian and on FreeBSD. Therefore,
|
||||
// I don't see a reason why using a double outright (instead of using the
|
||||
// vec_accu_t alias for example) could possibly be frowned upon.
|
||||
|
||||
double x, y, z, length;
|
||||
|
||||
x = (double) in[0];
|
||||
y = (double) in[1];
|
||||
z = (double) in[2];
|
||||
|
||||
length = sqrt((x * x) + (y * y) + (z * z));
|
||||
if (length == 0)
|
||||
{
|
||||
VectorClear (out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ilength = 1.0f/length;
|
||||
out[0] = in[0]*ilength;
|
||||
out[1] = in[1]*ilength;
|
||||
out[2] = in[2]*ilength;
|
||||
out[0] = (vec_t) (x / length);
|
||||
out[1] = (vec_t) (y / length);
|
||||
out[2] = (vec_t) (z / length);
|
||||
|
||||
return length;
|
||||
return (vec_t) length;
|
||||
}
|
||||
|
||||
vec_t ColorNormalize( const vec3_t in, vec3_t out ) {
|
||||
@@ -591,3 +648,153 @@ void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point,
|
||||
dst[i] = rot[i][0] * point[0] + rot[i][1] * point[1] + rot[i][2] * point[2];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Below is double-precision math stuff. This was initially needed by the new
|
||||
// "base winding" code in q3map2 brush processing in order to fix the famous
|
||||
// "disappearing triangles" issue. These definitions can be used wherever extra
|
||||
// precision is needed.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
=================
|
||||
VectorLengthAccu
|
||||
=================
|
||||
*/
|
||||
vec_accu_t VectorLengthAccu(const vec3_accu_t v)
|
||||
{
|
||||
return (vec_accu_t) sqrt((v[0] * v[0]) + (v[1] * v[1]) + (v[2] * v[2]));
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
DotProductAccu
|
||||
=================
|
||||
*/
|
||||
vec_accu_t DotProductAccu(const vec3_accu_t a, const vec3_accu_t b)
|
||||
{
|
||||
return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
VectorSubtractAccu
|
||||
=================
|
||||
*/
|
||||
void VectorSubtractAccu(const vec3_accu_t a, const vec3_accu_t b, vec3_accu_t out)
|
||||
{
|
||||
out[0] = a[0] - b[0];
|
||||
out[1] = a[1] - b[1];
|
||||
out[2] = a[2] - b[2];
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
VectorAddAccu
|
||||
=================
|
||||
*/
|
||||
void VectorAddAccu(const vec3_accu_t a, const vec3_accu_t b, vec3_accu_t out)
|
||||
{
|
||||
out[0] = a[0] + b[0];
|
||||
out[1] = a[1] + b[1];
|
||||
out[2] = a[2] + b[2];
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
VectorCopyAccu
|
||||
=================
|
||||
*/
|
||||
void VectorCopyAccu(const vec3_accu_t in, vec3_accu_t out)
|
||||
{
|
||||
out[0] = in[0];
|
||||
out[1] = in[1];
|
||||
out[2] = in[2];
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
VectorScaleAccu
|
||||
=================
|
||||
*/
|
||||
void VectorScaleAccu(const vec3_accu_t in, vec_accu_t scaleFactor, vec3_accu_t out)
|
||||
{
|
||||
out[0] = in[0] * scaleFactor;
|
||||
out[1] = in[1] * scaleFactor;
|
||||
out[2] = in[2] * scaleFactor;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CrossProductAccu
|
||||
=================
|
||||
*/
|
||||
void CrossProductAccu(const vec3_accu_t a, const vec3_accu_t b, vec3_accu_t out)
|
||||
{
|
||||
out[0] = (a[1] * b[2]) - (a[2] * b[1]);
|
||||
out[1] = (a[2] * b[0]) - (a[0] * b[2]);
|
||||
out[2] = (a[0] * b[1]) - (a[1] * b[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
Q_rintAccu
|
||||
=================
|
||||
*/
|
||||
vec_accu_t Q_rintAccu(vec_accu_t val)
|
||||
{
|
||||
return (vec_accu_t) floor(val + 0.5);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
VectorCopyAccuToRegular
|
||||
=================
|
||||
*/
|
||||
void VectorCopyAccuToRegular(const vec3_accu_t in, vec3_t out)
|
||||
{
|
||||
out[0] = (vec_t) in[0];
|
||||
out[1] = (vec_t) in[1];
|
||||
out[2] = (vec_t) in[2];
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
VectorCopyRegularToAccu
|
||||
=================
|
||||
*/
|
||||
void VectorCopyRegularToAccu(const vec3_t in, vec3_accu_t out)
|
||||
{
|
||||
out[0] = (vec_accu_t) in[0];
|
||||
out[1] = (vec_accu_t) in[1];
|
||||
out[2] = (vec_accu_t) in[2];
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
VectorNormalizeAccu
|
||||
=================
|
||||
*/
|
||||
vec_accu_t VectorNormalizeAccu(const vec3_accu_t in, vec3_accu_t out)
|
||||
{
|
||||
// The sqrt() function takes double as an input and returns double as an
|
||||
// output according the the man pages on Debian and on FreeBSD. Therefore,
|
||||
// I don't see a reason why using a double outright (instead of using the
|
||||
// vec_accu_t alias for example) could possibly be frowned upon.
|
||||
|
||||
vec_accu_t length;
|
||||
|
||||
length = (vec_accu_t) sqrt((in[0] * in[0]) + (in[1] * in[1]) + (in[2] * in[2]));
|
||||
if (length == 0)
|
||||
{
|
||||
VectorClear(out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
out[0] = in[0] / length;
|
||||
out[1] = in[1] / length;
|
||||
out[2] = in[2] / length;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user