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
 * Polygon object interpreter
11
 *
12
 */
13
 
14
#include <stdexcept>
15
#include <stdlib.h>
16
#include "dxxsconf.h"
17
#include "dsx-ns.h"
18
#include "dxxerror.h"
19
 
20
#include "interp.h"
21
#include "console.h"
22
#include "common/3d/globvars.h"
23
#include "polyobj.h"
24
#include "gr.h"
25
#include "byteutil.h"
26
#include "u_mem.h"
27
 
28
#include "compiler-range_for.h"
29
#include "d_range.h"
30
#include "d_zip.h"
31
#include "partial_range.h"
32
 
33
namespace dcx {
34
 
35
constexpr std::integral_constant<unsigned, 0> OP_EOF{};   //eof
36
constexpr std::integral_constant<unsigned, 1> OP_DEFPOINTS{};   //defpoints
37
constexpr std::integral_constant<unsigned, 2> OP_FLATPOLY{};   //flat-shaded polygon
38
constexpr std::integral_constant<unsigned, 3> OP_TMAPPOLY{};   //texture-mapped polygon
39
constexpr std::integral_constant<unsigned, 4> OP_SORTNORM{};   //sort by normal
40
constexpr std::integral_constant<unsigned, 5> OP_RODBM{};   //rod bitmap
41
constexpr std::integral_constant<unsigned, 6> OP_SUBCALL{};   //call a subobject
42
constexpr std::integral_constant<unsigned, 7> OP_DEFP_START{};   //defpoints with start
43
constexpr std::integral_constant<unsigned, 8> OP_GLOW{};   //glow value for next poly
44
 
45
#if DXX_USE_EDITOR
46
int g3d_interp_outline;
47
#endif
48
 
49
}
50
 
51
namespace dsx {
52
 
53
static int16_t init_model_sub(uint8_t *model_sub_ptr, const uint8_t *model_base_ptr, std::size_t model_size);
54
#if defined(DXX_BUILD_DESCENT_I)
55
static void validate_model_sub(uint8_t *model_sub_ptr, const uint8_t *model_base_ptr, std::size_t model_size);
56
#endif
57
 
58
static inline int16_t *wp(uint8_t *p)
59
{
60
        return reinterpret_cast<int16_t *>(p);
61
}
62
 
63
static inline const int16_t *wp(const uint8_t *p)
64
{
65
        return reinterpret_cast<const int16_t *>(p);
66
}
67
 
68
static inline const vms_vector *vp(const uint8_t *p)
69
{
70
        return reinterpret_cast<const vms_vector *>(p);
71
}
72
 
73
static inline int16_t w(const uint8_t *p)
74
{
75
        return *wp(p);
76
}
77
 
78
static void rotate_point_list(const zip<g3s_point * /* dest */, const vms_vector * /* src */> zr)
79
{
80
        range_for (auto &&z, zr)
81
        {
82
                auto &dest = std::get<0>(z);
83
                auto &src = std::get<1>(z);
84
                g3_rotate_point(dest, src);
85
        }
86
}
87
 
88
constexpr vms_angvec zero_angles = {0,0,0};
89
 
90
namespace {
91
 
92
class interpreter_ignore_op_defpoints
93
{
94
public:
95
        static void op_defpoints(const uint8_t *, uint16_t)
96
        {
97
        }
98
};
99
 
100
class interpreter_ignore_op_defp_start
101
{
102
public:
103
        static void op_defp_start(const uint8_t *, uint16_t)
104
        {
105
        }
106
};
107
 
108
class interpreter_ignore_op_flatpoly
109
{
110
public:
111
        static void op_flatpoly(const uint8_t *, uint16_t)
112
        {
113
        }
114
};
115
 
116
class interpreter_ignore_op_tmappoly
117
{
118
public:
119
        static void op_tmappoly(const uint8_t *, uint16_t)
120
        {
121
        }
122
};
123
 
124
class interpreter_ignore_op_rodbm
125
{
126
public:
127
        static void op_rodbm(const uint8_t *)
128
        {
129
        }
130
};
131
 
132
class interpreter_ignore_op_glow
133
{
134
public:
135
        static void op_glow(const uint8_t *)
136
        {
137
        }
138
};
139
 
140
class interpreter_track_model_extent
141
{
142
protected:
143
        const uint8_t *const model_base;
144
        const std::size_t model_length;
145
public:
146
        constexpr interpreter_track_model_extent(const uint8_t *const b, const std::size_t l) :
147
                model_base(b), model_length(l)
148
        {
149
        }
150
        uint8_t truncate_invalid_model(const unsigned line, uint8_t *const p, const std::ptrdiff_t d, const std::size_t size) const
151
        {
152
                const std::ptrdiff_t offset_from_base = p - model_base;
153
                const std::ptrdiff_t offset_of_value = offset_from_base + d;
154
                if (offset_of_value >= model_length || offset_of_value + size >= model_length)
155
                {
156
                        auto &opref = *wp(p);
157
                        const auto badop = opref;
158
                        opref = OP_EOF;
159
                        const unsigned long uml = model_length;
160
                        const long ofb = offset_from_base;
161
                        const long oov = offset_of_value;
162
                        con_printf(CON_URGENT, "%s:%u: warning: invalid polymodel at %p with length %lu; opcode %u at offset %li references invalid offset %li; replacing invalid operation with EOF", __FILE__, line, model_base, uml, badop, ofb, oov);
163
                        return 1;
164
                }
165
                return 0;
166
        }
167
};
168
 
169
class interpreter_base
170
{
171
public:
172
        static uint16_t get_raw_opcode(const uint8_t *const p)
173
        {
174
                return w(p);
175
        }
176
        static uint_fast32_t translate_opcode(const uint8_t *, const uint16_t op)
177
        {
178
                return op;
179
        }
180
        static uint16_t get_op_subcount(const uint8_t *const p)
181
        {
182
                return w(p + 2);
183
        }
184
        __attribute_cold
185
        static void op_default(const unsigned op, const uint8_t *const p)
186
        {
187
                char buf[64];
188
                snprintf(buf, sizeof(buf), "invalid polygon model opcode %u at %p", op, p);
189
                throw std::runtime_error(buf);
190
        }
191
};
192
 
193
class g3_poly_get_color_state :
194
        public interpreter_ignore_op_defpoints,
195
        public interpreter_ignore_op_defp_start,
196
        public interpreter_ignore_op_tmappoly,
197
        public interpreter_ignore_op_rodbm,
198
        public interpreter_ignore_op_glow,
199
        public interpreter_base
200
{
201
public:
202
        int color = 0;
203
        void op_flatpoly(const uint8_t *const p, const uint_fast32_t nv)
204
        {
205
                if (nv > MAX_POINTS_PER_POLY)
206
                        return;
207
                if (g3_check_normal_facing(*vp(p+4),*vp(p+16)) > 0) {
208
#if defined(DXX_BUILD_DESCENT_I)
209
                        color = (w(p+28));
210
#elif defined(DXX_BUILD_DESCENT_II)
211
                        color = gr_find_closest_color_15bpp(w(p + 28));
212
#endif
213
                }
214
        }
215
        void op_sortnorm(const uint8_t *const p)
216
        {
217
                const bool facing = g3_check_normal_facing(*vp(p+16),*vp(p+4)) > 0;
218
                color = g3_poly_get_color(facing ? p + w(p + 28) : p + w(p + 30));
219
        }
220
        void op_subcall(const uint8_t *const p)
221
        {
222
#if defined(DXX_BUILD_DESCENT_I)
223
                color = g3_poly_get_color(p+w(p+16));
224
#elif defined(DXX_BUILD_DESCENT_II)
225
                (void)p;
226
#endif
227
        }
228
};
229
 
230
class g3_interpreter_draw_base
231
{
232
protected:
233
        grs_bitmap *const *const model_bitmaps;
234
        polygon_model_points &Interp_point_list;
235
        grs_canvas &canvas;
236
        const submodel_angles anim_angles;
237
        const g3s_lrgb model_light;
238
private:
239
        void rotate(uint_fast32_t i, const vms_vector *const src, const uint_fast32_t n)
240
        {
241
                rotate_point_list(zip(partial_range(Interp_point_list, i, i + n), unchecked_partial_range(src, n)));
242
        }
243
        void set_color_by_model_light(fix g3s_lrgb::*const c, g3s_lrgb &o, const fix color) const
244
        {
245
                o.*c = fixmul(color, model_light.*c);
246
        }
247
protected:
248
        template <std::size_t N>
249
                std::array<cg3s_point *, N> prepare_point_list(const uint_fast32_t nv, const uint8_t *const p)
250
                {
251
                        std::array<cg3s_point *, N> point_list;
252
                        for (uint_fast32_t i = 0; i < nv; ++i)
253
                                point_list[i] = &Interp_point_list[wp(p + 30)[i]];
254
                        return point_list;
255
                }
256
        g3s_lrgb get_noglow_light(const uint8_t *const p) const
257
        {
258
                g3s_lrgb light;
259
                const auto negdot = -vm_vec_dot(View_matrix.fvec, *vp(p + 16));
260
                const auto color = (f1_0 / 4) + ((negdot * 3) / 4);
261
                set_color_by_model_light(&g3s_lrgb::r, light, color);
262
                set_color_by_model_light(&g3s_lrgb::g, light, color);
263
                set_color_by_model_light(&g3s_lrgb::b, light, color);
264
                return light;
265
        }
266
        g3_interpreter_draw_base(grs_bitmap *const *const mbitmaps, polygon_model_points &plist, grs_canvas &ccanvas, const submodel_angles aangles, const g3s_lrgb &mlight) :
267
                model_bitmaps(mbitmaps), Interp_point_list(plist),
268
                canvas(ccanvas),
269
                anim_angles(aangles), model_light(mlight)
270
        {
271
        }
272
        void op_defpoints(const vms_vector *const src, const uint_fast32_t n)
273
        {
274
                rotate(0, src, n);
275
        }
276
        void op_defp_start(const uint8_t *const p, const vms_vector *const src, const uint_fast32_t n)
277
        {
278
                rotate(static_cast<int>(w(p + 4)), src, n);
279
        }
280
        static std::pair<uint16_t, uint16_t> get_sortnorm_offsets(const uint8_t *const p)
281
        {
282
                const uint16_t a = w(p + 30), b = w(p + 28);
283
                return (g3_check_normal_facing(*vp(p + 16), *vp(p + 4)) > 0)
284
                        ? std::make_pair(a, b)  //draw back then front
285
                        : std::make_pair(b, a)  //not facing.  draw front then back
286
                ;
287
        }
288
        void op_rodbm(const uint8_t *const p)
289
        {
290
                const auto &&rod_bot_p = g3_rotate_point(*vp(p + 20));
291
                const auto &&rod_top_p = g3_rotate_point(*vp(p + 4));
292
                const g3s_lrgb rodbm_light{
293
                        f1_0, f1_0, f1_0
294
                };
295
                g3_draw_rod_tmap(canvas, *model_bitmaps[w(p + 2)], rod_bot_p, w(p + 16), rod_top_p, w(p + 32), rodbm_light);
296
        }
297
        void op_subcall(const uint8_t *const p, const glow_values_t *const glow_values)
298
        {
299
                g3_start_instance_angles(*vp(p + 4), anim_angles ? anim_angles[w(p + 2)] : zero_angles);
300
                g3_draw_polygon_model(model_bitmaps, Interp_point_list, canvas, anim_angles, model_light, glow_values, p + w(p + 16));
301
                g3_done_instance();
302
        }
303
};
304
 
305
class g3_draw_polygon_model_state :
306
        public interpreter_base,
307
        g3_interpreter_draw_base
308
{
309
        const glow_values_t *const glow_values;
310
        unsigned glow_num = ~0u;                //glow off by default
311
public:
312
        g3_draw_polygon_model_state(grs_bitmap *const *const mbitmaps, polygon_model_points &plist, grs_canvas &ccanvas, const submodel_angles aangles, const g3s_lrgb &mlight, const glow_values_t *const glvalues) :
313
                g3_interpreter_draw_base(mbitmaps, plist, ccanvas, aangles, mlight),
314
                glow_values(glvalues)
315
        {
316
        }
317
        void op_defpoints(const uint8_t *const p, const uint_fast32_t n)
318
        {
319
                g3_interpreter_draw_base::op_defpoints(vp(p + 4), n);
320
        }
321
        void op_defp_start(const uint8_t *const p, const uint_fast32_t n)
322
        {
323
                g3_interpreter_draw_base::op_defp_start(p, vp(p + 8), n);
324
        }
325
        void op_flatpoly(const uint8_t *const p, const uint_fast32_t nv)
326
        {
327
                if (nv > MAX_POINTS_PER_POLY)
328
                        return;
329
                if (g3_check_normal_facing(*vp(p+4),*vp(p+16)) > 0)
330
                {
331
#if defined(DXX_BUILD_DESCENT_II)
332
                        if (!glow_values || !(glow_num < glow_values->size()) || (*glow_values)[glow_num] != -3)
333
#endif
334
                        {
335
                                //                                      DPH: Now we treat this color as 15bpp
336
#if defined(DXX_BUILD_DESCENT_I)
337
                                const uint8_t color = w(p + 28);
338
#elif defined(DXX_BUILD_DESCENT_II)
339
                                const uint8_t color = (glow_values && glow_num < glow_values->size() && (*glow_values)[glow_num] == -2)
340
                                        ? 255
341
                                        : gr_find_closest_color_15bpp(w(p + 28));
342
#endif
343
                                const auto point_list = prepare_point_list<MAX_POINTS_PER_POLY>(nv, p);
344
                                g3_draw_poly(*grd_curcanv, nv, point_list, color);
345
                        }
346
                }
347
        }
348
        static g3s_lrgb get_glow_light(const fix c)
349
        {
350
                return {c, c, c};
351
        }
352
        void op_tmappoly(const uint8_t *const p, const uint_fast32_t nv)
353
        {
354
                if (nv > MAX_POINTS_PER_POLY)
355
                        return;
356
                if (!(g3_check_normal_facing(*vp(p+4),*vp(p+16)) > 0))
357
                        return;
358
                //calculate light from surface normal
359
                const auto &&light = (glow_values && glow_num < glow_values->size())
360
                        ? get_glow_light((*glow_values)[std::exchange(glow_num, -1)]) //yes glow
361
                        : get_noglow_light(p); //no glow
362
                //now poke light into l values
363
                std::array<g3s_uvl, MAX_POINTS_PER_POLY> uvl_list;
364
                std::array<g3s_lrgb, MAX_POINTS_PER_POLY> lrgb_list;
365
                const fix average_light = (light.r + light.g + light.b) / 3;
366
                range_for (const uint_fast32_t i, xrange(nv))
367
                {
368
                        lrgb_list[i] = light;
369
                        uvl_list[i] = (reinterpret_cast<const g3s_uvl *>(p+30+((nv&~1)+1)*2))[i];
370
                        uvl_list[i].l = average_light;
371
                }
372
                const auto point_list = prepare_point_list<MAX_POINTS_PER_POLY>(nv, p);
373
                g3_draw_tmap(canvas, nv, point_list, uvl_list, lrgb_list, *model_bitmaps[w(p + 28)]);
374
        }
375
        void op_sortnorm(const uint8_t *const p)
376
        {
377
                const auto &&offsets = get_sortnorm_offsets(p);
378
                const auto a = offsets.first;
379
                const auto b = offsets.second;
380
                g3_draw_polygon_model(model_bitmaps, Interp_point_list, canvas, anim_angles, model_light, glow_values, p + a);
381
                g3_draw_polygon_model(model_bitmaps, Interp_point_list, canvas, anim_angles, model_light, glow_values, p + b);
382
        }
383
        using g3_interpreter_draw_base::op_rodbm;
384
        void op_subcall(const uint8_t *const p)
385
        {
386
                g3_interpreter_draw_base::op_subcall(p, glow_values);
387
        }
388
        void op_glow(const uint8_t *const p)
389
        {
390
                glow_num = w(p+2);
391
        }
392
};
393
 
394
class g3_draw_morphing_model_state :
395
        public interpreter_ignore_op_glow,
396
        public interpreter_base,
397
        g3_interpreter_draw_base
398
{
399
        const vms_vector *const new_points;
400
        static constexpr const glow_values_t *glow_values = nullptr;
401
public:
402
        g3_draw_morphing_model_state(grs_bitmap *const *const mbitmaps, polygon_model_points &plist, grs_canvas &ccanvas, const submodel_angles aangles, const g3s_lrgb &mlight, const vms_vector *const npoints) :
403
                g3_interpreter_draw_base(mbitmaps, plist, ccanvas, aangles, mlight),
404
                new_points(npoints)
405
        {
406
        }
407
        void op_defpoints(const uint8_t *, const uint_fast32_t n)
408
        {
409
                g3_interpreter_draw_base::op_defpoints(new_points, n);
410
        }
411
        void op_defp_start(const uint8_t *const p, const uint_fast32_t n)
412
        {
413
                g3_interpreter_draw_base::op_defp_start(p, new_points, n);
414
        }
415
        void op_flatpoly(const uint8_t *const p, const uint_fast32_t nv)
416
        {
417
                int ntris;
418
                const uint8_t color = w(p+28);
419
                unsigned i;
420
                auto point_list = prepare_point_list<3>(i = 2, p);
421
                for (ntris=nv-2;ntris;ntris--) {
422
                        point_list[2] = &Interp_point_list[wp(p+30)[i++]];
423
                        g3_check_and_draw_poly(canvas, point_list, color);
424
                        point_list[1] = point_list[2];
425
                }
426
        }
427
        void op_tmappoly(const uint8_t *const p, const uint_fast32_t nv)
428
        {
429
                if (nv > MAX_POINTS_PER_POLY)
430
                        return;
431
                std::array<g3s_uvl, MAX_POINTS_PER_POLY> uvl_list;
432
                std::array<g3s_lrgb, MAX_POINTS_PER_POLY> lrgb_list;
433
                lrgb_list.fill(get_noglow_light(p));
434
                range_for (const uint_fast32_t i, xrange(nv))
435
                        uvl_list[i] = (reinterpret_cast<const g3s_uvl *>(p+30+((nv&~1)+1)*2))[i];
436
                const auto point_list = prepare_point_list<MAX_POINTS_PER_POLY>(nv, p);
437
                g3_draw_tmap(canvas, nv, point_list, uvl_list, lrgb_list, *model_bitmaps[w(p + 28)]);
438
        }
439
        void op_sortnorm(const uint8_t *const p)
440
        {
441
                const auto &&offsets = get_sortnorm_offsets(p);
442
                auto &a = offsets.first;
443
                auto &b = offsets.second;
444
                g3_draw_morphing_model(canvas, p + a, model_bitmaps, anim_angles, model_light, new_points, Interp_point_list);
445
                g3_draw_morphing_model(canvas, p + b, model_bitmaps, anim_angles, model_light, new_points, Interp_point_list);
446
        }
447
        using g3_interpreter_draw_base::op_rodbm;
448
        void op_subcall(const uint8_t *const p)
449
        {
450
                g3_interpreter_draw_base::op_subcall(p, glow_values);
451
        }
452
};
453
 
454
template <typename T>
455
class model_load_state :
456
        public interpreter_track_model_extent,
457
        public interpreter_ignore_op_defpoints,
458
        public interpreter_ignore_op_defp_start,
459
        public interpreter_ignore_op_rodbm,
460
        public interpreter_ignore_op_glow,
461
        public interpreter_base
462
{
463
public:
464
        using interpreter_track_model_extent::interpreter_track_model_extent;
465
        int16_t init_bounded_model_sub(const unsigned line, uint8_t *const p, const std::ptrdiff_t d) const
466
        {
467
                if (truncate_invalid_model(line, p, d, sizeof(uint16_t)))
468
                        return 0;
469
                return static_cast<const T *>(this)->init_sub_model(p + d);
470
        }
471
        void op_tmappoly(uint8_t *const p, const uint_fast32_t nv)
472
        {
473
                constexpr unsigned offset_texture = 28;
474
                (void)nv;
475
                Assert(nv > 2);         //must have 3 or more points
476
                if (truncate_invalid_model(__LINE__, p, offset_texture, sizeof(uint16_t)))
477
                        return;
478
                static_cast<T *>(this)->update_texture(w(p + offset_texture));
479
        }
480
        void op_sortnorm(uint8_t *const p)
481
        {
482
                constexpr unsigned offset_submodel0 = 28;
483
                constexpr unsigned offset_submodel1 = offset_submodel0 + sizeof(uint16_t);
484
                if (truncate_invalid_model(__LINE__, p, offset_submodel1, sizeof(uint16_t)))
485
                        return;
486
                const auto n0 = w(p + offset_submodel0);
487
                const auto h0 = init_bounded_model_sub(__LINE__, p, n0);
488
                const auto n1 = w(p + offset_submodel1);
489
                const auto h1 = init_bounded_model_sub(__LINE__, p, n1);
490
                const auto hm = std::max(h0, h1);
491
                static_cast<T *>(this)->update_texture(hm);
492
        }
493
        void op_subcall(uint8_t *const p)
494
        {
495
                constexpr unsigned offset_displacement = 16;
496
                if (truncate_invalid_model(__LINE__, p, offset_displacement, sizeof(uint16_t)))
497
                        return;
498
                const auto n0 = w(p + offset_displacement);
499
                const auto h0 = init_bounded_model_sub(__LINE__, p, n0);
500
                static_cast<T *>(this)->update_texture(h0);
501
        }
502
};
503
 
504
class init_model_sub_state :
505
        public model_load_state<init_model_sub_state>
506
#if defined(DXX_BUILD_DESCENT_II)
507
        , public interpreter_ignore_op_flatpoly
508
#endif
509
{
510
public:
511
        int16_t highest_texture_num = -1;
512
        using model_load_state::model_load_state;
513
        int16_t init_sub_model(uint8_t *const p) const
514
        {
515
                return init_model_sub(p, model_base, model_length);
516
        }
517
        void update_texture(const int16_t t)
518
        {
519
                if (highest_texture_num < t)
520
                        highest_texture_num = t;
521
        }
522
#if defined(DXX_BUILD_DESCENT_I)
523
        void op_flatpoly(uint8_t *const p, const uint_fast32_t nv) const
524
        {
525
                //must have 3 or more points
526
                if (nv <= 2)
527
                        return;
528
                const auto p16 = wp(p + 28);
529
                *p16 = static_cast<short>(gr_find_closest_color_15bpp(*p16));
530
        }
531
#endif
532
};
533
 
534
#if defined(DXX_BUILD_DESCENT_I)
535
class validate_model_sub_state :
536
        public model_load_state<validate_model_sub_state>,
537
        public interpreter_ignore_op_flatpoly
538
{
539
public:
540
        using model_load_state::model_load_state;
541
        unsigned init_sub_model(uint8_t *const p) const
542
        {
543
                validate_model_sub(p, model_base, model_length);
544
                return 0;
545
        }
546
        void update_texture(int16_t)
547
        {
548
        }
549
};
550
#endif
551
 
552
constexpr const glow_values_t *g3_draw_morphing_model_state::glow_values;
553
 
554
}
555
 
556
template <typename P, typename State>
557
static std::size_t dispatch_polymodel_op(const P p, State &state, const uint_fast32_t op)
558
{
559
        switch (op)
560
        {
561
                case OP_DEFPOINTS: {
562
                        const auto n = state.get_op_subcount(p);
563
                        const std::size_t record_size = n * sizeof(vms_vector) + 4;
564
                        state.op_defpoints(p, n);
565
                        return record_size;
566
                }
567
                case OP_DEFP_START: {
568
                        const auto n = state.get_op_subcount(p);
569
                        const std::size_t record_size = n * sizeof(vms_vector) + 8;
570
                        state.op_defp_start(p, n);
571
                        return record_size;
572
                }
573
                case OP_FLATPOLY: {
574
                        const auto n = state.get_op_subcount(p);
575
                        const std::size_t record_size = 30 + ((n & ~1) + 1) * 2;
576
                        state.op_flatpoly(p, n);
577
                        return record_size;
578
                }
579
                case OP_TMAPPOLY: {
580
                        const auto n = state.get_op_subcount(p);
581
                        const std::size_t record_size = 30 + ((n & ~1) + 1) * 2 + n * 12;
582
                        state.op_tmappoly(p, n);
583
                        return record_size;
584
                }
585
                case OP_SORTNORM: {
586
                        const std::size_t record_size = 32;
587
                        state.op_sortnorm(p);
588
                        return record_size;
589
                }
590
                case OP_RODBM: {
591
                        const std::size_t record_size = 36;
592
                        state.op_rodbm(p);
593
                        return record_size;
594
                }
595
                case OP_SUBCALL: {
596
                        const std::size_t record_size = 20;
597
                        state.op_subcall(p);
598
                        return record_size;
599
                }
600
                case OP_GLOW: {
601
                        const std::size_t record_size = 4;
602
                        state.op_glow(p);
603
                        return record_size;
604
                }
605
                default:
606
                        state.op_default(op, p);
607
                        return 2;
608
        }
609
}
610
 
611
template <typename P, typename State>
612
static P iterate_polymodel(P p, State &state)
613
{
614
        for (uint16_t op; (op = state.get_raw_opcode(p)) != OP_EOF;)
615
                p += dispatch_polymodel_op(p, state, state.translate_opcode(p, op));
616
        return p;
617
}
618
 
619
}
620
 
621
#if DXX_WORDS_BIGENDIAN
622
namespace dcx {
623
 
624
static inline fix *fp(uint8_t *p)
625
{
626
        return reinterpret_cast<fix *>(p);
627
}
628
 
629
static inline vms_vector *vp(uint8_t *p)
630
{
631
        return reinterpret_cast<vms_vector *>(p);
632
}
633
 
634
static void short_swap(short *s)
635
{
636
        *s = SWAPSHORT(*s);
637
}
638
 
639
static void fix_swap(fix &f)
640
{
641
        f = SWAPINT(f);
642
}
643
 
644
static void fix_swap(fix *f)
645
{
646
        fix_swap(*f);
647
}
648
 
649
static void vms_vector_swap(vms_vector &v)
650
{
651
        fix_swap(v.x);
652
        fix_swap(v.y);
653
        fix_swap(v.z);
654
}
655
 
656
namespace {
657
 
658
class swap_polygon_model_data_state : public interpreter_base
659
{
660
public:
661
        static uint_fast32_t translate_opcode(uint8_t *const p, const uint16_t op)
662
        {
663
                return *wp(p) = INTEL_SHORT(op);
664
        }
665
        static uint16_t get_op_subcount(const uint8_t *const p)
666
        {
667
                return SWAPSHORT(w(p + 2));
668
        }
669
        static void op_defpoints(uint8_t *const p, const uint_fast32_t n)
670
        {
671
                *wp(p + 2) = n;
672
                range_for (const uint_fast32_t i, xrange(n))
673
                        vms_vector_swap(*vp((p + 4) + (i * sizeof(vms_vector))));
674
        }
675
        static void op_defp_start(uint8_t *const p, const uint_fast32_t n)
676
        {
677
                *wp(p + 2) = n;
678
                short_swap(wp(p + 4));
679
                range_for (const uint_fast32_t i, xrange(n))
680
                        vms_vector_swap(*vp((p + 8) + (i * sizeof(vms_vector))));
681
        }
682
        static void op_flatpoly(uint8_t *const p, const uint_fast32_t n)
683
        {
684
                *wp(p + 2) = n;
685
                vms_vector_swap(*vp(p + 4));
686
                vms_vector_swap(*vp(p + 16));
687
                short_swap(wp(p+28));
688
                for (uint_fast32_t i = 0; i < n; ++i)
689
                        short_swap(wp(p + 30 + (i * 2)));
690
        }
691
        static void op_tmappoly(uint8_t *const p, const uint_fast32_t n)
692
        {
693
                *wp(p + 2) = n;
694
                vms_vector_swap(*vp(p + 4));
695
                vms_vector_swap(*vp(p + 16));
696
                range_for (const uint_fast32_t i, xrange(n)) {
697
                        const auto uvl_val = reinterpret_cast<g3s_uvl *>((p+30+((n&~1)+1)*2) + (i * sizeof(g3s_uvl)));
698
                        fix_swap(&uvl_val->u);
699
                        fix_swap(&uvl_val->v);
700
                }
701
                short_swap(wp(p+28));
702
                range_for (const uint_fast32_t i, xrange(n))
703
                        short_swap(wp(p + 30 + (i * 2)));
704
        }
705
        void op_sortnorm(uint8_t *const p)
706
        {
707
                vms_vector_swap(*vp(p + 4));
708
                vms_vector_swap(*vp(p + 16));
709
                short_swap(wp(p + 28));
710
                short_swap(wp(p + 30));
711
                swap_polygon_model_data(p + w(p+28));
712
                swap_polygon_model_data(p + w(p+30));
713
        }
714
        static void op_rodbm(uint8_t *const p)
715
        {
716
                vms_vector_swap(*vp(p + 20));
717
                vms_vector_swap(*vp(p + 4));
718
                short_swap(wp(p+2));
719
                fix_swap(fp(p + 16));
720
                fix_swap(fp(p + 32));
721
        }
722
        void op_subcall(uint8_t *const p)
723
        {
724
                short_swap(wp(p+2));
725
                vms_vector_swap(*vp(p+4));
726
                short_swap(wp(p+16));
727
                swap_polygon_model_data(p + w(p+16));
728
        }
729
        static void op_glow(uint8_t *const p)
730
        {
731
                short_swap(wp(p + 2));
732
        }
733
};
734
 
735
}
736
 
737
void swap_polygon_model_data(ubyte *data)
738
{
739
        swap_polygon_model_data_state state;
740
        iterate_polymodel(data, state);
741
}
742
 
743
}
744
#endif
745
 
746
#if DXX_WORDS_NEED_ALIGNMENT
747
 
748
#if DXX_WORDS_NEED_ALIGNMENT
749
#define MAX_CHUNKS 100 // increase if insufficent
750
#endif //def WORDS_NEED_ALIGNMENT
751
namespace dcx {
752
 
753
namespace {
754
 
755
/*
756
 * A chunk struct (as used for alignment) contains all relevant data
757
 * concerning a piece of data that may need to be aligned.
758
 * To align it, we need to copy it to an aligned position,
759
 * and update all pointers  to it.
760
 * (Those pointers are actually offsets
761
 * relative to start of model_data) to it.
762
 */
763
struct chunk
764
{
765
        const uint8_t *old_base; // where the offset sets off from (relative to beginning of model_data)
766
        uint8_t *new_base; // where the base is in the aligned structure
767
        short offset; // how much to add to base to get the address of the offset
768
        short correction; // how much the value of the offset must be shifted for alignment
769
};
770
 
771
static void add_chunk(const uint8_t *old_base, uint8_t *new_base, int offset,
772
               chunk *chunk_list, int *no_chunks)
773
{
774
        Assert(*no_chunks + 1 < MAX_CHUNKS); //increase MAX_CHUNKS if you get this
775
        chunk_list[*no_chunks].old_base = old_base;
776
        chunk_list[*no_chunks].new_base = new_base;
777
        chunk_list[*no_chunks].offset = offset;
778
        chunk_list[*no_chunks].correction = 0;
779
        (*no_chunks)++;
780
}
781
 
782
class get_chunks_state :
783
        public interpreter_ignore_op_defpoints,
784
        public interpreter_ignore_op_defp_start,
785
        public interpreter_ignore_op_flatpoly,
786
        public interpreter_ignore_op_tmappoly,
787
        public interpreter_ignore_op_rodbm,
788
        public interpreter_ignore_op_glow,
789
        public interpreter_base
790
{
791
        const uint8_t *const data;
792
        uint8_t *const new_data;
793
        chunk *const list;
794
        int *const no;
795
public:
796
        get_chunks_state(const uint8_t *const p, uint8_t *const ndata, chunk *const l, int *const n) :
797
                data(p), new_data(ndata), list(l), no(n)
798
        {
799
        }
800
        static uint_fast32_t translate_opcode(const uint8_t *, const uint16_t op)
801
        {
802
                return INTEL_SHORT(op);
803
        }
804
        static uint16_t get_op_subcount(const uint8_t *const p)
805
        {
806
                return GET_INTEL_SHORT(p + 2);
807
        }
808
        void op_sortnorm(const uint8_t *const p)
809
        {
810
                add_chunk(p, p - data + new_data, 28, list, no);
811
                add_chunk(p, p - data + new_data, 30, list, no);
812
        }
813
        void op_subcall(const uint8_t *const p)
814
        {
815
                add_chunk(p, p - data + new_data, 16, list, no);
816
        }
817
};
818
 
819
/*
820
 * finds what chunks the data points to, adds them to the chunk_list,
821
 * and returns the length of the current chunk
822
 */
823
int get_chunks(const uint8_t *data, uint8_t *new_data, chunk *list, int *no)
824
{
825
        get_chunks_state state(data, new_data, list, no);
826
        auto p = iterate_polymodel(data, state);
827
        return p + 2 - data;
828
}
829
 
830
static const uint8_t *old_dest(const chunk &o) // return where chunk is (in unaligned struct)
831
{
832
        return GET_INTEL_SHORT(&o.old_base[o.offset]) + o.old_base;
833
}
834
static uint8_t *new_dest(const chunk &o) // return where chunk is (in aligned struct)
835
{
836
        return GET_INTEL_SHORT(&o.old_base[o.offset]) + o.new_base + o.correction;
837
}
838
 
839
/*
840
 * find chunk with smallest address
841
 */
842
static int get_first_chunks_index(chunk *chunk_list, int no_chunks)
843
{
844
        int first_index = 0;
845
        Assert(no_chunks >= 1);
846
        for (int i = 1; i < no_chunks; i++)
847
                if (old_dest(chunk_list[i]) < old_dest(chunk_list[first_index]))
848
                        first_index = i;
849
        return first_index;
850
}
851
 
852
}
853
 
854
void align_polygon_model_data(polymodel *pm)
855
{
856
        int chunk_len;
857
        int total_correction = 0;
858
        chunk cur_ch;
859
        chunk ch_list[MAX_CHUNKS];
860
        int no_chunks = 0;
861
        constexpr unsigned SHIFT_SPACE = 500;   // increase if insufficient
862
        int tmp_size = pm->model_data_size + SHIFT_SPACE;
863
        RAIIdmem<uint8_t[]> tmp;
864
        MALLOC(tmp, uint8_t[], tmp_size); // where we build the aligned version of pm->model_data
865
 
866
        Assert(tmp != NULL);
867
        //start with first chunk (is always aligned!)
868
        const uint8_t *cur_old = pm->model_data.get();
869
        auto cur_new = tmp.get();
870
        chunk_len = get_chunks(cur_old, cur_new, ch_list, &no_chunks);
871
        memcpy(cur_new, cur_old, chunk_len);
872
        while (no_chunks > 0) {
873
                int first_index = get_first_chunks_index(ch_list, no_chunks);
874
                cur_ch = ch_list[first_index];
875
                // remove first chunk from array:
876
                no_chunks--;
877
                for (int i = first_index; i < no_chunks; i++)
878
                        ch_list[i] = ch_list[i + 1];
879
                // if (new) address unaligned:
880
                const uintptr_t u = reinterpret_cast<uintptr_t>(new_dest(cur_ch));
881
                if (u % 4L != 0) {
882
                        // calculate how much to move to be aligned
883
                        short to_shift = 4 - u % 4L;
884
                        // correct chunks' addresses
885
                        cur_ch.correction += to_shift;
886
                        for (int i = 0; i < no_chunks; i++)
887
                                ch_list[i].correction += to_shift;
888
                        total_correction += to_shift;
889
                        Assert(reinterpret_cast<uintptr_t>(new_dest(cur_ch)) % 4L == 0);
890
                        Assert(total_correction <= SHIFT_SPACE); // if you get this, increase SHIFT_SPACE
891
                }
892
                //write (corrected) chunk for current chunk:
893
                *(reinterpret_cast<short *>(cur_ch.new_base + cur_ch.offset))
894
                        = INTEL_SHORT(static_cast<short>(cur_ch.correction + GET_INTEL_SHORT(cur_ch.old_base + cur_ch.offset)));
895
                //write (correctly aligned) chunk:
896
                cur_old = old_dest(cur_ch);
897
                cur_new = new_dest(cur_ch);
898
                chunk_len = get_chunks(cur_old, cur_new, ch_list, &no_chunks);
899
                memcpy(cur_new, cur_old, chunk_len);
900
                //correct submodel_ptr's for pm, too
901
                for (auto &sp : pm->submodel_ptrs)
902
                        if (&pm->model_data[sp] >= cur_old &&
903
                                &pm->model_data[sp] < cur_old + chunk_len)
904
                                sp += (cur_new - tmp.get()) - (cur_old - pm->model_data.get());
905
        }
906
        pm->model_data_size += total_correction;
907
        pm->model_data = std::make_unique<uint8_t[]>(pm->model_data_size);
908
        memcpy(pm->model_data.get(), tmp.get(), pm->model_data_size);
909
}
910
 
911
}
912
#endif //def WORDS_NEED_ALIGNMENT
913
 
914
namespace dsx {
915
 
916
// check a polymodel for it's color and return it
917
int g3_poly_get_color(const uint8_t *p)
918
{
919
        g3_poly_get_color_state state;
920
        iterate_polymodel(p, state);
921
        return state.color;
922
}
923
 
924
//calls the object interpreter to render an object.  The object renderer
925
//is really a seperate pipeline. returns true if drew
926
void g3_draw_polygon_model(grs_bitmap *const *const model_bitmaps, polygon_model_points &Interp_point_list, grs_canvas &canvas, const submodel_angles anim_angles, const g3s_lrgb model_light, const glow_values_t *const glow_values, const uint8_t *const p)
927
{
928
        g3_draw_polygon_model_state state(model_bitmaps, Interp_point_list, canvas, anim_angles, model_light, glow_values);
929
        iterate_polymodel(p, state);
930
}
931
 
932
#ifndef NDEBUG
933
static int nest_count;
934
#endif
935
 
936
//alternate interpreter for morphing object
937
void g3_draw_morphing_model(grs_canvas &canvas, const uint8_t *const p, grs_bitmap *const *const model_bitmaps, const submodel_angles anim_angles, const g3s_lrgb model_light, const vms_vector *new_points, polygon_model_points &Interp_point_list)
938
{
939
        g3_draw_morphing_model_state state(model_bitmaps, Interp_point_list, canvas, anim_angles, model_light, new_points);
940
        iterate_polymodel(p, state);
941
}
942
 
943
static int16_t init_model_sub(uint8_t *const model_sub_ptr, const uint8_t *const model_base_ptr, const std::size_t model_size)
944
{
945
        init_model_sub_state state(model_base_ptr, model_size);
946
        Assert(++nest_count < 1000);
947
        iterate_polymodel(model_sub_ptr, state);
948
        return state.highest_texture_num;
949
}
950
 
951
//init code for bitmap models
952
int16_t g3_init_polygon_model(uint8_t *const model_ptr, const std::size_t model_size)
953
{
954
        #ifndef NDEBUG
955
        nest_count = 0;
956
        #endif
957
        return init_model_sub(model_ptr, model_ptr, model_size);
958
}
959
 
960
#if defined(DXX_BUILD_DESCENT_I)
961
static void validate_model_sub(uint8_t *const model_sub_ptr, const uint8_t *const model_base_ptr, const std::size_t model_size)
962
{
963
        validate_model_sub_state state(model_base_ptr, model_size);
964
        assert(++nest_count < 1000);
965
        iterate_polymodel(model_sub_ptr, state);
966
}
967
 
968
void g3_validate_polygon_model(uint8_t *const model_ptr, const std::size_t model_size)
969
{
970
#ifndef NDEBUG
971
        nest_count = 0;
972
#endif
973
        return validate_model_sub(model_ptr, model_ptr, model_size);
974
}
975
#endif
976
 
977
}