Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Portions of this file are copyright Rebirth contributors and licensed as
  3.  * described in COPYING.txt.
  4.  * Portions of this file are copyright Parallax Software and licensed
  5.  * according to the Parallax license below.
  6.  * See COPYING.txt for license details.
  7.  
  8. THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
  9. SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
  10. END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
  11. ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
  12. IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
  13. SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
  14. FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
  15. CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
  16. AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
  17. COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
  18. */
  19.  
  20. /*
  21.  *
  22.  * Morphing code
  23.  *
  24.  */
  25.  
  26. #include <algorithm>
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <string.h>
  30.  
  31. #include "texmap.h"
  32. #include "dxxerror.h"
  33. #include "inferno.h"
  34. #include "polyobj.h"
  35. #include "game.h"
  36. #include "lighting.h"
  37. #include "newdemo.h"
  38. #include "piggy.h"
  39. #include "bm.h"
  40. #include "interp.h"
  41. #include "render.h"
  42. #include "object.h"
  43.  
  44. #include "compiler-poison.h"
  45. #include "compiler-range_for.h"
  46. #include "d_enumerate.h"
  47. #include "d_range.h"
  48. #include "d_zip.h"
  49. #include "partial_range.h"
  50.  
  51. using std::max;
  52.  
  53. namespace dcx {
  54.  
  55. namespace {
  56.  
  57. class invalid_morph_model_type : public std::runtime_error
  58. {
  59.         __attribute_cold
  60.         static std::string prepare_message(const unsigned type)
  61.         {
  62.                 char buf[32 + sizeof("4294967295")];
  63.                 const auto len = std::snprintf(buf, sizeof buf, "invalid morph model type: %u", type);
  64.                 return std::string(buf, len);
  65.         }
  66. public:
  67.         invalid_morph_model_type(const unsigned type) :
  68.                 runtime_error(prepare_message(type))
  69.         {
  70.         }
  71. };
  72.  
  73. class invalid_morph_model_vertex_count : public std::runtime_error
  74. {
  75.         __attribute_cold
  76.         static std::string prepare_message(const unsigned count, const morph_data::polymodel_idx idx, const unsigned submodel_num)
  77.         {
  78.                 char buf[68 + 3 * sizeof("4294967295")];
  79.                 const unsigned uidx = idx.idx;
  80.                 const auto len = std::snprintf(buf, sizeof buf, "too many vertices in morph model: found %u in model %u, submodel %u", count, uidx, submodel_num);
  81.                 return std::string(buf, len);
  82.         }
  83. public:
  84.         invalid_morph_model_vertex_count(const unsigned count, const morph_data::polymodel_idx idx, const unsigned submodel_num) :
  85.                 runtime_error(prepare_message(count, idx, submodel_num))
  86.         {
  87.         }
  88. };
  89.  
  90. struct submodel_data
  91. {
  92.         const uint16_t *body;
  93.         const unsigned type;
  94.         const unsigned nverts;
  95.         const unsigned startpoint;
  96. };
  97.  
  98. submodel_data parse_model_data_header(const polymodel &pm, const unsigned submodel_num)
  99. {
  100.         auto data = reinterpret_cast<const uint16_t *>(&pm.model_data[pm.submodel_ptrs[submodel_num]]);
  101.         const auto ptype = data++;
  102.  
  103.         const uint16_t type = *ptype;
  104.         const auto pnverts = data++;
  105.  
  106.         const uint16_t startpoint = (type == 7)
  107.                 ? *std::exchange(data, data + 2)                //get start point number, skip pad
  108.                 : (type == 1)
  109.                 ? 0                             //start at zero
  110.                 : throw invalid_morph_model_type(type);
  111.         const uint16_t nverts = *pnverts;
  112.         return {data, type, nverts, startpoint};
  113. }
  114.  
  115. std::size_t count_submodel_points(const polymodel &pm, const morph_data::polymodel_idx model_idx, const unsigned submodel_num)
  116. {
  117.         /* Return the minimum array size that will not cause this submodel
  118.          * to index past the end of the array.
  119.          */
  120.         const auto &&sd = parse_model_data_header(pm, submodel_num);
  121.         const std::size_t count = sd.startpoint + sd.nverts;
  122.         if (count > morph_data::MAX_VECS)
  123.                 throw invalid_morph_model_vertex_count(count, model_idx, submodel_num);
  124.         return count;
  125. }
  126.  
  127. std::size_t count_model_points(const polymodel &pm, const morph_data::polymodel_idx model_idx)
  128. {
  129.         /* Return the minimum array size that will not cause any used
  130.          * submodel of this model to index past the end of the array.
  131.          *
  132.          * Unused submodels are not considered.  A submodel is used if:
  133.          * - its index is not above pm.n_models
  134.          * - its parent index is valid
  135.          * - its parent index is a used submodel
  136.          *
  137.          * Submodel 0 is always used.
  138.          */
  139.         auto count = count_submodel_points(pm, model_idx, 0);
  140.         unsigned visited_submodels = 1;
  141.         const unsigned mask_all_enabled_models = (1 << pm.n_models) - 1;
  142.         const auto &&submodel_parents = enumerate(partial_range(pm.submodel_parents, pm.n_models));
  143.         for (;;)
  144.         {
  145.                 if (visited_submodels == mask_all_enabled_models)
  146.                         /* Every submodel has been checked, so the next pass through
  147.                          * the loop will ignore every element.  Break out early to
  148.                          * avoid the extra iteration.
  149.                          */
  150.                         break;
  151.                 const auto previous_visited_submodels = visited_submodels;
  152.                 range_for (auto &&e, submodel_parents)
  153.                 {
  154.                         const unsigned mask_this_submodel = 1 << e.idx;
  155.                         if (mask_this_submodel & visited_submodels)
  156.                                 /* Already tested on a prior iteration */
  157.                                 continue;
  158.                         if (e.value >= pm.submodel_parents.size())
  159.                                 /* Ignore submodels with out-of-range parents.  This
  160.                                  * avoids undefined behavior in the shift, since some
  161.                                  * submodels have a parent of 0xff.
  162.                                  */
  163.                                 continue;
  164.                         const unsigned mask_parent_submodel = 1 << e.value;
  165.                         if (mask_parent_submodel & visited_submodels)
  166.                         {
  167.                                 visited_submodels |= mask_this_submodel;
  168.                                 /* Parent is in use, so this submodel is also in use. */
  169.                                 const auto subcount = count_submodel_points(pm, model_idx, e.idx);
  170.                                 count = std::max(count, subcount);
  171.                         }
  172.                 }
  173.                 if (previous_visited_submodels == visited_submodels)
  174.                         /* No changes on the most recent pass, so no changes will
  175.                          * occur on any subsequent pass.  Break out.
  176.                          */
  177.                         break;
  178.         }
  179.         return count;
  180. }
  181.  
  182. }
  183.  
  184. void *morph_data::operator new(std::size_t, const max_vectors max_vecs)
  185. {
  186.         return ::operator new(sizeof(morph_data) + (max_vecs.count * (sizeof(fix) + sizeof(vms_vector) + sizeof(vms_vector))));
  187. }
  188.  
  189. morph_data::ptr morph_data::create(object_base &o, const polymodel &pm, const polymodel_idx model_idx)
  190. {
  191.         const max_vectors m{count_model_points(pm, model_idx)};
  192.         /* This is an unusual form of `new` overload.  Although arguments to
  193.          * `new` are typically considered a use of `placement new`, this is
  194.          * not used to place the object.  Instead, it is used to pass extra
  195.          * information to `operator new` so that the count of allocated
  196.          * bytes can be adjusted.
  197.          */
  198.         return ptr(new(m) morph_data(o, m));
  199. }
  200.  
  201. morph_data::morph_data(object_base &o, const max_vectors m) :
  202.         obj(&o), Morph_sig(o.signature), max_vecs(m)
  203. {
  204.         DXX_POISON_VAR(submodel_active, 0xcc);
  205.         const auto morph_times = get_morph_times();
  206.         DXX_POISON_MEMORY(morph_times.begin(), morph_times.end(), 0xcc);
  207.         const auto morph_vecs = get_morph_times();
  208.         DXX_POISON_MEMORY(morph_vecs.begin(), morph_vecs.end(), 0xcc);
  209.         const auto morph_deltas = get_morph_times();
  210.         DXX_POISON_MEMORY(morph_deltas.begin(), morph_deltas.end(), 0xcc);
  211.         DXX_POISON_VAR(n_morphing_points, 0xcc);
  212.         DXX_POISON_VAR(submodel_startpoints, 0xcc);
  213. }
  214.  
  215. span<fix> morph_data::get_morph_times()
  216. {
  217.         return {reinterpret_cast<fix *>(this + 1), max_vecs.count};
  218. }
  219.  
  220. span<vms_vector> morph_data::get_morph_vecs()
  221. {
  222.         return {reinterpret_cast<vms_vector *>(get_morph_times().end()), max_vecs.count};
  223. }
  224.  
  225. span<vms_vector> morph_data::get_morph_deltas()
  226. {
  227.         return {get_morph_vecs().end(), max_vecs.count};
  228. }
  229.  
  230. d_level_unique_morph_object_state::~d_level_unique_morph_object_state() = default;
  231.  
  232. //returns ptr to data for this object, or NULL if none
  233. morph_data::ptr *find_morph_data(d_level_unique_morph_object_state &LevelUniqueMorphObjectState, object_base &obj)
  234. {
  235.         auto &morph_objects = LevelUniqueMorphObjectState.morph_objects;
  236.         if (Newdemo_state == ND_STATE_PLAYBACK) {
  237.                 return nullptr;
  238.         }
  239.  
  240.         range_for (auto &i, morph_objects)
  241.                 if (i && i->obj == &obj)
  242.                         return &i;
  243.         return nullptr;
  244. }
  245.  
  246. }
  247.  
  248. static void assign_max(fix &a, const fix &b)
  249. {
  250.         a = std::max(a, b);
  251. }
  252.  
  253. static void assign_min(fix &a, const fix &b)
  254. {
  255.         a = std::min(a, b);
  256. }
  257.  
  258. static void update_bounds(vms_vector &minv, vms_vector &maxv, const vms_vector &v, fix vms_vector::*const p)
  259. {
  260.         auto &mx = maxv.*p;
  261.         assign_max(mx, v.*p);
  262.         auto &mn = minv.*p;
  263.         assign_min(mn, v.*p);
  264. }
  265.  
  266. //takes pm, fills in min & max
  267. static void find_min_max(const polymodel &pm, const unsigned submodel_num, vms_vector &minv, vms_vector &maxv)
  268. {
  269.         const auto &&sd = parse_model_data_header(pm, submodel_num);
  270.         const unsigned nverts = sd.nverts;
  271.         if (!nverts)
  272.         {
  273.                 minv = maxv = {};
  274.                 return;
  275.         }
  276.         const auto vp = reinterpret_cast<const vms_vector *>(sd.body);
  277.  
  278.         minv = maxv = *vp;
  279.  
  280.         range_for (auto &v, unchecked_partial_range(vp + 1, nverts - 1))
  281.         {
  282.                 update_bounds(minv, maxv, v, &vms_vector::x);
  283.                 update_bounds(minv, maxv, v, &vms_vector::y);
  284.                 update_bounds(minv, maxv, v, &vms_vector::z);
  285.         }
  286. }
  287.  
  288. #define MORPH_RATE (f1_0*3)
  289.  
  290. constexpr fix morph_rate = MORPH_RATE;
  291.  
  292. static fix update_bounding_box_extent(const vms_vector &vp, const vms_vector &box_size, fix vms_vector::*const p, const fix entry_extent)
  293. {
  294.         if (!(vp.*p))
  295.                 return entry_extent;
  296.         const auto box_size_p = box_size.*p;
  297.         const auto abs_vp_p = abs(vp.*p);
  298.         if (f2i(box_size_p) >= abs_vp_p / 2)
  299.                 return entry_extent;
  300.         const fix t = fixdiv(box_size_p, abs_vp_p);
  301.         return std::min(entry_extent, t);
  302. }
  303.  
  304. static fix compute_bounding_box_extents(const vms_vector &vp, const vms_vector &box_size)
  305. {
  306.         fix k = INT32_MAX;
  307.  
  308.         k = update_bounding_box_extent(vp, box_size, &vms_vector::x, k);
  309.         k = update_bounding_box_extent(vp, box_size, &vms_vector::y, k);
  310.         k = update_bounding_box_extent(vp, box_size, &vms_vector::z, k);
  311.  
  312.         return k;
  313. }
  314.  
  315. static void init_points(const polymodel &pm, const vms_vector *const box_size, const unsigned submodel_num, morph_data *const md)
  316. {
  317.         const auto &&sd = parse_model_data_header(pm, submodel_num);
  318.         const unsigned startpoint = sd.startpoint;
  319.         const unsigned endpoint = sd.startpoint + sd.nverts;
  320.  
  321.         md->submodel_active[submodel_num] = morph_data::submodel_state::animating;
  322.         md->n_morphing_points[submodel_num] = 0;
  323.         md->submodel_startpoints[submodel_num] = startpoint;
  324.  
  325.         const auto morph_times = md->get_morph_times();
  326.         const auto morph_vecs = md->get_morph_vecs();
  327.         const auto morph_deltas = md->get_morph_deltas();
  328.         auto &&zr = zip(
  329.                 unchecked_partial_range(reinterpret_cast<const vms_vector *>(sd.body), sd.nverts),
  330.                 partial_range(morph_vecs, startpoint, endpoint),
  331.                 partial_range(morph_deltas, startpoint, endpoint),
  332.                 partial_range(morph_times, startpoint, endpoint)
  333.         );
  334.         range_for (auto &&z, zr)
  335.         {
  336.                 const auto vp = &std::get<0>(z);
  337.                 auto &morph_vec = std::get<1>(z);
  338.                 auto &morph_delta = std::get<2>(z);
  339.                 auto &morph_time = std::get<3>(z);
  340.                 fix k;
  341.  
  342.                 if (box_size && (k = compute_bounding_box_extents(*vp, *box_size) != INT32_MAX))
  343.                         vm_vec_copy_scale(morph_vec, *vp, k);
  344.                 else
  345.                         morph_vec = {};
  346.  
  347.                 const fix dist = vm_vec_normalized_dir_quick(morph_delta, *vp, morph_vec);
  348.                 morph_time = fixdiv(dist, morph_rate);
  349.  
  350.                 if (morph_time != 0)
  351.                         md->n_morphing_points[submodel_num]++;
  352.  
  353.                 vm_vec_scale(morph_delta, morph_rate);
  354.         }
  355. }
  356.  
  357. static void update_points(const polymodel &pm, const unsigned submodel_num, morph_data *const md)
  358. {
  359.         const auto &&sd = parse_model_data_header(pm, submodel_num);
  360.         const unsigned startpoint = sd.startpoint;
  361.         const unsigned endpoint = startpoint + sd.nverts;
  362.  
  363.         const auto morph_times = md->get_morph_times();
  364.         const auto morph_vecs = md->get_morph_vecs();
  365.         const auto morph_deltas = md->get_morph_deltas();
  366.         auto &&zr = zip(
  367.                 unchecked_partial_range(reinterpret_cast<const vms_vector *>(sd.body), sd.nverts),
  368.                 partial_range(morph_vecs, startpoint, endpoint),
  369.                 partial_range(morph_deltas, startpoint, endpoint),
  370.                 partial_range(morph_times, startpoint, endpoint)
  371.         );
  372.         range_for (auto &&z, zr)
  373.         {
  374.                 const auto vp = &std::get<0>(z);
  375.                 auto &morph_vec = std::get<1>(z);
  376.                 auto &morph_delta = std::get<2>(z);
  377.                 auto &morph_time = std::get<3>(z);
  378.                 if (morph_time)         //not done yet
  379.                 {
  380.                         if ((morph_time -= FrameTime) <= 0) {
  381.                                 morph_vec = *vp;
  382.                                 morph_time = 0;
  383.                                 md->n_morphing_points[submodel_num]--;
  384.                         }
  385.                         else
  386.                                 vm_vec_scale_add2(morph_vec, morph_delta, FrameTime);
  387.                 }
  388.         }
  389. }
  390.  
  391. //process the morphing object for one frame
  392. void do_morph_frame(object &obj)
  393. {
  394.         auto &LevelUniqueMorphObjectState = LevelUniqueObjectState.MorphObjectState;
  395.         const auto umd = find_morph_data(LevelUniqueMorphObjectState, obj);
  396.  
  397.         if (!umd) {                                     //maybe loaded half-morphed from disk
  398.                 obj.flags |= OF_SHOULD_BE_DEAD;         //..so kill it
  399.                 return;
  400.         }
  401.         const auto md = umd->get();
  402.         assert(md->obj == &obj);
  403.  
  404.         auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models;
  405.         const polymodel &pm = Polygon_models[obj.rtype.pobj_info.model_num];
  406.  
  407.         const auto n_models = pm.n_models;
  408.         range_for (const auto &&zi, zip(xrange(n_models), md->submodel_active, md->n_morphing_points))
  409.         {
  410.                 const unsigned i = std::get<0>(zi);
  411.                 auto &submodel_active = std::get<1>(zi);
  412.                 if (submodel_active == morph_data::submodel_state::animating)
  413.                 {
  414.                         update_points(pm,i,md);
  415.                         const auto &n_morphing_points = std::get<2>(zi);
  416.                         if (n_morphing_points == 0) {           //maybe start submodel
  417.                                 submodel_active = morph_data::submodel_state::visible;          //not animating, just visible
  418.                                 md->n_submodels_active--;               //this one done animating
  419.                                 range_for (const auto &&zt, zip(xrange(n_models), pm.submodel_parents))
  420.                                 {
  421.                                         auto &submodel_parents = std::get<1>(zt);
  422.                                         if (submodel_parents == i)
  423.                                         {               //start this one
  424.                                                 const auto t = std::get<0>(zt);
  425.                                                 init_points(pm,nullptr,t,md);
  426.                                                 md->n_submodels_active++;
  427.                                         }
  428.                                 }
  429.                         }
  430.                 }
  431.         }
  432.  
  433.         if (!md->n_submodels_active) {                  //done morphing!
  434.  
  435.                 obj.control_type = md->morph_save_control_type;
  436.                 set_object_movement_type(obj, md->morph_save_movement_type);
  437.  
  438.                 obj.render_type = RT_POLYOBJ;
  439.  
  440.                 obj.mtype.phys_info = md->morph_save_phys_info;
  441.                 umd->reset();
  442.         }
  443. }
  444.  
  445. constexpr vms_vector morph_rotvel{0x4000,0x2000,0x1000};
  446.  
  447. void init_morphs()
  448. {
  449.         auto &LevelUniqueMorphObjectState = LevelUniqueObjectState.MorphObjectState;
  450.         auto &morph_objects = LevelUniqueMorphObjectState.morph_objects;
  451.         morph_objects = {};
  452. }
  453.  
  454. //make the object morph
  455. void morph_start(d_level_unique_morph_object_state &LevelUniqueMorphObjectState, d_level_shared_polygon_model_state &LevelSharedPolygonModelState, object_base &obj)
  456. {
  457.         vms_vector pmmin,pmmax;
  458.         vms_vector box_size;
  459.  
  460.         auto &morph_objects = LevelUniqueMorphObjectState.morph_objects;
  461.         const auto mob = morph_objects.begin();
  462.         const auto moe = morph_objects.end();
  463.         const auto mop = [](const morph_data::ptr &pmo) {
  464.                 if (!pmo)
  465.                         return true;
  466.                 auto &mo = *pmo.get();
  467.                 return mo.obj->type == OBJ_NONE || mo.obj->signature != mo.Morph_sig;
  468.         };
  469.         const auto moi = std::find_if(mob, moe, mop);
  470.  
  471.         if (moi == moe)         //no free slots
  472.                 return;
  473.  
  474.         auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models;
  475.         const morph_data::polymodel_idx pmi(obj.rtype.pobj_info.model_num);
  476.         auto &pm = Polygon_models[pmi.idx];
  477.  
  478.         *moi = morph_data::create(obj, pm, pmi);
  479.         morph_data *const md = moi->get();
  480.  
  481.         assert(obj.render_type == RT_POLYOBJ);
  482.  
  483.         md->morph_save_control_type = obj.control_type;
  484.         md->morph_save_movement_type = obj.movement_type;
  485.         md->morph_save_phys_info = obj.mtype.phys_info;
  486.  
  487.         assert(obj.control_type == CT_AI);              //morph objects are also AI objects
  488.  
  489.         obj.control_type = CT_MORPH;
  490.         obj.render_type = RT_MORPH;
  491.         obj.movement_type = MT_PHYSICS;         //RT_NONE;
  492.  
  493.         obj.mtype.phys_info.rotvel = morph_rotvel;
  494.  
  495.         find_min_max(pm,0,pmmin,pmmax);
  496.  
  497.         box_size.x = max(-pmmin.x,pmmax.x) / 2;
  498.         box_size.y = max(-pmmin.y,pmmax.y) / 2;
  499.         box_size.z = max(-pmmin.z,pmmax.z) / 2;
  500.  
  501.         //clear all points
  502.         const auto morph_times = md->get_morph_times();
  503.         std::fill(morph_times.begin(), morph_times.end(), fix());
  504.         //clear all parts
  505.         md->submodel_active = {};
  506.  
  507.         md->n_submodels_active = 1;
  508.  
  509.         //now, project points onto surface of box
  510.  
  511.         init_points(pm,&box_size,0,md);
  512. }
  513.  
  514. static void draw_model(grs_canvas &canvas, polygon_model_points &robot_points, polymodel *const pm, const unsigned submodel_num, const submodel_angles anim_angles, g3s_lrgb light, morph_data *const md)
  515. {
  516.         std::array<unsigned, MAX_SUBMODELS> sort_list;
  517.         unsigned sort_n;
  518.  
  519.  
  520.         //first, sort the submodels
  521.  
  522.         sort_list[0] = submodel_num;
  523.         sort_n = 1;
  524.  
  525.         const uint_fast32_t n_models = pm->n_models;
  526.         range_for (const uint_fast32_t i, xrange(n_models))
  527.                 if (md->submodel_active[i] != morph_data::submodel_state::invisible && pm->submodel_parents[i] == submodel_num)
  528.                 {
  529.                         const auto facing = g3_check_normal_facing(pm->submodel_pnts[i],pm->submodel_norms[i]);
  530.                         if (!facing)
  531.                                 sort_list[sort_n] = i;
  532.                         else {          //put at start
  533.                                 const auto b = sort_list.begin();
  534.                                 const auto e = std::next(b, sort_n);
  535.                                 std::move_backward(b, e, std::next(e));
  536.                                 sort_list[0] = i;
  537.                         }
  538.                         ++sort_n;
  539.                 }
  540.  
  541.         //now draw everything
  542.  
  543.         range_for (const auto mn, partial_const_range(sort_list, sort_n))
  544.         {
  545.                 if (mn == submodel_num) {
  546.                         std::array<bitmap_index, MAX_POLYOBJ_TEXTURES> texture_list_index;
  547.                         std::array<grs_bitmap *, MAX_POLYOBJ_TEXTURES> texture_list;
  548.                         for (unsigned i = 0; i < pm->n_textures; ++i)
  549.                         {
  550.                                 const auto ptr = ObjBitmapPtrs[pm->first_texture + i];
  551.                                 const auto &bmp = ObjBitmaps[ptr];
  552.                                 texture_list_index[i] = bmp;
  553.                                 texture_list[i] = &GameBitmaps[bmp.index];
  554.                         }
  555.  
  556.                         // Make sure the textures for this object are paged in...
  557.                         range_for (auto &j, partial_const_range(texture_list_index, pm->n_textures))
  558.                                 PIGGY_PAGE_IN(j);
  559.                         // Hmmm... cache got flushed in the middle of paging all these in,
  560.                         // so we need to reread them all in.
  561.                         // Make sure that they can all fit in memory.
  562.                         const auto morph_vecs = md->get_morph_vecs();
  563.                         g3_draw_morphing_model(canvas, &pm->model_data[pm->submodel_ptrs[submodel_num]], &texture_list[0], anim_angles, light, &morph_vecs[md->submodel_startpoints[submodel_num]], robot_points);
  564.                 }
  565.                 else {
  566.                         const auto &&orient = vm_angles_2_matrix(anim_angles[mn]);
  567.                         g3_start_instance_matrix(pm->submodel_offsets[mn], orient);
  568.                         draw_model(canvas, robot_points,pm,mn,anim_angles,light,md);
  569.                         g3_done_instance();
  570.                 }
  571.         }
  572.  
  573. }
  574.  
  575. namespace dsx {
  576.  
  577. void draw_morph_object(grs_canvas &canvas, const d_level_unique_light_state &LevelUniqueLightState, const vmobjptridx_t obj)
  578. {
  579.         if (Newdemo_state == ND_STATE_PLAYBACK)
  580.                 return;
  581.         polymodel *po;
  582.  
  583.         auto &LevelUniqueMorphObjectState = LevelUniqueObjectState.MorphObjectState;
  584.         const auto umd = find_morph_data(LevelUniqueMorphObjectState, obj);
  585.         if (!umd)
  586.                 throw std::runtime_error("missing morph data");
  587.         const auto md = umd->get();
  588.  
  589.         auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models;
  590.         po=&Polygon_models[obj->rtype.pobj_info.model_num];
  591.  
  592.         const auto light = compute_object_light(LevelUniqueLightState, obj);
  593.  
  594.         g3_start_instance_matrix(obj->pos, obj->orient);
  595.         polygon_model_points robot_points;
  596.         draw_model(canvas, robot_points, po, 0, obj->rtype.pobj_info.anim_angles, light, md);
  597.  
  598.         g3_done_instance();
  599.  
  600.         if (Newdemo_state == ND_STATE_RECORDING)
  601.                 newdemo_record_morph_frame(obj);
  602. }
  603.  
  604. }
  605.