dxx-rebirth/similar/editor/curves.cpp

386 lines
14 KiB
C++

/*
* 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.
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.
COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
*/
/*
*
* curve generation stuff
*
*/
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include "vecmat.h"
#include "gr.h"
#include "editor.h"
#include "editor/esegment.h"
#include "gameseg.h"
#include "console.h"
#define ONE_OVER_SQRT2 F1_0 * 0.707106781
#define CURVE_RIGHT 1
#define CURVE_UP 2
#include "compiler-range_for.h"
#include "partial_range.h"
static imsegptridx_t OriginalSeg = segment_none;
static imsegptridx_t OriginalMarkedSeg = segment_none;
static sidenum_t OriginalSide;
static sidenum_t OriginalMarkedSide;
static std::array<segment *, MAX_SEGMENTS> CurveSegs;
static unsigned CurveNumSegs;
static void generate_banked_curve(fix maxscale, vms_equation coeffs);
static void create_curve(vms_vector &p1, vms_vector &p4, vms_vector &r1, vms_vector &r4, vms_equation &coeffs)
{
// Q(t) = (2t^3 - 3t^2 + 1) p1 + (-2t^3 + 3t^2) p4 + (t~3 - 2t^2 + t) r1 + (t^3 - t^2 ) r4
coeffs.n.x3 = fixmul(2*F1_0,p1.x) - fixmul(2*F1_0,p4.x) + r1.x + r4.x;
coeffs.n.x2 = fixmul(-3*F1_0,p1.x) + fixmul(3*F1_0,p4.x) - fixmul(2*F1_0,r1.x) - fixmul(1*F1_0,r4.x);
coeffs.n.x1 = r1.x;
coeffs.n.x0 = p1.x;
coeffs.n.y3 = fixmul(2*F1_0,p1.y) - fixmul(2*F1_0,p4.y) + r1.y + r4.y;
coeffs.n.y2 = fixmul(-3*F1_0,p1.y) + fixmul(3*F1_0,p4.y) - fixmul(2*F1_0,r1.y) - fixmul(1*F1_0,r4.y);
coeffs.n.y1 = r1.y;
coeffs.n.y0 = p1.y;
coeffs.n.z3 = fixmul(2*F1_0,p1.z) - fixmul(2*F1_0,p4.z) + r1.z + r4.z;
coeffs.n.z2 = fixmul(-3*F1_0,p1.z) + fixmul(3*F1_0,p4.z) - fixmul(2*F1_0,r1.z) - fixmul(1*F1_0,r4.z);
coeffs.n.z1 = r1.z;
coeffs.n.z0 = p1.z;
}
vms_vector evaluate_curve(vms_equation *coeffs, int degree, fix t) {
fix t2, t3;
vms_vector coord;
if (degree != 3)
con_puts(CON_CRITICAL," for Hermite Curves degree must be 3");
t2 = fixmul(t,t); t3 = fixmul(t2,t);
coord.x = fixmul(coeffs->n.x3,t3) + fixmul(coeffs->n.x2,t2) + fixmul(coeffs->n.x1,t) + coeffs->n.x0;
coord.y = fixmul(coeffs->n.y3,t3) + fixmul(coeffs->n.y2,t2) + fixmul(coeffs->n.y1,t) + coeffs->n.y0;
coord.z = fixmul(coeffs->n.z3,t3) + fixmul(coeffs->n.z2,t2) + fixmul(coeffs->n.z1,t) + coeffs->n.z0;
return coord;
}
fix curve_dist(vms_equation *coeffs, int degree, fix t0, const vms_vector &p0, fix dist)
{
vms_vector coord;
fix t, diff;
if (degree != 3)
con_puts(CON_CRITICAL," for Hermite Curves degree must be 3");
for (t=t0;t<1*F1_0;t+=0.001*F1_0) {
coord = evaluate_curve(coeffs, 3, t);
diff = dist - vm_vec_dist(coord, p0);
if (diff<ACCURACY) //&&(diff>-ACCURACY))
return t;
}
return -1*F1_0;
}
void plot_parametric(vms_equation *coeffs, fix min_t, fix max_t, fix del_t) {
vms_vector coord, dcoord;
fix t, dt;
const uint8_t color = 15;
gr_box(*grd_curcanv, 75, 40, 325, 290, color);
gr_box(*grd_curcanv, 75, 310, 325, 560, color);
gr_box(*grd_curcanv,475, 310, 725, 560, color);
//gr_pal_fade_in( grd_curscreen->pal );
for (t=min_t;t<max_t-del_t;t+=del_t) {
dt = t+del_t;
coord = evaluate_curve(coeffs, 3, t);
dcoord = evaluate_curve(coeffs, 3, dt);
gr_line (*grd_curcanv, 75*F1_0 + coord.x, 290*F1_0 - coord.z, 75*F1_0 + dcoord.x, 290*F1_0 - dcoord.z, 9);
gr_line (*grd_curcanv, 75*F1_0 + coord.x, 560*F1_0 - coord.y, 75*F1_0 + dcoord.x, 560*F1_0 - dcoord.y, 10);
gr_line (*grd_curcanv, 475*F1_0 + coord.z, 560*F1_0 - coord.y, 475*F1_0 + dcoord.z, 560*F1_0 - dcoord.y, 12);
}
}
static vms_vector p1, p4, r1, r4;
static vms_vector r4t, r1save;
int generate_curve(const fix r1scale, const fix r4scale)
{
auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
auto &Vertices = LevelSharedVertexState.get_vertices();
vms_vector vec_dir, tvec;
vms_vector coord,prev_point;
vms_equation coeffs;
fix enddist, nextdist;
int firstsegflag;
fix t, maxscale;
fixang rangle, uangle;
const shared_segment &cursegp = Cursegp;
auto &vcvertptr = Vertices.vcptr;
compute_center_point_on_side(vcvertptr, p1, cursegp, Curside);
switch( Curside ) {
case sidenum_t::WLEFT:
extract_right_vector_from_segment(vcvertptr, cursegp, r1);
vm_vec_scale(r1, -F1_0 );
break;
case sidenum_t::WTOP:
extract_up_vector_from_segment(vcvertptr, cursegp, r1);
break;
case sidenum_t::WRIGHT:
extract_right_vector_from_segment(vcvertptr, cursegp, r1);
break;
case sidenum_t::WBOTTOM:
extract_up_vector_from_segment(vcvertptr, cursegp, r1);
vm_vec_scale(r1, -F1_0 );
break;
case sidenum_t::WBACK:
extract_forward_vector_from_segment(vcvertptr, cursegp, r1);
break;
case sidenum_t::WFRONT:
extract_forward_vector_from_segment(vcvertptr, cursegp, r1);
vm_vec_scale(r1, -F1_0 );
break;
}
const shared_segment &markedsegp = Markedsegp;
compute_center_point_on_side(vcvertptr, p4, markedsegp, Markedside);
switch( Markedside ) {
case sidenum_t::WLEFT:
extract_right_vector_from_segment(vcvertptr, markedsegp, r4);
extract_up_vector_from_segment(vcvertptr, markedsegp, r4t);
break;
case sidenum_t::WTOP:
extract_up_vector_from_segment(vcvertptr, markedsegp, r4);
vm_vec_scale(r4, -F1_0 );
extract_forward_vector_from_segment(vcvertptr, markedsegp, r4t);
vm_vec_scale(r4t, -F1_0 );
break;
case sidenum_t::WRIGHT:
extract_right_vector_from_segment(vcvertptr, markedsegp, r4);
vm_vec_scale(r4, -F1_0 );
extract_up_vector_from_segment(vcvertptr, markedsegp, r4t);
break;
case sidenum_t::WBOTTOM:
extract_up_vector_from_segment(vcvertptr, markedsegp, r4);
extract_forward_vector_from_segment(vcvertptr, markedsegp, r4t);
break;
case sidenum_t::WBACK:
extract_forward_vector_from_segment(vcvertptr, markedsegp, r4);
vm_vec_scale(r4, -F1_0 );
extract_up_vector_from_segment(vcvertptr, markedsegp, r4t);
break;
case sidenum_t::WFRONT:
extract_forward_vector_from_segment(vcvertptr, markedsegp, r4);
extract_up_vector_from_segment(vcvertptr, markedsegp, r4t);
break;
}
r1save = r1;
tvec = r1;
vm_vec_scale(r1,r1scale);
vm_vec_scale(r4,r4scale);
create_curve( p1, p4, r1, r4, coeffs );
OriginalSeg = Cursegp;
OriginalMarkedSeg = Markedsegp;
OriginalSide = Curside;
OriginalMarkedSide = Markedside;
CurveNumSegs = 0;
coord = prev_point = p1;
t=0;
firstsegflag = 1;
enddist = F1_0; nextdist = 0;
while ( enddist > fixmul( nextdist, 1.5*F1_0 )) {
vms_matrix rotmat;
if (firstsegflag==1)
firstsegflag=0;
else
extract_forward_vector_from_segment(vcvertptr, cursegp, tvec);
nextdist = vm_vec_mag(tvec); // nextdist := distance to next point
t = curve_dist(&coeffs, 3, t, prev_point, nextdist); // t = argument at which function is forward vector magnitude units away from prev_point (in 3-space, not along curve)
coord = evaluate_curve(&coeffs, 3, t); // coord := point about forward vector magnitude units away from prev_point
enddist = vm_vec_dist(coord, p4); // enddist := distance from current to end point, vec_dir used as a temporary variable
//vm_vec_normalize(vm_vec_sub(&vec_dir, &coord, &prev_point));
vm_vec_normalized_dir(vec_dir, coord, prev_point);
if (!med_attach_segment(Cursegp, vmsegptr(&New_segment), Curside, AttachSide))
{
med_extract_matrix_from_segment(cursegp, rotmat); // rotmat := matrix describing orientation of Cursegp
const auto tdest = vm_vec_rotate(vec_dir,rotmat); // tdest := vec_dir in reference frame of Cursegp
vec_dir = tdest;
const auto rotmat2 = vm_vector_2_matrix(vec_dir,nullptr,nullptr);
med_rotate_segment( Cursegp, rotmat2 );
prev_point = coord;
Curside = Side_opposite[AttachSide];
CurveSegs[CurveNumSegs]=Cursegp;
CurveNumSegs++;
} else return 0;
}
extract_up_vector_from_segment(vcvertptr, cursegp, tvec);
uangle = vm_vec_delta_ang( tvec, r4t, r4 );
if (uangle >= F1_0 * 1/8) uangle -= F1_0 * 1/4;
if (uangle >= F1_0 * 1/8) uangle -= F1_0 * 1/4;
if (uangle <= -F1_0 * 1/8) uangle += F1_0 * 1/4;
if (uangle <= -F1_0 * 1/8) uangle += F1_0 * 1/4;
extract_right_vector_from_segment(vcvertptr, cursegp, tvec);
rangle = vm_vec_delta_ang( tvec, r4t, r4 );
if (rangle >= F1_0/8) rangle -= F1_0/4;
if (rangle >= F1_0/8) rangle -= F1_0/4;
if (rangle <= -F1_0/8) rangle += F1_0/4;
if (rangle <= -F1_0/8) rangle += F1_0/4;
if ((uangle != 0) && (rangle != 0)) {
maxscale = CurveNumSegs*F1_0;
generate_banked_curve(maxscale, coeffs);
}
if (CurveNumSegs) {
med_form_bridge_segment( Cursegp, Side_opposite[AttachSide], Markedsegp, Markedside );
CurveSegs[CurveNumSegs] = vmsegptr(Markedsegp->shared_segment::children[Markedside]);
CurveNumSegs++;
}
Cursegp = OriginalSeg;
Curside = OriginalSide;
med_create_new_segment_from_cursegp();
//warn_if_concave_segments();
if (CurveNumSegs) return 1;
else return 0;
}
[[nodiscard]]
static inline vms_matrix vm_vec_ang_2_matrix (const vms_vector &v, fixang a)
{
vms_matrix m;
return vm_vec_ang_2_matrix(m, v, a), m;
}
void generate_banked_curve(const fix maxscale, vms_equation coeffs)
{
auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
auto &Vertices = LevelSharedVertexState.get_vertices();
vms_vector vec_dir, tvec, b4r4t;
vms_vector coord,prev_point;
fix enddist, nextdist;
int firstsegflag;
fixang rangle, uangle, angle, scaled_ang=0;
fix t;
if (CurveNumSegs) {
const shared_segment &cursegp = Cursegp;
auto &vcvertptr = Vertices.vcptr;
extract_up_vector_from_segment(vcvertptr, cursegp, b4r4t);
uangle = vm_vec_delta_ang( b4r4t, r4t, r4 );
if (uangle >= F1_0 * 1/8) uangle -= F1_0 * 1/4;
if (uangle >= F1_0 * 1/8) uangle -= F1_0 * 1/4;
if (uangle <= -F1_0 * 1/8) uangle += F1_0 * 1/4;
if (uangle <= -F1_0 * 1/8) uangle += F1_0 * 1/4;
extract_right_vector_from_segment(vcvertptr, cursegp, b4r4t);
rangle = vm_vec_delta_ang( b4r4t, r4t, r4 );
if (rangle >= F1_0/8) rangle -= F1_0/4;
if (rangle >= F1_0/8) rangle -= F1_0/4;
if (rangle <= -F1_0/8) rangle += F1_0/4;
if (rangle <= -F1_0/8) rangle += F1_0/4;
angle = uangle;
if (abs(rangle) < abs(uangle)) angle = rangle;
delete_curve();
coord = prev_point = p1;
#define MAGIC_NUM 0.707*F1_0
if (maxscale)
scaled_ang = fixdiv(angle,fixmul(maxscale,MAGIC_NUM));
t=0;
tvec = r1save;
firstsegflag = 1;
enddist = F1_0; nextdist = 0;
while ( enddist > fixmul( nextdist, 1.5*F1_0 )) {
vms_matrix rotmat;
if (firstsegflag==1)
firstsegflag=0;
else
extract_forward_vector_from_segment(vcvertptr, cursegp, tvec);
nextdist = vm_vec_mag(tvec); // nextdist := distance to next point
t = curve_dist(&coeffs, 3, t, prev_point, nextdist); // t = argument at which function is forward vector magnitude units away from prev_point (in 3-space, not along curve)
coord = evaluate_curve(&coeffs, 3, t); // coord := point about forward vector magnitude units away from prev_point
enddist = vm_vec_dist(coord, p4); // enddist := distance from current to end point, vec_dir used as a temporary variable
//vm_vec_normalize(vm_vec_sub(&vec_dir, &coord, &prev_point));
vm_vec_normalized_dir(vec_dir, coord, prev_point);
if (!med_attach_segment(Cursegp, vmsegptr(&New_segment), Curside, AttachSide))
{
med_extract_matrix_from_segment(cursegp, rotmat); // rotmat := matrix describing orientation of Cursegp
const auto tdest = vm_vec_rotate(vec_dir,rotmat); // tdest := vec_dir in reference frame of Cursegp
vec_dir = tdest;
const auto rotmat2 = vm_vec_ang_2_matrix(vec_dir,scaled_ang);
med_rotate_segment( Cursegp, rotmat2 );
prev_point = coord;
Curside = Side_opposite[AttachSide];
CurveSegs[CurveNumSegs]=Cursegp;
CurveNumSegs++;
}
}
}
}
void delete_curve() {
range_for (auto &i, partial_const_range(CurveSegs, CurveNumSegs))
{
if (i->shared_segment::segnum != segment_none)
med_delete_segment(vmsegptridx(i));
}
Markedsegp = OriginalMarkedSeg;
Markedside = OriginalMarkedSide;
Cursegp = OriginalSeg;
Curside = OriginalSide;
med_create_new_segment_from_cursegp();
CurveNumSegs = 0;
//editor_status("");
//warn_if_concave_segments();
}