Serious-Engine/Sources/Engine/Math/Clipping.inl

415 lines
9.8 KiB
C++
Executable File

#ifndef SE_INCL_CLIPPING_INL
#define SE_INCL_CLIPPING_INL
#ifdef PRAGMA_ONCE
#pragma once
#endif
/*
* Line clipping flags
*/
#define LCF_REMOVED (0x00L) // the entire edge is invisible
#define LCF_UNCLIPPED (0x80L) // this point remains unclipped
#define LCF_NEAR (0x01L) // this point is clipped at near plane
#define LCF_FAR (0x02L) // this point is clipped at far plane
#define LCF_LEFT (0x04L) // this point is clipped at left plane
#define LCF_RIGHT (0x08L) // this point is clipped at right plane
#define LCF_TOP (0x10L) // this point is clipped at top plane
#define LCF_BOTTOM (0x20L) // this point is clipped at bottom plane
#define LCF_EDGEREMOVED (0x0000L) // used for testing if entire edge is removed
// shifts used for clip flags for start/end vertex
#define LCS_VERTEX0 (0)
#define LCS_VERTEX1 (8)
// masks used for clip flags for start/end vertex
#define LCM_VERTEX0 (0x00FFL)
#define LCM_VERTEX1 (0xFF00L)
// creating line clip flags for start/end vertex
#define LCFVERTEX0(lcf) ((lcf)<<LCS_VERTEX0)
#define LCFVERTEX1(lcf) ((lcf)<<LCS_VERTEX1)
// asm shortcuts
#define O offset
#define Q qword ptr
#define D dword ptr
#define W word ptr
#define B byte ptr
/*
* Intersecting object (works on edges)
*/
class ENGINE_API CIntersector {
private:
FLOAT ci_fX0; // relative coordinates of point
FLOAT ci_fY0;
INDEX ci_ct;
public:
// constructor
inline CIntersector(FLOAT fX0=0.0f, FLOAT fY0=0.0f)
: ci_fX0(fX0), ci_fY0(fY0), ci_ct(0) {};
inline void Clear(void) { ci_ct = 0;}; // Clears intersection count
inline void AddEdge( FLOAT fedgx1, FLOAT fedgy1, FLOAT fedgx2, FLOAT fedgy2); // Checks for intersection
inline BOOL IsIntersecting() { return (ci_ct % 2) != 0; }; // Do we have intersection?
};
// inline functions implementation
/////////////////////////////////////////////////////////////////////
// CIntersector
/////////////////////////////////////////////////////////////////////
/*
* Checks for intersection of edge with +x axis
*/
ENGINE_API inline void CIntersector::AddEdge( FLOAT fedgx1, FLOAT fedgy1, FLOAT fedgx2, FLOAT fedgy2)
{
// transform edge relative to the origin
fedgx1-=ci_fX0; fedgy1-=ci_fY0;
fedgx2-=ci_fX0; fedgy2-=ci_fY0;
if( fedgy1 > 0)
{
if( fedgy2 > 0) return;
if( fedgx1 <= 0)
{
if( fedgx2 <= 0) return;
}
else if( fedgx2 > 0)
{
ci_ct ++;
return;
}
}
else
{
if( fedgy2 <= 0) return;
if( fedgx1 <= 0)
{
if( fedgx2 <= 0) return;
}
else if( fedgx2 > 0)
{
ci_ct ++;
return;
}
}
// here we calculate x coordinate of edge and +x axis intersection point
FLOAT a, b;
a = (fedgy2 - fedgy1)/(fedgx2 - fedgx1);
b = fedgy1 - a*fedgx1;
if( -b/a < 0) return; // no intersection, intersection coordinate x is left from 0
ci_ct ++;
}
/* rcg10042001 !!! FIXME */
#ifdef _MSC_VER
#define ASMOPT 1
#endif
// how much are the clip planes offset inside frustum
#define CLIPPLANE_EPSILON (1E-3f)
/*
* Clip a line by a single plane -- helper function.
*/
inline BOOL ClipLineByNearPlane(FLOAT3D &v0, FLOAT3D &v1, FLOAT fPlaneDistance,
ULONG &ulCode0, ULONG &ulCode1, ULONG ulCodeClip)
{
#if ASMOPT
static FLOAT f1=1;
static FLOAT fDistance0, fDistance1;
__asm {
mov esi,D [v0]
mov edi,D [v1]
fld D [fPlaneDistance]
fchs
fld D [esi+ 8]
fsubr st(0),st(1)
fld D [edi+ 8]
fsubp st(2),st(0)
fstp D [fDistance0]
fst D [fDistance1]
fsubr D [fDistance0]
fdivr D [f1]
cmp D [fDistance0],0
jg firstFront
;firstBack:
cmp D [fDistance1],0
jle falseRet
;secondFront:
mov eax,D [fPlaneDistance]
mov ebx,D [ulCode0]
xor eax,80000000h
mov edx,D [ulCodeClip]
mov D [esi+ 8],eax
mov D [ebx],edx
fmul D [fDistance0]
;//st0=fFactor
fld D [esi+ 0]
fsub D [edi+ 0]
fld D [esi+ 4]
fsub D [edi+ 4]
;//st0=v0(2)-v1(2), st1=v0(1)-v1(1), st2=fFactor
fxch st(1)
fmul st(0),st(2)
fxch st(1)
fmulp st(2),st(0)
;//st0=(v0(1)-v1(1))*fFactor, st1=(v0(2)-v1(2))*fFactor
fsubr D [esi+ 0]
fxch st(1)
fsubr D [esi+ 4]
fxch st(1)
fstp D [esi+ 0]
fst D [esi+ 4]
jmp trueRet
firstFront:
cmp D [fDistance1],0
jg trueRet
;secondBack:
mov eax,D [fPlaneDistance]
mov ebx,D [ulCode1]
mov edx,D [ulCodeClip]
xor eax,80000000h
shl edx,8
mov D [edi+ 8],eax
mov D [ebx],edx
fmul D [fDistance1]
;//st0=fFactor
fld D [esi+ 0]
fsub D [edi+ 0]
fld D [esi+ 4]
fsub D [edi+ 4]
;//st0=v0(2)-v1(2), st1=v0(1)-v1(1), st2=fFactor
fxch st(1)
fmul st(0),st(2)
fxch st(1)
fmulp st(2),st(0)
;//st0=(v0(1)-v1(1))*fFactor, st1=(v0(2)-v1(2))*fFactor
fsubr D [edi+ 0]
fxch st(1)
fsubr D [edi+ 4]
fxch st(1)
fstp D [edi+ 0]
fst D [edi+ 4]
}
trueRet:
_asm fstp st(0)
return TRUE;
falseRet:
_asm fstp st(0)
return FALSE;
#else
// calculate point distances from clip plane
FLOAT fDistance0 = -fPlaneDistance-v0(3);
FLOAT fDistance1 = -fPlaneDistance-v1(3);
if (fDistance0<=0) {
// if both are back
if (fDistance1<=0) {
// no line remains
return FALSE;
// if first is back, second front
} else {
// clip first
FLOAT fDivisor = 1.0f/(fDistance0-fDistance1);
FLOAT fFactor = fDistance0*fDivisor;
v0(1) = v0(1)-(v0(1)-v1(1))*fFactor;
v0(2) = v0(2)-(v0(2)-v1(2))*fFactor;
v0(3) = -fPlaneDistance;
// mark that first was clipped
ulCode0 = LCFVERTEX0(ulCodeClip);
// line remains
return TRUE;
}
} else {
// if first is front, second back
if (fDistance1<=0) {
// clip second
FLOAT fDivisor = 1.0f/(fDistance0-fDistance1);
FLOAT fFactor = fDistance1*fDivisor;
v1(1) = v1(1)-(v0(1)-v1(1))*fFactor;
v1(2) = v1(2)-(v0(2)-v1(2))*fFactor;
v1(3) = -fPlaneDistance;
// mark that second was clipped
ulCode1 = LCFVERTEX1(ulCodeClip);
// line remains
return TRUE;
// if both are front
} else {
// line remains unclipped
return TRUE;
}
}
#endif
}
/*
* Clip a line by a single plane -- helper function.
*/
inline BOOL ClipLineByFarPlane(FLOAT3D &v0, FLOAT3D &v1, FLOAT fPlaneDistance,
ULONG &ulCode0, ULONG &ulCode1, ULONG ulCodeClip)
{
#if ASMOPT
static FLOAT f1=1;
static FLOAT fDistance0, fDistance1;
__asm {
mov esi,D [v0]
mov edi,D [v1]
fld D [fPlaneDistance]
fadd D [esi+ 8]
fld D [fPlaneDistance]
fadd D [edi+ 8]
fxch st(1)
fstp D [fDistance0]
fst D [fDistance1]
fsubr D [fDistance0]
fdivr D [f1]
cmp D [fDistance0],0
jg firstFront
;firstBack:
cmp D [fDistance1],0
jle falseRet
;secondFront:
mov eax,D [fPlaneDistance]
mov ebx,D [ulCode0]
xor eax,80000000h
mov edx,D [ulCodeClip]
mov D [esi+ 8],eax
mov D [ebx],edx
fmul D [fDistance0]
;//st0=fFactor
fld D [esi+ 0]
fsub D [edi+ 0]
fld D [esi+ 4]
fsub D [edi+ 4]
;//st0=v0(2)-v1(2), st1=v0(1)-v1(1), st2=fFactor
fxch st(1)
fmul st(0),st(2)
fxch st(1)
fmulp st(2),st(0)
;//st0=(v0(1)-v1(1))*fFactor, st1=(v0(2)-v1(2))*fFactor
fsubr D [esi+ 0]
fxch st(1)
fsubr D [esi+ 4]
fxch st(1)
fstp D [esi+ 0]
fst D [esi+ 4]
jmp trueRet
firstFront:
cmp D [fDistance1],0
jg trueRet
;secondBack:
mov eax,D [fPlaneDistance]
mov ebx,D [ulCode1]
mov edx,D [ulCodeClip]
xor eax,80000000h
shl edx,8
mov D [edi+8],eax
mov D [ebx],edx
fmul D [fDistance1]
;//st0=fFactor
fld D [esi+ 0]
fsub D [edi+ 0]
fld D [esi+ 4]
fsub D [edi+ 4]
;//st0=v0(2)-v1(2), st1=v0(1)-v1(1), st2=fFactor
fxch st(1)
fmul st(0),st(2)
fxch st(1)
fmulp st(2),st(0)
;//st0=(v0(1)-v1(1))*fFactor, st1=(v0(2)-v1(2))*fFactor
fsubr D [edi+ 0]
fxch st(1)
fsubr D [edi+ 4]
fxch st(1)
fstp D [edi+ 0]
fst D [edi+ 4]
}
trueRet:
_asm fstp st(0)
return TRUE;
falseRet:
_asm fstp st(0)
return FALSE;
#else
// calculate point distances from clip plane
FLOAT fDistance0 = fPlaneDistance+v0(3);
FLOAT fDistance1 = fPlaneDistance+v1(3);
if (fDistance0<=0) {
// if both are back
if (fDistance1<=0) {
// no line remains
return FALSE;
// if first is back, second front
} else {
// clip first
FLOAT fDivisor = 1.0f/(fDistance0-fDistance1);
FLOAT fFactor = fDistance0*fDivisor;
v0(1) = v0(1)-(v0(1)-v1(1))*fFactor;
v0(2) = v0(2)-(v0(2)-v1(2))*fFactor;
v0(3) = -fPlaneDistance;
// mark that first was clipped
ulCode0 = LCFVERTEX0(ulCodeClip);
// line remains
return TRUE;
}
} else {
// if first is front, second back
if (fDistance1<=0) {
// clip second
FLOAT fDivisor = 1.0f/(fDistance0-fDistance1);
FLOAT fFactor = fDistance1*fDivisor;
v1(1) = v1(1)-(v0(1)-v1(1))*fFactor;
v1(2) = v1(2)-(v0(2)-v1(2))*fFactor;
v1(3) = -fPlaneDistance;
// mark that second was clipped
ulCode1 = LCFVERTEX1(ulCodeClip);
// line remains
return TRUE;
// if both are front
} else {
// line remains unclipped
return TRUE;
}
}
#endif
}
static inline void MakeClipPlane(const FLOAT3D &vN, FLOAT fD, FLOATplane3D &pl)
{
FLOAT fOoL = 1.0f/vN.Length();
pl = FLOATplane3D(vN*fOoL, fD*fOoL);
}
#undef ASMOPT
#undef O
#undef Q
#undef D
#undef W
#undef B
#endif /* include-once blocker. */