dxx-rebirth/similar/main/morph.cpp

427 lines
11 KiB
C++
Raw Normal View History

2006-03-20 16:43:15 +00:00
/*
2014-06-01 17:55:23 +00:00
* Portions of this file are copyright Rebirth contributors and licensed as
* described in COPYING.txt.
* Portions of this file are copyright Parallax Software and licensed
* according to the Parallax license below.
* See COPYING.txt for license details.
2006-03-20 16:43:15 +00:00
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
2006-03-20 16:43:15 +00:00
COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
*/
2006-03-20 16:43:15 +00:00
/*
*
* Morphing code
2006-03-20 16:43:15 +00:00
*
*/
#include <algorithm>
2006-03-20 16:43:15 +00:00
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "texmap.h"
#include "dxxerror.h"
2006-03-20 16:43:15 +00:00
#include "inferno.h"
#include "morph.h"
#include "polyobj.h"
#include "game.h"
#include "lighting.h"
#include "newdemo.h"
#include "piggy.h"
#include "bm.h"
#include "interp.h"
#include "render.h"
2006-03-20 16:43:15 +00:00
2015-02-14 22:48:27 +00:00
#include "compiler-range_for.h"
#include "d_range.h"
2015-02-14 22:48:27 +00:00
#include "partial_range.h"
using std::max;
namespace dsx {
d_level_unique_morph_object_state LevelUniqueMorphObjectState;
}
2006-03-20 16:43:15 +00:00
//returns ptr to data for this object, or NULL if none
morph_data *find_morph_data(object &obj)
2006-03-20 16:43:15 +00:00
{
auto &morph_objects = LevelUniqueMorphObjectState.morph_objects;
2006-03-20 16:43:15 +00:00
if (Newdemo_state == ND_STATE_PLAYBACK) {
morph_objects[0].obj = &obj;
2006-03-20 16:43:15 +00:00
return &morph_objects[0];
}
2015-02-14 22:48:27 +00:00
range_for (auto &i, morph_objects)
if (i.obj == &obj)
2015-02-14 22:48:27 +00:00
return &i;
return nullptr;
2006-03-20 16:43:15 +00:00
}
2014-11-02 03:42:54 +00:00
static void assign_max(fix &a, const fix &b)
{
a = std::max(a, b);
}
static void assign_min(fix &a, const fix &b)
{
a = std::min(a, b);
}
template <fix vms_vector::*p>
static void update_bounds(vms_vector &minv, vms_vector &maxv, const vms_vector *vp)
{
auto &mx = maxv.*p;
assign_max(mx, vp->*p);
auto &mn = minv.*p;
assign_min(mn, vp->*p);
2014-11-02 03:42:54 +00:00
}
2006-03-20 16:43:15 +00:00
//takes pm, fills in min & max
2014-11-02 03:42:54 +00:00
static void find_min_max(polymodel *pm,int submodel_num,vms_vector &minv,vms_vector &maxv)
2006-03-20 16:43:15 +00:00
{
ushort nverts;
2016-09-04 00:02:53 +00:00
uint16_t type;
2006-03-20 16:43:15 +00:00
2016-09-04 00:02:53 +00:00
auto data = reinterpret_cast<uint16_t *>(&pm->model_data[pm->submodel_ptrs[submodel_num]]);
2006-03-20 16:43:15 +00:00
type = *data++;
Assert(type == 7 || type == 1);
nverts = *data++;
if (type==7)
data+=2; //skip start & pad
2014-11-02 03:42:54 +00:00
auto vp = reinterpret_cast<const vms_vector *>(data);
2006-03-20 16:43:15 +00:00
2014-11-02 03:42:54 +00:00
minv = maxv = *vp++;
nverts--;
2006-03-20 16:43:15 +00:00
while (nverts--) {
2014-11-02 03:42:54 +00:00
update_bounds<&vms_vector::x>(minv, maxv, vp);
update_bounds<&vms_vector::y>(minv, maxv, vp);
update_bounds<&vms_vector::z>(minv, maxv, vp);
2006-03-20 16:43:15 +00:00
vp++;
}
}
#define MORPH_RATE (f1_0*3)
2017-11-05 20:49:09 +00:00
constexpr fix morph_rate = MORPH_RATE;
2006-03-20 16:43:15 +00:00
static void init_points(const polymodel *const pm, const vms_vector *const box_size, const unsigned submodel_num, morph_data *const md)
2006-03-20 16:43:15 +00:00
{
auto data = reinterpret_cast<const uint16_t *>(&pm->model_data[pm->submodel_ptrs[submodel_num]]);
2006-03-20 16:43:15 +00:00
2020-02-01 22:33:31 +00:00
const uint16_t type = *data++;
2006-03-20 16:43:15 +00:00
Assert(type == 7 || type == 1);
2020-02-01 22:33:31 +00:00
const uint16_t nverts = *data++;
2006-03-20 16:43:15 +00:00
md->n_morphing_points[submodel_num] = 0;
2020-02-01 22:33:31 +00:00
const unsigned startpoint = (type == 7)
? *exchange(data, data + 2) //get start point number, skip pad
: 0; //start at zero
2006-03-20 16:43:15 +00:00
2020-02-01 22:33:31 +00:00
unsigned i = startpoint;
2020-02-01 22:33:31 +00:00
assert(i + nverts < morph_data::MAX_VECS);
2006-03-20 16:43:15 +00:00
md->submodel_startpoints[submodel_num] = i;
2014-11-02 03:42:54 +00:00
auto vp = reinterpret_cast<const vms_vector *>(data);
2006-03-20 16:43:15 +00:00
2020-02-01 22:33:31 +00:00
range_for (auto &&iteration, xrange(nverts))
{
(void)iteration;
2006-03-20 16:43:15 +00:00
fix k,dist;
if (box_size) {
fix t;
k = INT32_MAX;
2006-03-20 16:43:15 +00:00
if (vp->x && f2i(box_size->x)<abs(vp->x)/2 && (t = fixdiv(box_size->x,abs(vp->x))) < k) k=t;
if (vp->y && f2i(box_size->y)<abs(vp->y)/2 && (t = fixdiv(box_size->y,abs(vp->y))) < k) k=t;
if (vp->z && f2i(box_size->z)<abs(vp->z)/2 && (t = fixdiv(box_size->z,abs(vp->z))) < k) k=t;
if (k == INT32_MAX)
k = 0;
2006-03-20 16:43:15 +00:00
}
else
k=0;
2014-09-28 21:43:14 +00:00
vm_vec_copy_scale(md->morph_vecs[i],*vp,k);
2006-03-20 16:43:15 +00:00
dist = vm_vec_normalized_dir_quick(md->morph_deltas[i],*vp,md->morph_vecs[i]);
2006-03-20 16:43:15 +00:00
md->morph_times[i] = fixdiv(dist,morph_rate);
if (md->morph_times[i] != 0)
md->n_morphing_points[submodel_num]++;
2014-09-28 21:11:05 +00:00
vm_vec_scale(md->morph_deltas[i],morph_rate);
2006-03-20 16:43:15 +00:00
vp++; i++;
}
}
static void update_points(const polymodel *const pm, const unsigned submodel_num, morph_data *const md)
2006-03-20 16:43:15 +00:00
{
ushort nverts;
2016-09-04 00:02:53 +00:00
uint16_t type;
2006-03-20 16:43:15 +00:00
int i;
2016-09-04 00:02:53 +00:00
auto data = reinterpret_cast<uint16_t *>(&pm->model_data[pm->submodel_ptrs[submodel_num]]);
2006-03-20 16:43:15 +00:00
type = *data++;
Assert(type == 7 || type == 1);
nverts = *data++;
if (type==7) {
i = *data++; //get start point number
data++; //skip pad
}
else
i = 0; //start at zero
2014-11-02 03:42:54 +00:00
auto vp = reinterpret_cast<const vms_vector *>(data);
2006-03-20 16:43:15 +00:00
while (nverts--) {
if (md->morph_times[i]) //not done yet
{
2006-03-20 16:43:15 +00:00
if ((md->morph_times[i] -= FrameTime) <= 0) {
md->morph_vecs[i] = *vp;
md->morph_times[i] = 0;
md->n_morphing_points[submodel_num]--;
}
else
2014-09-28 21:43:14 +00:00
vm_vec_scale_add2(md->morph_vecs[i],md->morph_deltas[i],FrameTime);
}
2006-03-20 16:43:15 +00:00
vp++; i++;
}
}
//process the morphing object for one frame
void do_morph_frame(object &obj)
2006-03-20 16:43:15 +00:00
{
morph_data *md;
md = find_morph_data(obj);
if (md == NULL) { //maybe loaded half-morphed from disk
obj.flags |= OF_SHOULD_BE_DEAD; //..so kill it
2006-03-20 16:43:15 +00:00
return;
}
assert(md->obj == &obj);
2006-03-20 16:43:15 +00:00
auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models;
const polymodel *const pm = &Polygon_models[obj.rtype.pobj_info.model_num];
2006-03-20 16:43:15 +00:00
2014-12-05 03:31:07 +00:00
for (uint_fast32_t i = 0; i != pm->n_models; ++i)
if (md->submodel_active[i] == morph_data::submodel_state::animating)
{
2006-03-20 16:43:15 +00:00
update_points(pm,i,md);
if (md->n_morphing_points[i] == 0) { //maybe start submodel
md->submodel_active[i] = morph_data::submodel_state::visible; //not animating, just visible
2006-03-20 16:43:15 +00:00
md->n_submodels_active--; //this one done animating
2014-12-05 03:31:07 +00:00
for (uint_fast32_t t = 0; t != pm->n_models; ++t)
2006-03-20 16:43:15 +00:00
if (pm->submodel_parents[t] == i) { //start this one
2014-11-02 03:42:54 +00:00
init_points(pm,nullptr,t,md);
2006-03-20 16:43:15 +00:00
md->n_submodels_active++;
md->submodel_active[t] = morph_data::submodel_state::animating;
2006-03-20 16:43:15 +00:00
}
}
}
if (!md->n_submodels_active) { //done morphing!
obj.control_type = md->morph_save_control_type;
set_object_movement_type(obj, md->morph_save_movement_type);
2006-03-20 16:43:15 +00:00
obj.render_type = RT_POLYOBJ;
2006-03-20 16:43:15 +00:00
obj.mtype.phys_info = md->morph_save_phys_info;
2006-03-20 16:43:15 +00:00
md->obj = NULL;
}
}
constexpr vms_vector morph_rotvel{0x4000,0x2000,0x1000};
2006-03-20 16:43:15 +00:00
void init_morphs()
{
auto &morph_objects = LevelUniqueMorphObjectState.morph_objects;
2015-02-14 22:48:27 +00:00
range_for (auto &i, morph_objects)
i.obj = nullptr;
2006-03-20 16:43:15 +00:00
}
//make the object morph
void morph_start(const vmobjptr_t obj)
2006-03-20 16:43:15 +00:00
{
polymodel *pm;
vms_vector pmmin,pmmax;
vms_vector box_size;
auto &morph_objects = LevelUniqueMorphObjectState.morph_objects;
const auto mob = morph_objects.begin();
const auto moe = morph_objects.end();
const auto mop = [](const morph_data &mo) {
return mo.obj == nullptr || mo.obj->type == OBJ_NONE || mo.obj->signature != mo.Morph_sig;
};
const auto moi = std::find_if(mob, moe, mop);
2006-03-20 16:43:15 +00:00
if (moi == moe) //no free slots
2006-03-20 16:43:15 +00:00
return;
morph_data *const md = &*moi;
2006-03-20 16:43:15 +00:00
Assert(obj->render_type == RT_POLYOBJ);
md->obj = obj;
md->Morph_sig = obj->signature;
md->morph_save_control_type = obj->control_type;
md->morph_save_movement_type = obj->movement_type;
md->morph_save_phys_info = obj->mtype.phys_info;
Assert(obj->control_type == CT_AI); //morph objects are also AI objects
obj->control_type = CT_MORPH;
obj->render_type = RT_MORPH;
obj->movement_type = MT_PHYSICS; //RT_NONE;
obj->mtype.phys_info.rotvel = morph_rotvel;
auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models;
2006-03-20 16:43:15 +00:00
pm = &Polygon_models[obj->rtype.pobj_info.model_num];
2014-11-02 03:42:54 +00:00
find_min_max(pm,0,pmmin,pmmax);
2006-03-20 16:43:15 +00:00
box_size.x = max(-pmmin.x,pmmax.x) / 2;
box_size.y = max(-pmmin.y,pmmax.y) / 2;
box_size.z = max(-pmmin.z,pmmax.z) / 2;
2016-02-12 04:02:28 +00:00
//clear all points
md->morph_times = {};
//clear all parts
md->submodel_active = {};
2006-03-20 16:43:15 +00:00
md->submodel_active[0] = morph_data::submodel_state::animating; //1 means visible & animating
2006-03-20 16:43:15 +00:00
md->n_submodels_active = 1;
//now, project points onto surface of box
init_points(pm,&box_size,0,md);
}
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)
2006-03-20 16:43:15 +00:00
{
array<unsigned, MAX_SUBMODELS> sort_list;
2016-05-22 17:49:30 +00:00
unsigned sort_n;
2006-03-20 16:43:15 +00:00
//first, sort the submodels
sort_list[0] = submodel_num;
sort_n = 1;
const uint_fast32_t n_models = pm->n_models;
range_for (const uint_fast32_t i, xrange(n_models))
if (md->submodel_active[i] != morph_data::submodel_state::invisible && pm->submodel_parents[i] == submodel_num)
{
const auto facing = g3_check_normal_facing(pm->submodel_pnts[i],pm->submodel_norms[i]);
2006-03-20 16:43:15 +00:00
if (!facing)
sort_list[sort_n] = i;
2006-03-20 16:43:15 +00:00
else { //put at start
const auto b = sort_list.begin();
const auto e = std::next(b, sort_n);
std::move_backward(b, e, std::next(e));
2006-03-20 16:43:15 +00:00
sort_list[0] = i;
}
++sort_n;
2006-03-20 16:43:15 +00:00
}
//now draw everything
2016-05-22 17:49:30 +00:00
range_for (const auto mn, partial_const_range(sort_list, sort_n))
{
2006-03-20 16:43:15 +00:00
if (mn == submodel_num) {
array<bitmap_index, MAX_POLYOBJ_TEXTURES> texture_list_index;
2016-02-12 04:02:28 +00:00
for (unsigned i = 0; i < pm->n_textures; ++i)
{
2016-05-22 17:49:30 +00:00
const auto ptr = ObjBitmapPtrs[pm->first_texture + i];
const auto &bmp = ObjBitmaps[ptr];
texture_list_index[i] = bmp;
texture_list[i] = &GameBitmaps[bmp.index];
2006-03-20 16:43:15 +00:00
}
// Make sure the textures for this object are paged in...
2016-02-12 04:02:28 +00:00
range_for (auto &j, partial_const_range(texture_list_index, pm->n_textures))
PIGGY_PAGE_IN(j);
2006-03-20 16:43:15 +00:00
// Hmmm... cache got flushed in the middle of paging all these in,
// so we need to reread them all in.
// Make sure that they can all fit in memory.
2017-03-11 19:56:27 +00:00
g3_draw_morphing_model(canvas, &pm->model_data[pm->submodel_ptrs[submodel_num]], &texture_list[0], anim_angles, light, &md->morph_vecs[md->submodel_startpoints[submodel_num]], robot_points);
2006-03-20 16:43:15 +00:00
}
else {
2015-02-05 03:03:51 +00:00
const auto &&orient = vm_angles_2_matrix(anim_angles[mn]);
g3_start_instance_matrix(pm->submodel_offsets[mn], orient);
2017-03-11 19:56:27 +00:00
draw_model(canvas, robot_points,pm,mn,anim_angles,light,md);
2006-03-20 16:43:15 +00:00
g3_done_instance();
}
}
}
namespace dsx {
void draw_morph_object(grs_canvas &canvas, const d_level_unique_light_state &LevelUniqueLightState, const vmobjptridx_t obj)
2006-03-20 16:43:15 +00:00
{
// int save_light;
polymodel *po;
morph_data *md;
md = find_morph_data(obj);
Assert(md != NULL);
auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models;
2006-03-20 16:43:15 +00:00
po=&Polygon_models[obj->rtype.pobj_info.model_num];
const auto light = compute_object_light(LevelUniqueLightState, obj);
2006-03-20 16:43:15 +00:00
g3_start_instance_matrix(obj->pos, obj->orient);
2015-02-05 03:03:50 +00:00
polygon_model_points robot_points;
2017-03-11 19:56:27 +00:00
draw_model(canvas, robot_points, po, 0, obj->rtype.pobj_info.anim_angles, light, md);
2006-03-20 16:43:15 +00:00
g3_done_instance();
if (Newdemo_state == ND_STATE_RECORDING)
newdemo_record_morph_frame(obj);
2006-03-20 16:43:15 +00:00
}
}