dxx-rebirth/common/3d/clipper.cpp

210 lines
5.6 KiB
C++
Raw Normal View History

2006-03-20 17:12:09 +00:00
/*
* This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>.
* It is copyright by its individual contributors, as recorded in the
* project's Git history. See COPYING.txt at the top level for license
* terms and a link to the Git history.
*/
2006-03-20 17:12:09 +00:00
#include "3d.h"
#include "globvars.h"
#include "clipper.h"
#include "dxxerror.h"
2006-03-20 17:12:09 +00:00
2014-11-16 19:14:51 +00:00
#include "compiler-range_for.h"
#include <stdexcept>
2014-11-13 03:19:45 +00:00
2015-12-13 18:00:49 +00:00
namespace dcx {
2015-12-05 22:57:24 +00:00
#if !DXX_USE_OGL
2014-11-16 19:14:51 +00:00
temporary_points_t::temporary_points_t() :
free_point_num(0)
2006-03-20 17:12:09 +00:00
{
2014-11-16 19:14:51 +00:00
auto p = &temp_points.front();
range_for (auto &f, free_points)
f = p++;
2006-03-20 17:12:09 +00:00
}
2020-12-26 21:17:29 +00:00
namespace {
2014-11-16 19:14:51 +00:00
static g3s_point &get_temp_point(temporary_points_t &t)
2006-03-20 17:12:09 +00:00
{
2014-11-16 19:14:51 +00:00
if (t.free_point_num >= t.free_points.size())
throw std::out_of_range("not enough free points");
auto &p = *t.free_points[t.free_point_num++];
2022-10-02 19:51:35 +00:00
p.p3_flags = projection_flag::temp_point;
2006-03-20 17:12:09 +00:00
return p;
}
2020-12-26 21:17:29 +00:00
}
void temporary_points_t::free_temp_point(g3s_point &p)
2006-03-20 17:12:09 +00:00
{
2022-10-02 19:51:35 +00:00
if (!(p.p3_flags & projection_flag::temp_point))
2014-11-16 19:14:51 +00:00
throw std::invalid_argument("freeing non-temporary point");
if (--free_point_num >= free_points.size())
throw std::out_of_range("too many free points");
free_points[free_point_num] = &p;
2022-10-02 19:51:35 +00:00
p.p3_flags &= ~projection_flag::temp_point;
2006-03-20 17:12:09 +00:00
}
2020-12-26 21:17:29 +00:00
namespace {
2006-03-20 17:12:09 +00:00
//clips an edge against one plane.
static g3s_point &clip_edge(const clipping_code plane_flag, g3s_point *on_pnt, g3s_point *off_pnt, temporary_points_t &tp)
2006-03-20 17:12:09 +00:00
{
fix psx_ratio;
fix a,b,kn,kd;
//compute clipping value k = (xs-zs) / (xs-xe-zs+ze)
//use x or y as appropriate, and negate x/y value as appropriate
if ((plane_flag & (clipping_code::off_right | clipping_code::off_left)) != clipping_code::None)
{
2006-03-20 17:12:09 +00:00
a = on_pnt->p3_x;
b = off_pnt->p3_x;
}
else {
a = on_pnt->p3_y;
b = off_pnt->p3_y;
}
if ((plane_flag & (clipping_code::off_left | clipping_code::off_bot)) != clipping_code::None)
{
2006-03-20 17:12:09 +00:00
a = -a;
b = -b;
}
kn = a - on_pnt->p3_z; //xs-zs
kd = kn - b + off_pnt->p3_z; //xs-zs-xe+ze
2014-11-16 19:14:51 +00:00
auto &tmp = get_temp_point(tp);
2006-03-20 17:12:09 +00:00
psx_ratio = fixdiv( kn, kd );
2014-11-16 19:14:51 +00:00
tmp.p3_x = on_pnt->p3_x + fixmul( (off_pnt->p3_x-on_pnt->p3_x), psx_ratio);
tmp.p3_y = on_pnt->p3_y + fixmul( (off_pnt->p3_y-on_pnt->p3_y), psx_ratio);
2006-03-20 17:12:09 +00:00
if ((plane_flag & (clipping_code::off_top | clipping_code::off_bot)) != clipping_code::None)
2014-11-16 19:14:51 +00:00
tmp.p3_z = tmp.p3_y;
2006-03-20 17:12:09 +00:00
else
2014-11-16 19:14:51 +00:00
tmp.p3_z = tmp.p3_x;
2006-03-20 17:12:09 +00:00
if ((plane_flag & (clipping_code::off_left | clipping_code::off_bot)) != clipping_code::None)
2014-11-16 19:14:51 +00:00
tmp.p3_z = -tmp.p3_z;
2006-03-20 17:12:09 +00:00
2022-10-02 19:51:35 +00:00
if (on_pnt->p3_flags & projection_flag::uvs) {
2006-03-20 17:12:09 +00:00
// PSX_HACK!!!!
2014-11-16 19:14:51 +00:00
// tmp.p3_u = on_pnt->p3_u + fixmuldiv(off_pnt->p3_u-on_pnt->p3_u,kn,kd);
// tmp.p3_v = on_pnt->p3_v + fixmuldiv(off_pnt->p3_v-on_pnt->p3_v,kn,kd);
tmp.p3_u = on_pnt->p3_u + fixmul((off_pnt->p3_u-on_pnt->p3_u), psx_ratio);
tmp.p3_v = on_pnt->p3_v + fixmul((off_pnt->p3_v-on_pnt->p3_v), psx_ratio);
2006-03-20 17:12:09 +00:00
2022-10-02 19:51:35 +00:00
tmp.p3_flags |= projection_flag::uvs;
2006-03-20 17:12:09 +00:00
}
2022-10-02 19:51:35 +00:00
if (on_pnt->p3_flags & projection_flag::ls) {
2006-03-20 17:12:09 +00:00
// PSX_HACK
2014-11-16 19:14:51 +00:00
// tmp.p3_r = on_pnt->p3_r + fixmuldiv(off_pnt->p3_r-on_pnt->p3_r,kn,kd);
// tmp.p3_g = on_pnt->p3_g + fixmuldiv(off_pnt->p3_g-on_pnt->p3_g,kn,kd);
// tmp.p3_b = on_pnt->p3_b + fixmuldiv(off_pnt->p3_b-on_pnt->p3_b,kn,kd);
tmp.p3_l = on_pnt->p3_l + fixmul((off_pnt->p3_l-on_pnt->p3_l), psx_ratio);
2022-10-02 19:51:35 +00:00
tmp.p3_flags |= projection_flag::ls;
2006-03-20 17:12:09 +00:00
}
2014-11-16 19:14:51 +00:00
g3_code_point(tmp);
return tmp;
2006-03-20 17:12:09 +00:00
}
2020-12-26 21:17:29 +00:00
}
2006-03-20 17:12:09 +00:00
//clips a line to the viewing pyramid.
void clip_line(g3s_point *&p0, g3s_point *&p1, const clipping_code codes_or, temporary_points_t &tp)
2006-03-20 17:12:09 +00:00
{
//might have these left over
2022-10-02 19:51:35 +00:00
p0->p3_flags &= ~(projection_flag::uvs|projection_flag::ls);
p1->p3_flags &= ~(projection_flag::uvs|projection_flag::ls);
2006-03-20 17:12:09 +00:00
for (uint8_t plane_step = 1; plane_step < 16; plane_step <<= 1)
{
const clipping_code plane_flag{plane_step};
if ((codes_or & plane_flag) != clipping_code::None)
{
if ((p0->p3_codes & plane_flag) != clipping_code::None)
2014-11-13 03:19:45 +00:00
std::swap(p0, p1);
auto &old_p1 = *std::exchange(p1, &clip_edge(plane_flag, p0, p1, tp));
2022-10-02 19:51:35 +00:00
if (old_p1.p3_flags & projection_flag::temp_point)
2014-11-16 19:14:51 +00:00
tp.free_temp_point(old_p1);
2006-03-20 17:12:09 +00:00
}
}
2006-03-20 17:12:09 +00:00
}
2020-12-26 21:17:29 +00:00
namespace {
static int clip_plane(const clipping_code plane_flag, polygon_clip_points &src, polygon_clip_points &dest, int *nv, g3s_codes *const cc, temporary_points_t &tp)
2006-03-20 17:12:09 +00:00
{
//copy first two verts to end
src[*nv] = src[0];
src[*nv+1] = src[1];
*cc = {};
2006-03-20 17:12:09 +00:00
2014-11-16 19:14:50 +00:00
uint_fast32_t j = 0;
for (int i=1;i<=*nv;i++) {
2006-03-20 17:12:09 +00:00
if ((src[i]->p3_codes & plane_flag) != clipping_code::None)
{ //cur point off?
2006-03-20 17:12:09 +00:00
if ((src[i-1]->p3_codes & plane_flag) == clipping_code::None)
{ //prev not off?
2014-11-16 19:14:51 +00:00
dest[j] = &clip_edge(plane_flag,src[i-1],src[i],tp);
2014-11-16 19:14:50 +00:00
cc->uor |= dest[j]->p3_codes;
cc->uand &= dest[j]->p3_codes;
++j;
2006-03-20 17:12:09 +00:00
}
if ((src[i+1]->p3_codes & plane_flag) == clipping_code::None)
{
2014-11-16 19:14:51 +00:00
dest[j] = &clip_edge(plane_flag,src[i+1],src[i],tp);
2014-11-16 19:14:50 +00:00
cc->uor |= dest[j]->p3_codes;
cc->uand &= dest[j]->p3_codes;
++j;
2006-03-20 17:12:09 +00:00
}
//see if must free discarded point
2022-10-02 19:51:35 +00:00
if (src[i]->p3_flags & projection_flag::temp_point)
tp.free_temp_point(*src[i]);
2006-03-20 17:12:09 +00:00
}
else { //cur not off, copy to dest buffer
2014-11-16 19:14:50 +00:00
dest[j++] = src[i];
2006-03-20 17:12:09 +00:00
cc->uor |= src[i]->p3_codes;
cc->uand &= src[i]->p3_codes;
2006-03-20 17:12:09 +00:00
}
}
2014-11-16 19:14:50 +00:00
return j;
2006-03-20 17:12:09 +00:00
}
2020-12-26 21:17:29 +00:00
}
2014-11-16 19:14:51 +00:00
const polygon_clip_points &clip_polygon(polygon_clip_points &rsrc,polygon_clip_points &rdest,int *nv,g3s_codes *cc, temporary_points_t &tp)
2006-03-20 17:12:09 +00:00
{
polygon_clip_points *src = &rsrc, *dest = &rdest;
for (uint8_t plane_step = 1; plane_step < 16; plane_step <<= 1)
{
const clipping_code plane_flag{plane_step};
if ((cc->uor & plane_flag) != clipping_code::None)
{
2014-11-16 19:14:51 +00:00
*nv = clip_plane(plane_flag,*src,*dest,nv,cc,tp);
if (cc->uand != clipping_code::None) //clipped away
return *dest;
2006-03-20 17:12:09 +00:00
2014-08-20 01:50:40 +00:00
std::swap(src, dest);
2006-03-20 17:12:09 +00:00
}
}
2006-03-20 17:12:09 +00:00
return *src; //we swapped after we copied
2006-03-20 17:12:09 +00:00
}
#endif
2006-03-20 17:12:09 +00:00
2015-12-05 22:57:24 +00:00
}