// * This file is part of the COLOBOT source code // * Copyright (C) 2001-2008, Daniel ROUX & EPSITEC SA, www.epsitec.ch // * // * This program 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 3 of the License, or // * (at your option) any later version. // * // * This program 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 this program. If not, see http://www.gnu.org/licenses/. // math3d.cpp #define STRICT #define D3D_OVERLOADS #include #include #include #include "struct.h" #include "d3dengine.h" #include "d3dmath.h" #include "d3dutil.h" #include "math3d.h" // Returns TRUE if two numbers are nearly equal. BOOL IsEqual(float a, float b) { return Abs(a-b) < CHOUIA; } // Returns the minimum value. float Min(float a, float b) { if ( a <= b ) return a; else return b; } float Min(float a, float b, float c) { return Min( Min(a,b), c ); } float Min(float a, float b, float c, float d) { return Min( Min(a,b), Min(c,d) ); } float Min(float a, float b, float c, float d, float e) { return Min( Min(a,b), Min(c,d), e ); } // Returns the maximum value. float Max(float a, float b) { if ( a >= b ) return a; else return b; } float Max(float a, float b, float c) { return Max( Max(a,b), c ); } float Max(float a, float b, float c, float d) { return Max( Max(a,b), Max(c,d) ); } float Max(float a, float b, float c, float d, float e) { return Max( Max(a,b), Max(c,d), e ); } // Returns the normalized value (0 .. 1). float Norm(float a) { if ( a < 0.0f ) return 0.0f; if ( a > 1.0f ) return 1.0f; return a; } // Returns the absolute value of a number. float Abs(float a) { return (float)fabs(a); } // Swaps two integers. void Swap(int &a, int &b) { int c; c = a; a = b; b = c; } // Swaps two real numbers. void Swap(float &a, float &b) { float c; c = a; a = b; b = c; } // Permutes two points. void Swap(FPOINT &a, FPOINT &b) { FPOINT c; c = a; a = b; b = c; } // Returns the modulo of a floating point number. // Mod(8.1, 4) = 0.1 // Mod(n, 1) = fractional part of n float Mod(float a, float m) { return a - ((int)(a/m))*m; } // Returns a normalized angle, that is in other words between 0 and 2 * PI. float NormAngle(float angle) { angle = Mod(angle, PI*2.0f); if ( angle < 0.0f ) { return PI*2.0f + angle; } else { return angle; } } // Test if a angle is between two terminals. BOOL TestAngle(float angle, float min, float max) { angle = NormAngle(angle); min = NormAngle(min); max = NormAngle(max); if ( min > max ) { return ( angle <= max || angle >= min ); } else { return ( angle >= min && angle <= max ); } } // Calculates the angle to rotate the angle a to the angle g. // A positive angle is counterclockwise (CCW). float Direction(float a, float g) { a = NormAngle(a); g = NormAngle(g); if ( a < g ) { if ( a+PI*2.0f-g < g-a ) a += PI*2.0f; } else { if ( g+PI*2.0f-a < a-g ) g += PI*2.0f; } return (g-a); } // Rotates a point around a center. // The angle is in radians. // A positive angle is counterclockwise (CCW). FPOINT RotatePoint(FPOINT center, float angle, FPOINT p) { FPOINT a, b; a.x = p.x-center.x; a.y = p.y-center.y; b.x = a.x*cosf(angle) - a.y*sinf(angle); b.y = a.x*sinf(angle) + a.y*cosf(angle); b.x += center.x; b.y += center.y; return b; } // Rotates a point around the origin. // The angle is in radians. // A positive angle is counterclockwise (CCW). FPOINT RotatePoint(float angle, FPOINT p) { FPOINT a; a.x = p.x*cosf(angle) - p.y*sinf(angle); a.y = p.x*sinf(angle) + p.y*cosf(angle); return a; } // Rotates a vector (dist, 0). // The angle is in radians. // A positive angle is counterclockwise (CCW). FPOINT RotatePoint(float angle, float dist) { FPOINT a; a.x = dist*cosf(angle); a.y = dist*sinf(angle); return a; } // Calculates the angle of a right triangle. // The angle is counterclockwise (CCW), between 0 and 2 * PI. // For an angle clockwise (CW), just go ahead. // // ^ // | // y o----o // | / | // |/)a | // ----o----o--> // | x // | float RotateAngle(float x, float y) { #if 1 if ( x == 0.0f && y == 0.0f ) return 0.0f; if ( x >= 0.0f ) { if ( y >= 0.0f ) { if ( x > y ) return atanf(y/x); else return PI*0.5f - atanf(x/y); } else { if ( x > -y ) return PI*2.0f + atanf(y/x); else return PI*1.5f - atanf(x/y); } } else { if ( y >= 0.0f ) { if ( -x > y ) return PI*1.0f + atanf(y/x); else return PI*0.5f - atanf(x/y); } else { if ( -x > -y ) return PI*1.0f + atanf(y/x); else return PI*1.5f - atanf(x/y); } } #else float angle; if ( x == 0.0f ) { if ( y > 0.0f ) { return 90.0f*PI/180.0f; } else { return 270.0f*PI/180.0f; } } else { angle = atanf(y/x); if ( x < 0.0f ) { angle += PI; } return angle; } #endif } // Calculates the angle between two points and one center. // The angle is in radians. // A positive angle is counterclockwise (CCW). float RotateAngle(FPOINT center, FPOINT p1, FPOINT p2) { float a1, a2, a; if ( p1.x == center.x && p1.y == center.y ) return 0; if ( p2.x == center.x && p2.y == center.y ) return 0; a1 = asinf((p1.y-center.y)/Length(p1,center)); a2 = asinf((p2.y-center.y)/Length(p2,center)); if ( p1.x < center.x ) a1 = PI-a1; if ( p2.x < center.x ) a2 = PI-a2; a = a2-a1; if ( a < 0 ) a += PI*2; return a; } // Returns py up on the line ab. float MidPoint(FPOINT a, FPOINT b, float px) { if ( Abs(a.x-b.x) < CHOUIA ) { if ( a.y < b.y ) return BEAUCOUP; else return -BEAUCOUP; } return (b.y-a.y)*(px-a.x)/(b.x-a.x)+a.y; } // Advance "dist" along the segment p1-p2. D3DVECTOR SegmentDist(const D3DVECTOR &p1, const D3DVECTOR &p2, float dist) { return p1+Normalize(p2-p1)*dist; } // Check if a point is inside a triangle. BOOL IsInsideTriangle(FPOINT a, FPOINT b, FPOINT c, FPOINT p) { float n, m; if ( p.x < a.x && p.x < b.x && p.x < c.x ) return FALSE; if ( p.x > a.x && p.x > b.x && p.x > c.x ) return FALSE; if ( p.y < a.y && p.y < b.y && p.y < c.y ) return FALSE; if ( p.y > a.y && p.y > b.y && p.y > c.y ) return FALSE; if ( a.x > b.x ) Swap(a,b); if ( a.x > c.x ) Swap(a,c); if ( c.x < a.x ) Swap(c,a); if ( c.x < b.x ) Swap(c,b); n = MidPoint(a, b, p.x); m = MidPoint(a, c, p.x); if ( (n>p.y||p.y>m) && (np.y||p.y>m) && (n= 100 ) break; } } sum.x = 0; sum.y = 0; sum.z = 0; for ( j=0 ; j 0.1f || Abs(n1.y-n2.y) > 0.1f || Abs(n1.z-n2.z) > 0.1f ) return FALSE; dist = DistancePlanPoint(plan1[0], plan1[1], plan1[2], plan2[0]); if ( dist > 0.1f ) return FALSE; return TRUE; } // Calculates the matrix to make three rotations in the X, Y and Z // >>>>>> OPTIMIZING!!! void MatRotateXZY(D3DMATRIX &mat, D3DVECTOR angle) { D3DMATRIX temp; D3DUtil_SetRotateXMatrix(temp, angle.x); D3DUtil_SetRotateZMatrix(mat, angle.z); D3DMath_MatrixMultiply(mat, mat, temp); D3DUtil_SetRotateYMatrix(temp, angle.y); D3DMath_MatrixMultiply(mat, mat, temp); // X-Z-Y } // Calculates the matrix to make three rotations in the order Z, X and Y. // >>>>>> OPTIMIZING!!! void MatRotateZXY(D3DMATRIX &mat, D3DVECTOR angle) { D3DMATRIX temp; D3DUtil_SetRotateZMatrix(temp, angle.z); D3DUtil_SetRotateXMatrix(mat, angle.x); D3DMath_MatrixMultiply(mat, mat, temp); D3DUtil_SetRotateYMatrix(temp, angle.y); D3DMath_MatrixMultiply(mat, mat, temp); // Z-X-Y } // Returns a random value between 0 and 1. float Rand() { return (float)rand()/RAND_MAX; } // Managing the dead zone of a joystick. // in: -1 0 1 // --|-------|----o----|-------|--> // <----> // dead // out: -1 0 0 1 float Neutral(float value, float dead) { if ( Abs(value) <= dead ) { return 0.0f; } else { if ( value > 0.0f ) return (value-dead)/(1.0f-dead); else return (value+dead)/(1.0f-dead); } } // Calculates a value (radians) proportional between a and b (degrees). float Prop(int a, int b, float p) { float aa, bb; aa = (float)a*PI/180.0f; bb = (float)b*PI/180.0f; return aa+p*(bb-aa); } // Gently advanced a desired value from its current value. // Over time, the greater the progression is rapid. float Smooth(float actual, float hope, float time) { float futur; futur = actual + (hope-actual)*time; if ( hope > actual ) { if ( futur > hope ) futur = hope; } if ( hope < actual ) { if ( futur < hope ) futur = hope; } return futur; } // Bounces any movement. // out // | // 1+------o-------o--- // | o | o o | | bounce // | o | o---|--- // | o | | // | o | | // -o------|-------+----> progress // 0| | 1 // |<---->|middle float Bounce(float progress, float middle, float bounce) { if ( progress < middle ) { progress = progress/middle; // 0..1 return 0.5f+sinf(progress*PI-PI/2.0f)/2.0f; } else { progress = (progress-middle)/(1.0f-middle); // 0..1 return (1.0f-bounce/2.0f)+sinf((0.5f+progress*2.0f)*PI)*(bounce/2.0f); } } // Returns the color corresponding D3DCOLOR. D3DCOLOR RetColor(float intensity) { D3DCOLOR color; if ( intensity <= 0.0f ) return 0x00000000; if ( intensity >= 1.0f ) return 0xffffffff; color = (int)(intensity*255.0f)<<24; color |= (int)(intensity*255.0f)<<16; color |= (int)(intensity*255.0f)<<8; color |= (int)(intensity*255.0f); return color; } // Returns the color corresponding D3DCOLOR. D3DCOLOR RetColor(D3DCOLORVALUE intensity) { D3DCOLOR color; color = (int)(intensity.a*255.0f)<<24; color |= (int)(intensity.r*255.0f)<<16; color |= (int)(intensity.g*255.0f)<<8; color |= (int)(intensity.b*255.0f); return color; } // Returns the color corresponding D3DCOLORVALUE. D3DCOLORVALUE RetColor(D3DCOLOR intensity) { D3DCOLORVALUE color; color.r = (float)((intensity>>16)&0xff)/256.0f; color.g = (float)((intensity>>8 )&0xff)/256.0f; color.b = (float)((intensity>>0 )&0xff)/256.0f; color.a = (float)((intensity>>24)&0xff)/256.0f; return color; } // RGB to HSV conversion. void RGB2HSV(D3DCOLORVALUE src, ColorHSV &dest) { float min, max, delta; min = Min(src.r, src.g, src.b); max = Max(src.r, src.g, src.b); dest.v = max; // intensity if ( max == 0.0f ) { dest.s = 0.0f; // saturation dest.h = 0.0f; // undefined color! } else { delta = max-min; dest.s = delta/max; // saturation if ( src.r == max ) // between yellow & magenta { dest.h = (src.g-src.b)/delta; } else if ( src.g == max ) // between cyan & yellow { dest.h = 2.0f+(src.b-src.r)/delta; } else // between magenta & cyan { dest.h = 4.0f+(src.r-src.g)/delta; } dest.h *= 60.0f; // in degrees if ( dest.h < 0.0f ) dest.h += 360.0f; dest.h /= 360.0f; // 0..1 } } // HSV to RGB conversion. void HSV2RGB(ColorHSV src, D3DCOLORVALUE &dest) { int i; float f,v,p,q,t; src.h = Norm(src.h)*360.0f; src.s = Norm(src.s); src.v = Norm(src.v); if ( src.s == 0.0f ) // zero saturation? { dest.r = src.v; dest.g = src.v; dest.b = src.v; // gray } else { if ( src.h == 360.0f ) src.h = 0.0f; src.h /= 60.0f; i = (int)src.h; // integer part (0 .. 5) f = src.h-i; // fractional part v = src.v; p = src.v*(1.0f-src.s); q = src.v*(1.0f-(src.s*f)); t = src.v*(1.0f-(src.s*(1.0f-f))); switch (i) { case 0: dest.r=v; dest.g=t; dest.b=p; break; case 1: dest.r=q; dest.g=v; dest.b=p; break; case 2: dest.r=p; dest.g=v; dest.b=t; break; case 3: dest.r=p; dest.g=q; dest.b=v; break; case 4: dest.r=t; dest.g=p; dest.b=v; break; case 5: dest.r=v; dest.g=p; dest.b=q; break; } } }