Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
/*
2
 * This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>.
3
 * It is copyright by its individual contributors, as recorded in the
4
 * project's Git history.  See COPYING.txt at the top level for license
5
 * terms and a link to the Git history.
6
 */
7
 
8
/*
9
 *
10
 * C version of fixed point library
11
 *
12
 */
13
 
14
#include <cstdint>
15
#include <stdlib.h>
16
#include <math.h>
17
 
18
#include "dxxerror.h"
19
#include "maths.h"
20
 
21
namespace dcx {
22
 
23
#define EPSILON (F1_0/100)
24
 
25
namespace {
26
 
27
class fix_sincos_input
28
{
29
public:
30
        const uint8_t m_idx;
31
        const signed m_mul;
32
        fix_sincos_input(fix a) :
33
                m_idx(static_cast<uint8_t>(a >> 8)),
34
                m_mul(static_cast<uint8_t>(a))
35
        {
36
        }
37
};
38
 
39
}
40
 
41
fix64 fixmul64(fix a, fix b)
42
{
43
        const fix64 a64 = a;
44
        const fix64 b64 = b;
45
        return (a64 * b64) / 65536;
46
}
47
 
48
fix fixdiv(fix a, fix b)
49
{
50
        if (!b)
51
                return 1;
52
        const fix64 a64 = a;
53
        return static_cast<fix>((a64 * 65536) / b);
54
}
55
 
56
fix fixmuldiv(fix a, fix b, fix c)
57
{
58
        if (!c)
59
                return 1;
60
        const fix64 a64 = a;
61
        return static_cast<fix>((a64 * b) / c);
62
}
63
 
64
//given cos & sin of an angle, return that angle.
65
//parms need not be normalized, that is, the ratio of the parms cos/sin must
66
//equal the ratio of the actual cos & sin for the result angle, but the parms 
67
//need not be the actual cos & sin.  
68
//NOTE: this is different from the standard C atan2, since it is left-handed.
69
 
70
fixang fix_atan2(fix cos,fix sin)
71
{
72
        fixang t;
73
 
74
        //Assert(!(cos==0 && sin==0));
75
 
76
        //find smaller of two
77
 
78
        const auto dsin = static_cast<double>(sin);
79
        const auto dcos = static_cast<double>(cos);
80
        double d;
81
        d = sqrt((dsin * dsin) + (dcos * dcos));
82
 
83
        if (d==0.0)
84
                return 0;
85
 
86
        if (labs(sin) < labs(cos)) {                            //sin is smaller, use arcsin
87
                t = fix_asin(static_cast<fix>((dsin / d) * 65536.0));
88
                if (cos<0)
89
                        t = 0x8000 - t;
90
                return t;
91
        }
92
        else {
93
                t = fix_acos(static_cast<fix>((dcos / d) * 65536.0));
94
                if (sin<0)
95
                        t = -t;
96
                return t;
97
        }
98
}
99
 
100
static unsigned int fixdivquadlongu(quadint n, uint64_t d)
101
{
102
        return n.q / d;
103
}
104
 
105
uint32_t quad_sqrt(const quadint iq)
106
{
107
        const uint32_t low = iq.low;
108
        const int32_t high = iq.high;
109
        int i, cnt;
110
        uint32_t r,old_r,t;
111
 
112
        if (high<0)
113
                return 0;
114
 
115
        if (high==0 && static_cast<int32_t>(low)>=0)
116
                return long_sqrt(static_cast<int32_t>(low));
117
 
118
        if ((i = high >> 24)) {
119
                cnt=12+16;
120
        }
121
        else if ((i = high >> 16))
122
        {
123
                cnt=8+16;
124
        }
125
        else if ((i = high >> 8))
126
        {
127
                cnt=4+16;
128
        } else {
129
                cnt=0+16; i = high;
130
        }
131
 
132
        r = guess_table[i]<<cnt;
133
 
134
        //quad loop usually executed 4 times
135
 
136
        r = fixdivquadlongu(iq,r)/2 + r/2;
137
        r = fixdivquadlongu(iq,r)/2 + r/2;
138
        r = fixdivquadlongu(iq,r)/2 + r/2;
139
 
140
        do {
141
 
142
                old_r = r;
143
                t = fixdivquadlongu(iq,r);
144
 
145
                if (t==r)       //got it!
146
                        return r;
147
 
148
                r = t/2 + r/2;
149
 
150
        } while (!(r==t || r==old_r));
151
 
152
        t = fixdivquadlongu(iq,r);
153
        quadint tq;
154
        //edited 05/17/99 Matt Mueller - tq.high is undefined here.. so set them to = 0
155
        tq.q = 0;
156
        //end edit -MM
157
        fixmulaccum(&tq,r,t);
158
        if (tq.q != iq.q)
159
                r++;
160
 
161
        return r;
162
}
163
 
164
//computes the square root of a long, returning a short
165
ushort long_sqrt(int32_t a)
166
{
167
        int cnt,r,old_r,t;
168
 
169
        if (a<=0)
170
                return 0;
171
 
172
        if (a & 0xff000000)
173
                cnt=12;
174
        else if (a & 0xff0000)
175
                cnt=8;
176
        else if (a & 0xff00)
177
                cnt=4;
178
        else
179
                cnt=0;
180
 
181
        r = guess_table[(a>>cnt)&0xff]<<cnt;
182
 
183
        //the loop nearly always executes 3 times, so we'll unroll it 2 times and
184
        //not do any checking until after the third time.  By my calcutations, the
185
        //loop is executed 2 times in 99.97% of cases, 3 times in 93.65% of cases, 
186
        //four times in 16.18% of cases, and five times in 0.44% of cases.  It never
187
        //executes more than five times.  By timing, I determined that is is faster
188
        //to always execute three times and not check for termination the first two
189
        //times through.  This means that in 93.65% of cases, we save 6 cmp/jcc pairs,
190
        //and in 6.35% of cases we do an extra divide.  In real life, these numbers
191
        //might not be the same.
192
 
193
        r = ((a/r)+r)/2;
194
        r = ((a/r)+r)/2;
195
 
196
        do {
197
 
198
                old_r = r;
199
                t = a/r;
200
 
201
                if (t==r)       //got it!
202
                        return r;
203
 
204
                r = (t+r)/2;
205
 
206
        } while (!(r==t || r==old_r));
207
 
208
        if (a % r)
209
                r++;
210
 
211
        return r;
212
}
213
 
214
//computes the square root of a fix, returning a fix
215
fix fix_sqrt(fix a)
216
{
217
        return static_cast<fix>(long_sqrt(a)) << 8;
218
}
219
 
220
static fix fix_sincos(const uint8_t idx0, const signed mul)
221
{
222
        const fix t0 = sincos_table[idx0];
223
        const uint8_t idx1 = idx0 + 1;
224
        const fix t1 = sincos_table[idx1];
225
        return (t0 + (((t1 - t0) * mul) >> 8)) << 2;
226
}
227
 
228
static fix fix_sin(const fix_sincos_input sci)
229
{
230
        return fix_sincos(sci.m_idx, sci.m_mul);
231
}
232
 
233
__attribute_warn_unused_result
234
static fix fix_cos(const fix_sincos_input sci)
235
{
236
        return fix_sincos(static_cast<uint8_t>(sci.m_idx + 64), sci.m_mul);
237
}
238
 
239
//compute sine and cosine of an angle, filling in the variables
240
//either of the pointers can be NULL
241
//with interpolation
242
fix_sincos_result fix_sincos(fix a)
243
{
244
        fix_sincos_input i(a);
245
        return {fix_sin(i), fix_cos(i)};
246
}
247
 
248
fix fix_sin(fix a)
249
{
250
        return fix_sin(fix_sincos_input{a});
251
}
252
 
253
fix fix_cos(fix a)
254
{
255
        return fix_cos(fix_sincos_input{a});
256
}
257
 
258
//compute sine and cosine of an angle, filling in the variables
259
//either of the pointers can be NULL
260
//no interpolation
261
fix fix_fastsin(fix a)
262
{
263
        const uint8_t i = static_cast<uint8_t>(a >> 8);
264
        return sincos_table[i] << 2;
265
}
266
 
267
//compute inverse sine
268
fixang fix_asin(fix v)
269
{
270
        fix vv;
271
        int i,f,aa;
272
 
273
        vv = labs(v);
274
 
275
        if (vv >= f1_0)         //check for out of range
276
                return 0x4000;
277
 
278
        i = (vv>>8)&0xff;
279
        f = vv&0xff;
280
 
281
        aa = asin_table[i];
282
        aa = aa + (((asin_table[i+1] - aa) * f)>>8);
283
 
284
        if (v < 0)
285
                aa = -aa;
286
 
287
        return aa;
288
}
289
 
290
//compute inverse cosine
291
fixang fix_acos(fix v)
292
{
293
        fix vv;
294
        int i,f,aa;
295
 
296
        vv = labs(v);
297
 
298
        if (vv >= f1_0)         //check for out of range
299
                return 0;
300
 
301
        i = (vv>>8)&0xff;
302
        f = vv&0xff;
303
 
304
        aa = acos_table[i];
305
        aa = aa + (((acos_table[i+1] - aa) * f)>>8);
306
 
307
        if (v < 0)
308
                aa = 0x8000 - aa;
309
 
310
        return aa;
311
}
312
 
313
}