Initial commit

This commit is contained in:
Brian Harris
2012-11-26 12:58:24 -06:00
parent a5214f79ef
commit 5016f605b8
1115 changed files with 587266 additions and 0 deletions

View File

@@ -0,0 +1,76 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#pragma hdrstop
#include "../precompiled.h"
/*
============
idShadowVert::CreateShadowCache
============
*/
int idShadowVert::CreateShadowCache( idShadowVert * vertexCache, const idDrawVert *verts, const int numVerts ) {
for ( int i = 0; i < numVerts; i++ ) {
vertexCache[i*2+0].xyzw[0] = verts[i].xyz[0];
vertexCache[i*2+0].xyzw[1] = verts[i].xyz[1];
vertexCache[i*2+0].xyzw[2] = verts[i].xyz[2];
vertexCache[i*2+0].xyzw[3] = 1.0f;
vertexCache[i*2+1].xyzw[0] = verts[i].xyz[0];
vertexCache[i*2+1].xyzw[1] = verts[i].xyz[1];
vertexCache[i*2+1].xyzw[2] = verts[i].xyz[2];
vertexCache[i*2+1].xyzw[3] = 0.0f;
}
return numVerts * 2;
}
/*
===================
idShadowVertSkinned::CreateShadowCache
===================
*/
int idShadowVertSkinned::CreateShadowCache( idShadowVertSkinned * vertexCache, const idDrawVert *verts, const int numVerts ) {
for ( int i = 0; i < numVerts; i++ ) {
vertexCache[0].xyzw[0] = verts[i].xyz[0];
vertexCache[0].xyzw[1] = verts[i].xyz[1];
vertexCache[0].xyzw[2] = verts[i].xyz[2];
vertexCache[0].xyzw[3] = 1.0f;
*(unsigned int *)vertexCache[0].color = *(unsigned int *)verts[i].color;
*(unsigned int *)vertexCache[0].color2 = *(unsigned int *)verts[i].color2;
vertexCache[1].xyzw[0] = verts[i].xyz[0];
vertexCache[1].xyzw[1] = verts[i].xyz[1];
vertexCache[1].xyzw[2] = verts[i].xyz[2];
vertexCache[1].xyzw[3] = 0.0f;
*(unsigned int *)vertexCache[1].color = *(unsigned int *)verts[i].color;
*(unsigned int *)vertexCache[1].color2 = *(unsigned int *)verts[i].color2;
vertexCache += 2;
}
return numVerts * 2;
}

View File

@@ -0,0 +1,739 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#ifndef __DRAWVERT_H__
#define __DRAWVERT_H__
// The hardware converts a byte to a float by division with 255 and in the
// vertex programs we convert the floating-point value in the range [0, 1]
// to the range [-1, 1] by multiplying with 2 and subtracting 1.
#define VERTEX_BYTE_TO_FLOAT( x ) ( (x) * ( 2.0f / 255.0f ) - 1.0f )
#define VERTEX_FLOAT_TO_BYTE( x ) idMath::Ftob( ( (x) + 1.0f ) * ( 255.0f / 2.0f ) + 0.5f )
// The hardware converts a byte to a float by division with 255 and in the
// fragment programs we convert the floating-point value in the range [0, 1]
// to the range [-1, 1] by multiplying with 2 and subtracting 1.
// This is the conventional OpenGL mapping which specifies an exact
// representation for -1 and +1 but not 0. The DirectX 10 mapping is
// in the comments which specifies a non-linear mapping with an exact
// representation of -1, 0 and +1 but -1 is represented twice.
#define NORMALMAP_BYTE_TO_FLOAT( x ) VERTEX_BYTE_TO_FLOAT( x ) //( (x) - 128.0f ) * ( 1.0f / 127.0f )
#define NORMALMAP_FLOAT_TO_BYTE( x ) VERTEX_FLOAT_TO_BYTE( x ) //idMath::Ftob( 128.0f + 127.0f * (x) + 0.5f )
/*
================================================
halfFloat_t
================================================
*/
typedef unsigned short halfFloat_t;
// GPU half-float bit patterns
#define HF_MANTISSA(x) (x&1023)
#define HF_EXP(x) ((x&32767)>>10)
#define HF_SIGN(x) ((x&32768)?-1:1)
/*
========================
F16toF32
========================
*/
ID_INLINE float F16toF32( halfFloat_t x ) {
int e = HF_EXP( x );
int m = HF_MANTISSA( x );
int s = HF_SIGN( x );
if ( 0 < e && e < 31 ) {
return s * powf( 2.0f, ( e - 15.0f ) ) * ( 1 + m / 1024.0f );
} else if ( m == 0 ) {
return s * 0.0f;
}
return s * powf( 2.0f, -14.0f ) * ( m / 1024.0f );
}
/*
========================
F32toF16
========================
*/
ID_INLINE halfFloat_t F32toF16( float a ) {
unsigned int f = *(unsigned *)( &a );
unsigned int signbit = ( f & 0x80000000 ) >> 16;
int exponent = ( ( f & 0x7F800000 ) >> 23 ) - 112;
unsigned int mantissa = ( f & 0x007FFFFF );
if ( exponent <= 0 ) {
return 0;
}
if ( exponent > 30 ) {
return (halfFloat_t)( signbit | 0x7BFF );
}
return (halfFloat_t)( signbit | ( exponent << 10 ) | ( mantissa >> 13 ) );
}
/*
===============================================================================
Draw Vertex.
===============================================================================
*/
class idDrawVert {
public:
idVec3 xyz; // 12 bytes
halfFloat_t st[2]; // 4 bytes
byte normal[4]; // 4 bytes
byte tangent[4]; // 4 bytes -- [3] is texture polarity sign
byte color[4]; // 4 bytes
byte color2[4]; // 4 bytes -- weights for skinning
float operator[]( const int index ) const;
float & operator[]( const int index );
void Clear();
const idVec3 GetNormal() const;
const idVec3 GetNormalRaw() const; // not re-normalized for renderbump
// must be normalized already!
void SetNormal( float x, float y, float z );
void SetNormal( const idVec3 & n );
const idVec3 GetTangent() const;
const idVec3 GetTangentRaw() const; // not re-normalized for renderbump
// must be normalized already!
void SetTangent( float x, float y, float z );
void SetTangent( const idVec3 & t );
// derived from normal, tangent, and tangent flag
const idVec3 GetBiTangent() const;
const idVec3 GetBiTangentRaw() const; // not re-normalized for renderbump
void SetBiTangent( float x, float y, float z );
ID_INLINE void SetBiTangent( const idVec3 & t );
float GetBiTangentSign() const;
byte GetBiTangentSignBit() const;
void SetTexCoordNative( const halfFloat_t s, const halfFloat_t t );
void SetTexCoord( const idVec2 & st );
void SetTexCoord( float s, float t );
void SetTexCoordS( float s );
void SetTexCoordT( float t );
const idVec2 GetTexCoord() const;
const halfFloat_t GetTexCoordNativeS() const;
const halfFloat_t GetTexCoordNativeT() const;
// either 1.0f or -1.0f
ID_INLINE void SetBiTangentSign( float sign );
ID_INLINE void SetBiTangentSignBit( byte bit );
void Lerp( const idDrawVert &a, const idDrawVert &b, const float f );
void LerpAll( const idDrawVert &a, const idDrawVert &b, const float f );
void SetColor( dword color );
void SetNativeOrderColor( dword color );
dword GetColor() const;
void SetColor2( dword color );
void SetNativeOrderColor2( dword color );
void ClearColor2();
dword GetColor2() const;
static idDrawVert GetSkinnedDrawVert( const idDrawVert & vert, const idJointMat * joints );
static idVec3 GetSkinnedDrawVertPosition( const idDrawVert & vert, const idJointMat * joints );
};
#define DRAWVERT_SIZE 32
#define DRAWVERT_XYZ_OFFSET (0*4)
#define DRAWVERT_ST_OFFSET (3*4)
#define DRAWVERT_NORMAL_OFFSET (4*4)
#define DRAWVERT_TANGENT_OFFSET (5*4)
#define DRAWVERT_COLOR_OFFSET (6*4)
#define DRAWVERT_COLOR2_OFFSET (7*4)
assert_offsetof( idDrawVert, xyz, DRAWVERT_XYZ_OFFSET );
assert_offsetof( idDrawVert, normal, DRAWVERT_NORMAL_OFFSET );
assert_offsetof( idDrawVert, tangent, DRAWVERT_TANGENT_OFFSET );
/*
========================
VertexFloatToByte
Assumes input is in the range [-1, 1]
========================
*/
ID_INLINE void VertexFloatToByte( const float & x, const float & y, const float & z, byte * bval ) {
assert_4_byte_aligned( bval ); // for __stvebx
const __m128 vector_float_one = { 1.0f, 1.0f, 1.0f, 1.0f };
const __m128 vector_float_half = { 0.5f, 0.5f, 0.5f, 0.5f };
const __m128 vector_float_255_over_2 = { 255.0f / 2.0f, 255.0f / 2.0f, 255.0f / 2.0f, 255.0f / 2.0f };
const __m128 xyz = _mm_unpacklo_ps( _mm_unpacklo_ps( _mm_load_ss( &x ), _mm_load_ss( &z ) ), _mm_load_ss( &y ) );
const __m128 xyzScaled = _mm_madd_ps( _mm_add_ps( xyz, vector_float_one ), vector_float_255_over_2, vector_float_half );
const __m128i xyzInt = _mm_cvtps_epi32( xyzScaled );
const __m128i xyzShort = _mm_packs_epi32( xyzInt, xyzInt );
const __m128i xyzChar = _mm_packus_epi16( xyzShort, xyzShort );
const __m128i xyz16 = _mm_unpacklo_epi8( xyzChar, _mm_setzero_si128() );
bval[0] = (byte)_mm_extract_epi16( xyz16, 0 ); // cannot use _mm_extract_epi8 because it is an SSE4 instruction
bval[1] = (byte)_mm_extract_epi16( xyz16, 1 );
bval[2] = (byte)_mm_extract_epi16( xyz16, 2 );
}
/*
========================
idDrawVert::operator[]
========================
*/
ID_INLINE float idDrawVert::operator[]( const int index ) const {
assert( index >= 0 && index < 5 );
return ((float *)(&xyz))[index];
}
/*
========================
idDrawVert::operator[]
========================
*/
ID_INLINE float &idDrawVert::operator[]( const int index ) {
assert( index >= 0 && index < 5 );
return ((float *)(&xyz))[index];
}
/*
========================
idDrawVert::Clear
========================
*/
ID_INLINE void idDrawVert::Clear() {
*reinterpret_cast<dword *>(&this->xyz.x) = 0;
*reinterpret_cast<dword *>(&this->xyz.y) = 0;
*reinterpret_cast<dword *>(&this->xyz.z) = 0;
*reinterpret_cast<dword *>(this->st) = 0;
*reinterpret_cast<dword *>(this->normal) = 0x00FF8080; // x=0, y=0, z=1
*reinterpret_cast<dword *>(this->tangent) = 0xFF8080FF; // x=1, y=0, z=0
*reinterpret_cast<dword *>(this->color) = 0;
*reinterpret_cast<dword *>(this->color2) = 0;
}
/*
========================
idDrawVert::GetNormal
========================
*/
ID_INLINE const idVec3 idDrawVert::GetNormal() const {
idVec3 n( VERTEX_BYTE_TO_FLOAT( normal[0] ),
VERTEX_BYTE_TO_FLOAT( normal[1] ),
VERTEX_BYTE_TO_FLOAT( normal[2] ) );
n.Normalize(); // after the normal has been compressed & uncompressed, it may not be normalized anymore
return n;
}
/*
========================
idDrawVert::GetNormalRaw
========================
*/
ID_INLINE const idVec3 idDrawVert::GetNormalRaw() const {
idVec3 n( VERTEX_BYTE_TO_FLOAT( normal[0] ),
VERTEX_BYTE_TO_FLOAT( normal[1] ),
VERTEX_BYTE_TO_FLOAT( normal[2] ) );
// don't re-normalize just like we do in the vertex programs
return n;
}
/*
========================
idDrawVert::SetNormal
must be normalized already!
========================
*/
ID_INLINE void idDrawVert::SetNormal( const idVec3 & n ) {
VertexFloatToByte( n.x, n.y, n.z, normal );
}
/*
========================
idDrawVert::SetNormal
========================
*/
ID_INLINE void idDrawVert::SetNormal( float x, float y, float z ) {
VertexFloatToByte( x, y, z, normal );
}
/*
========================
&idDrawVert::GetTangent
========================
*/
ID_INLINE const idVec3 idDrawVert::GetTangent() const {
idVec3 t( VERTEX_BYTE_TO_FLOAT( tangent[0] ),
VERTEX_BYTE_TO_FLOAT( tangent[1] ),
VERTEX_BYTE_TO_FLOAT( tangent[2] ) );
t.Normalize();
return t;
}
/*
========================
&idDrawVert::GetTangentRaw
========================
*/
ID_INLINE const idVec3 idDrawVert::GetTangentRaw() const {
idVec3 t( VERTEX_BYTE_TO_FLOAT( tangent[0] ),
VERTEX_BYTE_TO_FLOAT( tangent[1] ),
VERTEX_BYTE_TO_FLOAT( tangent[2] ) );
// don't re-normalize just like we do in the vertex programs
return t;
}
/*
========================
idDrawVert::SetTangent
========================
*/
ID_INLINE void idDrawVert::SetTangent( float x, float y, float z ) {
VertexFloatToByte( x, y, z, tangent );
}
/*
========================
idDrawVert::SetTangent
========================
*/
ID_INLINE void idDrawVert::SetTangent( const idVec3 & t ) {
VertexFloatToByte( t.x, t.y, t.z, tangent );
}
/*
========================
idDrawVert::GetBiTangent
========================
*/
ID_INLINE const idVec3 idDrawVert::GetBiTangent() const {
// derive from the normal, tangent, and bitangent direction flag
idVec3 bitangent;
bitangent.Cross( GetNormal(), GetTangent() );
bitangent *= GetBiTangentSign();
return bitangent;
}
/*
========================
idDrawVert::GetBiTangentRaw
========================
*/
ID_INLINE const idVec3 idDrawVert::GetBiTangentRaw() const {
// derive from the normal, tangent, and bitangent direction flag
// don't re-normalize just like we do in the vertex programs
idVec3 bitangent;
bitangent.Cross( GetNormalRaw(), GetTangentRaw() );
bitangent *= GetBiTangentSign();
return bitangent;
}
/*
========================
idDrawVert::SetBiTangent
========================
*/
ID_INLINE void idDrawVert::SetBiTangent( float x, float y, float z ) {
SetBiTangent( idVec3( x, y, z ) );
}
/*
========================
idDrawVert::SetBiTangent
========================
*/
ID_INLINE void idDrawVert::SetBiTangent( const idVec3 &t ) {
idVec3 bitangent;
bitangent.Cross( GetNormal(), GetTangent() );
SetBiTangentSign( bitangent * t );
}
/*
========================
idDrawVert::GetBiTangentSign
========================
*/
ID_INLINE float idDrawVert::GetBiTangentSign() const {
return ( tangent[3] < 128 ) ? -1.0f : 1.0f;
}
/*
========================
idDrawVert::GetBiTangentSignBit
========================
*/
ID_INLINE byte idDrawVert::GetBiTangentSignBit() const {
return ( tangent[3] < 128 ) ? 1 : 0;
}
/*
========================
idDrawVert::SetBiTangentSign
========================
*/
ID_INLINE void idDrawVert::SetBiTangentSign( float sign ) {
tangent[3] = ( sign < 0.0f ) ? 0 : 255;
}
/*
========================
idDrawVert::SetBiTangentSignBit
========================
*/
ID_INLINE void idDrawVert::SetBiTangentSignBit( byte sign ) {
tangent[3] = sign ? 0 : 255;
}
/*
========================
idDrawVert::Lerp
========================
*/
ID_INLINE void idDrawVert::Lerp( const idDrawVert &a, const idDrawVert &b, const float f ) {
xyz = a.xyz + f * ( b.xyz - a.xyz );
SetTexCoord( ::Lerp( a.GetTexCoord(), b.GetTexCoord(), f ) );
}
/*
========================
idDrawVert::LerpAll
========================
*/
ID_INLINE void idDrawVert::LerpAll( const idDrawVert &a, const idDrawVert &b, const float f ) {
xyz = ::Lerp( a.xyz, b.xyz, f );
SetTexCoord( ::Lerp( a.GetTexCoord(), b.GetTexCoord(), f ) );
idVec3 normal = ::Lerp( a.GetNormal(), b.GetNormal(), f );
idVec3 tangent = ::Lerp( a.GetTangent(), b.GetTangent(), f );
idVec3 bitangent = ::Lerp( a.GetBiTangent(), b.GetBiTangent(), f );
normal.Normalize();
tangent.Normalize();
bitangent.Normalize();
SetNormal( normal );
SetTangent( tangent );
SetBiTangent( bitangent );
color[0] = (byte)( a.color[0] + f * ( b.color[0] - a.color[0] ) );
color[1] = (byte)( a.color[1] + f * ( b.color[1] - a.color[1] ) );
color[2] = (byte)( a.color[2] + f * ( b.color[2] - a.color[2] ) );
color[3] = (byte)( a.color[3] + f * ( b.color[3] - a.color[3] ) );
color2[0] = (byte)( a.color2[0] + f * ( b.color2[0] - a.color2[0] ) );
color2[1] = (byte)( a.color2[1] + f * ( b.color2[1] - a.color2[1] ) );
color2[2] = (byte)( a.color2[2] + f * ( b.color2[2] - a.color2[2] ) );
color2[3] = (byte)( a.color2[3] + f * ( b.color2[3] - a.color2[3] ) );
}
/*
========================
idDrawVert::SetNativeOrderColor
========================
*/
ID_INLINE void idDrawVert::SetNativeOrderColor( dword color ) {
*reinterpret_cast<dword *>(this->color) = color;
}
/*
========================
idDrawVert::SetColor
========================
*/
ID_INLINE void idDrawVert::SetColor( dword color ) {
*reinterpret_cast<dword *>(this->color) = color;
}
/*
========================
idDrawVert::SetColor
========================
*/
ID_INLINE dword idDrawVert::GetColor() const {
return *reinterpret_cast<const dword *>(this->color);
}
/*
========================
idDrawVert::SetTexCoordNative
========================
*/
ID_INLINE void idDrawVert::SetTexCoordNative( const halfFloat_t s, const halfFloat_t t ) {
st[0] = s;
st[1] = t;
}
/*
========================
idDrawVert::SetTexCoord
========================
*/
ID_INLINE void idDrawVert::SetTexCoord( const idVec2 & st ) {
SetTexCoordS( st.x );
SetTexCoordT( st.y );
}
/*
========================
idDrawVert::SetTexCoord
========================
*/
ID_INLINE void idDrawVert::SetTexCoord( float s, float t ) {
SetTexCoordS( s );
SetTexCoordT( t );
}
/*
========================
idDrawVert::SetTexCoordS
========================
*/
ID_INLINE void idDrawVert::SetTexCoordS( float s ) {
st[0] = F32toF16( s );
}
/*
========================
idDrawVert::SetTexCoordT
========================
*/
ID_INLINE void idDrawVert::SetTexCoordT( float t ) {
st[1] = F32toF16( t );
}
/*
========================
idDrawVert::GetTexCoord
========================
*/
ID_INLINE const idVec2 idDrawVert::GetTexCoord() const {
return idVec2( F16toF32( st[0] ), F16toF32( st[1] ) );
}
/*
========================
idDrawVert::GetTexCoordNativeS
========================
*/
ID_INLINE const halfFloat_t idDrawVert::GetTexCoordNativeS() const {
return st[0];
}
/*
========================
idDrawVert::GetTexCoordNativeT
========================
*/
ID_INLINE const halfFloat_t idDrawVert::GetTexCoordNativeT() const {
return st[1];
}
/*
========================
idDrawVert::SetNativeOrderColor2
========================
*/
ID_INLINE void idDrawVert::SetNativeOrderColor2( dword color2 ) {
*reinterpret_cast<dword *>(this->color2) = color2;
}
/*
========================
idDrawVert::SetColor
========================
*/
ID_INLINE void idDrawVert::SetColor2( dword color2 ) {
*reinterpret_cast<dword *>(this->color2) = color2;
}
/*
========================
idDrawVert::ClearColor2
========================
*/
ID_INLINE void idDrawVert::ClearColor2() {
*reinterpret_cast<dword *>(this->color2) = 0x80808080;
}
/*
========================
idDrawVert::GetColor2
========================
*/
ID_INLINE dword idDrawVert::GetColor2() const {
return *reinterpret_cast<const dword *>(this->color2);
}
/*
========================
WriteDrawVerts16
Use 16-byte in-order SIMD writes because the destVerts may live in write-combined memory
========================
*/
ID_INLINE void WriteDrawVerts16( idDrawVert * destVerts, const idDrawVert * localVerts, int numVerts ) {
assert_sizeof( idDrawVert, 32 );
assert_16_byte_aligned( destVerts );
assert_16_byte_aligned( localVerts );
for ( int i = 0; i < numVerts; i++ ) {
__m128i v0 = _mm_load_si128( (const __m128i *)( (byte *)( localVerts + i ) + 0 ) );
__m128i v1 = _mm_load_si128( (const __m128i *)( (byte *)( localVerts + i ) + 16 ) );
_mm_stream_si128( (__m128i *)( (byte *)( destVerts + i ) + 0 ), v0 );
_mm_stream_si128( (__m128i *)( (byte *)( destVerts + i ) + 16 ), v1 );
}
}
/*
=====================
idDrawVert::GetSkinnedDrawVert
=====================
*/
ID_INLINE idDrawVert idDrawVert::GetSkinnedDrawVert( const idDrawVert & vert, const idJointMat * joints ) {
if ( joints == NULL ) {
return vert;
}
const idJointMat & j0 = joints[vert.color[0]];
const idJointMat & j1 = joints[vert.color[1]];
const idJointMat & j2 = joints[vert.color[2]];
const idJointMat & j3 = joints[vert.color[3]];
const float w0 = vert.color2[0] * ( 1.0f / 255.0f );
const float w1 = vert.color2[1] * ( 1.0f / 255.0f );
const float w2 = vert.color2[2] * ( 1.0f / 255.0f );
const float w3 = vert.color2[3] * ( 1.0f / 255.0f );
idJointMat accum;
idJointMat::Mul( accum, j0, w0 );
idJointMat::Mad( accum, j1, w1 );
idJointMat::Mad( accum, j2, w2 );
idJointMat::Mad( accum, j3, w3 );
idDrawVert outVert;
outVert.xyz = accum * idVec4( vert.xyz.x, vert.xyz.y, vert.xyz.z, 1.0f );
outVert.SetTexCoordNative( vert.GetTexCoordNativeS(), vert.GetTexCoordNativeT() );
outVert.SetNormal( accum * vert.GetNormal() );
outVert.SetTangent( accum * vert.GetTangent() );
outVert.tangent[3] = vert.tangent[3];
for ( int i = 0; i < 4; i++ ) {
outVert.color[i] = vert.color[i];
outVert.color2[i] = vert.color2[i];
}
return outVert;
}
/*
=====================
idDrawVert::GetSkinnedDrawVertPosition
=====================
*/
ID_INLINE idVec3 idDrawVert::GetSkinnedDrawVertPosition( const idDrawVert & vert, const idJointMat * joints ) {
if ( joints == NULL ) {
return vert.xyz;
}
const idJointMat & j0 = joints[vert.color[0]];
const idJointMat & j1 = joints[vert.color[1]];
const idJointMat & j2 = joints[vert.color[2]];
const idJointMat & j3 = joints[vert.color[3]];
const float w0 = vert.color2[0] * ( 1.0f / 255.0f );
const float w1 = vert.color2[1] * ( 1.0f / 255.0f );
const float w2 = vert.color2[2] * ( 1.0f / 255.0f );
const float w3 = vert.color2[3] * ( 1.0f / 255.0f );
idJointMat accum;
idJointMat::Mul( accum, j0, w0 );
idJointMat::Mad( accum, j1, w1 );
idJointMat::Mad( accum, j2, w2 );
idJointMat::Mad( accum, j3, w3 );
return accum * idVec4( vert.xyz.x, vert.xyz.y, vert.xyz.z, 1.0f );
}
/*
===============================================================================
Shadow Vertex
===============================================================================
*/
class idShadowVert {
public:
idVec4 xyzw;
void Clear();
static int CreateShadowCache( idShadowVert * vertexCache, const idDrawVert *verts, const int numVerts );
};
#define SHADOWVERT_XYZW_OFFSET (0)
assert_offsetof( idShadowVert, xyzw, SHADOWVERT_XYZW_OFFSET );
ID_INLINE void idShadowVert::Clear() {
xyzw.Zero();
}
/*
===============================================================================
Skinned Shadow Vertex
===============================================================================
*/
class idShadowVertSkinned {
public:
idVec4 xyzw;
byte color[4];
byte color2[4];
byte pad[8]; // pad to multiple of 32-byte for glDrawElementsBaseVertex
void Clear();
static int CreateShadowCache( idShadowVertSkinned * vertexCache, const idDrawVert *verts, const int numVerts );
};
#define SHADOWVERTSKINNED_XYZW_OFFSET (0)
#define SHADOWVERTSKINNED_COLOR_OFFSET (16)
#define SHADOWVERTSKINNED_COLOR2_OFFSET (20)
assert_offsetof( idShadowVertSkinned, xyzw, SHADOWVERTSKINNED_XYZW_OFFSET );
assert_offsetof( idShadowVertSkinned, color, SHADOWVERTSKINNED_COLOR_OFFSET );
assert_offsetof( idShadowVertSkinned, color2, SHADOWVERTSKINNED_COLOR2_OFFSET );
ID_INLINE void idShadowVertSkinned::Clear() {
xyzw.Zero();
}
#endif /* !__DRAWVERT_H__ */

View File

@@ -0,0 +1,200 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#ifndef __DRAWVERT_INTRINSICS_H__
#define __DRAWVERT_INTRINSICS_H__
static const __m128i vector_int_f32_sign_mask = _mm_set1_epi32( 1U << IEEE_FLT_SIGN_BIT );
static const __m128i vector_int_f32_exponent_mask = _mm_set1_epi32( ( ( 1U << IEEE_FLT_EXPONENT_BITS ) - 1 ) << IEEE_FLT_MANTISSA_BITS );
static const __m128i vector_int_f32_mantissa_mask = _mm_set1_epi32( ( 1U << IEEE_FLT_MANTISSA_BITS ) - 1 );
static const __m128i vector_int_f16_min_exponent = _mm_set1_epi32( 0 );
static const __m128i vector_int_f16_max_exponent = _mm_set1_epi32( ( 30 << IEEE_FLT16_MANTISSA_BITS ) );
static const __m128i vector_int_f16_min_mantissa = _mm_set1_epi32( 0 );
static const __m128i vector_int_f16_max_mantissa = _mm_set1_epi32( ( ( 1 << IEEE_FLT16_MANTISSA_BITS ) - 1 ) );
static const __m128i vector_int_f32_to_f16_exponent_bias = _mm_set1_epi32( ( IEEE_FLT_EXPONENT_BIAS - IEEE_FLT16_EXPONENT_BIAS ) << IEEE_FLT16_MANTISSA_BITS );
static const int f32_to_f16_sign_shift = IEEE_FLT_SIGN_BIT - IEEE_FLT16_SIGN_BIT;
static const int f32_to_f16_exponent_shift = IEEE_FLT_MANTISSA_BITS - IEEE_FLT16_MANTISSA_BITS;
static const int f32_to_f16_mantissa_shift = IEEE_FLT_MANTISSA_BITS - IEEE_FLT16_MANTISSA_BITS;
static const __m128i vector_int_zero = _mm_setzero_si128();
static const __m128i vector_int_one = _mm_set_epi32( 1, 1, 1, 1 );
static const __m128 vector_float_mask_clear_last = __m128c( _mm_set_epi32( 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF ) );
static const __m128 vector_float_last_one = { 0.0f, 0.0f, 0.0f, 1.0f };
static const __m128 vector_float_1_over_255 = { 1.0f / 255.0f, 1.0f / 255.0f, 1.0f / 255.0f, 1.0f / 255.0f };
static const __m128 vector_float_1_over_4 = { 1.0f / 4.0f, 1.0f / 4.0f, 1.0f / 4.0f, 1.0f / 4.0f };
/*
====================
FastF32toF16
====================
*/
ID_INLINE_EXTERN __m128i FastF32toF16( __m128i f32_bits ) {
__m128i f16_sign = _mm_srli_epi32( _mm_and_si128( f32_bits, vector_int_f32_sign_mask ), f32_to_f16_sign_shift );
__m128i f16_exponent = _mm_srli_epi32( _mm_and_si128( f32_bits, vector_int_f32_exponent_mask ), f32_to_f16_exponent_shift );
__m128i f16_mantissa = _mm_srli_epi32( _mm_and_si128( f32_bits, vector_int_f32_mantissa_mask ), f32_to_f16_mantissa_shift );
f16_exponent = _mm_sub_epi32( f16_exponent, vector_int_f32_to_f16_exponent_bias );
const __m128i underflow = _mm_cmplt_epi32( f16_exponent, vector_int_f16_min_exponent );
const __m128i overflow = _mm_cmpgt_epi32( f16_exponent, vector_int_f16_max_exponent );
f16_exponent = _mm_sel_si128( f16_exponent, vector_int_f16_min_exponent, underflow );
f16_exponent = _mm_sel_si128( f16_exponent, vector_int_f16_max_exponent, overflow );
f16_mantissa = _mm_sel_si128( f16_mantissa, vector_int_f16_min_mantissa, underflow );
f16_mantissa = _mm_sel_si128( f16_mantissa, vector_int_f16_max_mantissa, overflow );
__m128i flt16 = _mm_or_si128( _mm_or_si128( f16_sign, f16_exponent ), f16_mantissa );
return _mm_packs_epi32( flt16, flt16 );
}
ID_INLINE_EXTERN halfFloat_t Scalar_FastF32toF16( float f32 ) {
const int f32_sign_mask = 1U << IEEE_FLT_SIGN_BIT;
const int f32_exponent_mask = ( ( 1U << IEEE_FLT_EXPONENT_BITS ) - 1 ) << IEEE_FLT_MANTISSA_BITS;
const int f32_mantissa_mask = ( 1U << IEEE_FLT_MANTISSA_BITS ) - 1;
const int f16_min_exponent = 0;
const int f16_max_exponent = ( 30 << IEEE_FLT16_MANTISSA_BITS );
const int f16_min_mantissa = 0;
const int f16_max_mantissa = ( ( 1 << IEEE_FLT16_MANTISSA_BITS ) - 1 );
const int f32_to_f16_sign_shift = IEEE_FLT_SIGN_BIT - IEEE_FLT16_SIGN_BIT;
const int f32_to_f16_exponent_shift = IEEE_FLT_MANTISSA_BITS - IEEE_FLT16_MANTISSA_BITS;
const int f32_to_f16_mantissa_shift = IEEE_FLT_MANTISSA_BITS - IEEE_FLT16_MANTISSA_BITS;
const int f32_to_f16_exponent_bias = ( IEEE_FLT_EXPONENT_BIAS - IEEE_FLT16_EXPONENT_BIAS ) << IEEE_FLT16_MANTISSA_BITS;
int f32_bits = *(unsigned int *)&f32;
int f16_sign = ( (unsigned int )( f32_bits & f32_sign_mask ) >> f32_to_f16_sign_shift );
int f16_exponent = ( (unsigned int )( f32_bits & f32_exponent_mask ) >> f32_to_f16_exponent_shift );
int f16_mantissa = ( (unsigned int )( f32_bits & f32_mantissa_mask ) >> f32_to_f16_mantissa_shift );
f16_exponent -= f32_to_f16_exponent_bias;
const bool underflow = ( f16_exponent < f16_min_exponent );
const bool overflow = ( f16_exponent > f16_max_exponent );
f16_exponent = underflow ? f16_min_exponent : f16_exponent;
f16_exponent = overflow ? f16_max_exponent : f16_exponent;
f16_mantissa = underflow ? f16_min_mantissa : f16_mantissa;
f16_mantissa = overflow ? f16_max_mantissa : f16_mantissa;
return (halfFloat_t)( f16_sign | f16_exponent | f16_mantissa );
}
/*
====================
LoadSkinnedDrawVertPosition
====================
*/
ID_INLINE_EXTERN __m128 LoadSkinnedDrawVertPosition( const idDrawVert & base, const idJointMat * joints ) {
const idJointMat & j0 = joints[base.color[0]];
const idJointMat & j1 = joints[base.color[1]];
const idJointMat & j2 = joints[base.color[2]];
const idJointMat & j3 = joints[base.color[3]];
__m128i weights_b = _mm_cvtsi32_si128( *(const unsigned int *)base.color2 );
__m128i weights_s = _mm_unpacklo_epi8( weights_b, vector_int_zero );
__m128i weights_i = _mm_unpacklo_epi16( weights_s, vector_int_zero );
__m128 weights = _mm_cvtepi32_ps( weights_i );
weights = _mm_mul_ps( weights, vector_float_1_over_255 );
__m128 w0 = _mm_splat_ps( weights, 0 );
__m128 w1 = _mm_splat_ps( weights, 1 );
__m128 w2 = _mm_splat_ps( weights, 2 );
__m128 w3 = _mm_splat_ps( weights, 3 );
__m128 matX = _mm_mul_ps( _mm_load_ps( j0.ToFloatPtr() + 0 * 4 ), w0 );
__m128 matY = _mm_mul_ps( _mm_load_ps( j0.ToFloatPtr() + 1 * 4 ), w0 );
__m128 matZ = _mm_mul_ps( _mm_load_ps( j0.ToFloatPtr() + 2 * 4 ), w0 );
matX = _mm_madd_ps( _mm_load_ps( j1.ToFloatPtr() + 0 * 4 ), w1, matX );
matY = _mm_madd_ps( _mm_load_ps( j1.ToFloatPtr() + 1 * 4 ), w1, matY );
matZ = _mm_madd_ps( _mm_load_ps( j1.ToFloatPtr() + 2 * 4 ), w1, matZ );
matX = _mm_madd_ps( _mm_load_ps( j2.ToFloatPtr() + 0 * 4 ), w2, matX );
matY = _mm_madd_ps( _mm_load_ps( j2.ToFloatPtr() + 1 * 4 ), w2, matY );
matZ = _mm_madd_ps( _mm_load_ps( j2.ToFloatPtr() + 2 * 4 ), w2, matZ );
matX = _mm_madd_ps( _mm_load_ps( j3.ToFloatPtr() + 0 * 4 ), w3, matX );
matY = _mm_madd_ps( _mm_load_ps( j3.ToFloatPtr() + 1 * 4 ), w3, matY );
matZ = _mm_madd_ps( _mm_load_ps( j3.ToFloatPtr() + 2 * 4 ), w3, matZ );
__m128 v = _mm_load_ps( base.xyz.ToFloatPtr() );
v = _mm_and_ps( v, vector_float_mask_clear_last );
v = _mm_or_ps( v, vector_float_last_one );
__m128 t0 = _mm_mul_ps( matX, v );
__m128 t1 = _mm_mul_ps( matY, v );
__m128 t2 = _mm_mul_ps( matZ, v );
__m128 t3 = vector_float_1_over_4;
__m128 s0 = _mm_unpacklo_ps( t0, t2 ); // x0, z0, x1, z1
__m128 s1 = _mm_unpackhi_ps( t0, t2 ); // x2, z2, x3, z3
__m128 s2 = _mm_unpacklo_ps( t1, t3 ); // y0, w0, y1, w1
__m128 s3 = _mm_unpackhi_ps( t1, t3 ); // y2, w2, y3, w3
__m128 r0 = _mm_unpacklo_ps( s0, s2 ); // x0, y0, z0, w0
__m128 r1 = _mm_unpackhi_ps( s0, s2 ); // x1, y1, z1, w1
__m128 r2 = _mm_unpacklo_ps( s1, s3 ); // x2, y2, z2, w2
__m128 r3 = _mm_unpackhi_ps( s1, s3 ); // x3, y3, z3, w3
r0 = _mm_add_ps( r0, r1 );
r2 = _mm_add_ps( r2, r3 );
r0 = _mm_add_ps( r0, r2 );
return r0;
}
ID_INLINE_EXTERN idVec3 Scalar_LoadSkinnedDrawVertPosition( const idDrawVert & vert, const idJointMat * joints ) {
const idJointMat & j0 = joints[vert.color[0]];
const idJointMat & j1 = joints[vert.color[1]];
const idJointMat & j2 = joints[vert.color[2]];
const idJointMat & j3 = joints[vert.color[3]];
const float w0 = vert.color2[0] * ( 1.0f / 255.0f );
const float w1 = vert.color2[1] * ( 1.0f / 255.0f );
const float w2 = vert.color2[2] * ( 1.0f / 255.0f );
const float w3 = vert.color2[3] * ( 1.0f / 255.0f );
idJointMat accum;
idJointMat::Mul( accum, j0, w0 );
idJointMat::Mad( accum, j1, w1 );
idJointMat::Mad( accum, j2, w2 );
idJointMat::Mad( accum, j3, w3 );
return accum * idVec4( vert.xyz.x, vert.xyz.y, vert.xyz.z, 1.0f );
}
#endif /* !__DRAWVERT_INTRINSICS_H__ */

View File

@@ -0,0 +1,87 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#pragma hdrstop
#include "../precompiled.h"
/*
=============
idJointMat::ToJointQuat
=============
*/
idJointQuat idJointMat::ToJointQuat() const {
idJointQuat jq;
float trace;
float s;
float t;
int i;
int j;
int k;
static int next[3] = { 1, 2, 0 };
trace = mat[0 * 4 + 0] + mat[1 * 4 + 1] + mat[2 * 4 + 2];
if ( trace > 0.0f ) {
t = trace + 1.0f;
s = idMath::InvSqrt( t ) * 0.5f;
jq.q[3] = s * t;
jq.q[0] = ( mat[1 * 4 + 2] - mat[2 * 4 + 1] ) * s;
jq.q[1] = ( mat[2 * 4 + 0] - mat[0 * 4 + 2] ) * s;
jq.q[2] = ( mat[0 * 4 + 1] - mat[1 * 4 + 0] ) * s;
} else {
i = 0;
if ( mat[1 * 4 + 1] > mat[0 * 4 + 0] ) {
i = 1;
}
if ( mat[2 * 4 + 2] > mat[i * 4 + i] ) {
i = 2;
}
j = next[i];
k = next[j];
t = ( mat[i * 4 + i] - ( mat[j * 4 + j] + mat[k * 4 + k] ) ) + 1.0f;
s = idMath::InvSqrt( t ) * 0.5f;
jq.q[i] = s * t;
jq.q[3] = ( mat[j * 4 + k] - mat[k * 4 + j] ) * s;
jq.q[j] = ( mat[i * 4 + j] + mat[j * 4 + i] ) * s;
jq.q[k] = ( mat[i * 4 + k] + mat[k * 4 + i] ) * s;
}
jq.t[0] = mat[0 * 4 + 3];
jq.t[1] = mat[1 * 4 + 3];
jq.t[2] = mat[2 * 4 + 3];
jq.w = 0.0f;
return jq;
}

View File

@@ -0,0 +1,544 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#ifndef __JOINTTRANSFORM_H__
#define __JOINTTRANSFORM_H__
/*
===============================================================================
Joint Quaternion
===============================================================================
*/
class idJointQuat {
public:
const float * ToFloatPtr() const { return q.ToFloatPtr(); }
float * ToFloatPtr() { return q.ToFloatPtr(); }
idQuat q;
idVec3 t;
float w;
};
// offsets for SIMD code
#define JOINTQUAT_SIZE (8*4) // sizeof( idJointQuat )
#define JOINTQUAT_SIZE_SHIFT 5 // log2( sizeof( idJointQuat ) )
#define JOINTQUAT_Q_OFFSET (0*4) // offsetof( idJointQuat, q )
#define JOINTQUAT_T_OFFSET (4*4) // offsetof( idJointQuat, t )
assert_sizeof( idJointQuat, JOINTQUAT_SIZE );
assert_sizeof( idJointQuat, (1<<JOINTQUAT_SIZE_SHIFT) );
assert_offsetof( idJointQuat, q, JOINTQUAT_Q_OFFSET );
assert_offsetof( idJointQuat, t, JOINTQUAT_T_OFFSET );
/*
===============================================================================
Joint Matrix
===============================================================================
*/
/*
================================================
idJointMat has the following structure:
idMat3 m;
idVec3 t;
m[0][0], m[1][0], m[2][0], t[0]
m[0][1], m[1][1], m[2][1], t[1]
m[0][2], m[1][2], m[2][2], t[2]
================================================
*/
class idJointMat {
public:
void SetRotation( const idMat3 &m );
idMat3 GetRotation() const;
void SetTranslation( const idVec3 &t );
idVec3 GetTranslation() const;
idVec3 operator*( const idVec3 &v ) const; // only rotate
idVec3 operator*( const idVec4 &v ) const; // rotate and translate
idJointMat & operator*=( const idJointMat &a ); // transform
idJointMat & operator/=( const idJointMat &a ); // untransform
bool Compare( const idJointMat &a ) const; // exact compare, no epsilon
bool Compare( const idJointMat &a, const float epsilon ) const; // compare with epsilon
bool operator==( const idJointMat &a ) const; // exact compare, no epsilon
bool operator!=( const idJointMat &a ) const; // exact compare, no epsilon
void Identity();
void Invert();
void FromMat4( const idMat4 & m );
idMat3 ToMat3() const;
idMat4 ToMat4() const;
idVec3 ToVec3() const;
const float * ToFloatPtr() const { return mat; }
float * ToFloatPtr() { return mat; }
idJointQuat ToJointQuat() const;
void Transform( idVec3 &result, const idVec3 &v ) const;
void Rotate( idVec3 &result, const idVec3 &v ) const;
static void Mul( idJointMat &result, const idJointMat &mat, const float s );
static void Mad( idJointMat &result, const idJointMat &mat, const float s );
static void Multiply( idJointMat &result, const idJointMat &m1, const idJointMat &m2 );
static void InverseMultiply( idJointMat &result, const idJointMat &m1, const idJointMat &m2 );
float mat[3*4];
};
// offsets for SIMD code
#define JOINTMAT_SIZE (4*3*4) // sizeof( idJointMat )
assert_sizeof( idJointMat, JOINTMAT_SIZE );
#define JOINTMAT_TYPESIZE ( 4 * 3 )
/*
========================
idJointMat::SetRotation
========================
*/
ID_INLINE void idJointMat::SetRotation( const idMat3 &m ) {
// NOTE: idMat3 is transposed because it is column-major
mat[0 * 4 + 0] = m[0][0];
mat[0 * 4 + 1] = m[1][0];
mat[0 * 4 + 2] = m[2][0];
mat[1 * 4 + 0] = m[0][1];
mat[1 * 4 + 1] = m[1][1];
mat[1 * 4 + 2] = m[2][1];
mat[2 * 4 + 0] = m[0][2];
mat[2 * 4 + 1] = m[1][2];
mat[2 * 4 + 2] = m[2][2];
}
/*
========================
idJointMat::GetRotation
========================
*/
ID_INLINE idMat3 idJointMat::GetRotation() const {
idMat3 m;
m[0][0] = mat[0 * 4 + 0];
m[1][0] = mat[0 * 4 + 1];
m[2][0] = mat[0 * 4 + 2];
m[0][1] = mat[1 * 4 + 0];
m[1][1] = mat[1 * 4 + 1];
m[2][1] = mat[1 * 4 + 2];
m[0][2] = mat[2 * 4 + 0];
m[1][2] = mat[2 * 4 + 1];
m[2][2] = mat[2 * 4 + 2];
return m;
}
/*
========================
idJointMat::SetTranslation
========================
*/
ID_INLINE void idJointMat::SetTranslation( const idVec3 &t ) {
mat[0 * 4 + 3] = t[0];
mat[1 * 4 + 3] = t[1];
mat[2 * 4 + 3] = t[2];
}
/*
========================
idJointMat::GetTranslation
========================
*/
ID_INLINE idVec3 idJointMat::GetTranslation() const {
idVec3 t;
t[0] = mat[0 * 4 + 3];
t[1] = mat[1 * 4 + 3];
t[2] = mat[2 * 4 + 3];
return t;
}
/*
========================
idJointMat::operator*
========================
*/
ID_INLINE idVec3 idJointMat::operator*( const idVec3 &v ) const {
return idVec3( mat[0 * 4 + 0] * v[0] + mat[0 * 4 + 1] * v[1] + mat[0 * 4 + 2] * v[2],
mat[1 * 4 + 0] * v[0] + mat[1 * 4 + 1] * v[1] + mat[1 * 4 + 2] * v[2],
mat[2 * 4 + 0] * v[0] + mat[2 * 4 + 1] * v[1] + mat[2 * 4 + 2] * v[2] );
}
ID_INLINE idVec3 idJointMat::operator*( const idVec4 &v ) const {
return idVec3( mat[0 * 4 + 0] * v[0] + mat[0 * 4 + 1] * v[1] + mat[0 * 4 + 2] * v[2] + mat[0 * 4 + 3] * v[3],
mat[1 * 4 + 0] * v[0] + mat[1 * 4 + 1] * v[1] + mat[1 * 4 + 2] * v[2] + mat[1 * 4 + 3] * v[3],
mat[2 * 4 + 0] * v[0] + mat[2 * 4 + 1] * v[1] + mat[2 * 4 + 2] * v[2] + mat[2 * 4 + 3] * v[3] );
}
/*
========================
idJointMat::operator*=
========================
*/
ID_INLINE idJointMat & idJointMat::operator*=( const idJointMat &a ) {
float tmp[3];
tmp[0] = mat[0 * 4 + 0] * a.mat[0 * 4 + 0] + mat[1 * 4 + 0] * a.mat[0 * 4 + 1] + mat[2 * 4 + 0] * a.mat[0 * 4 + 2];
tmp[1] = mat[0 * 4 + 0] * a.mat[1 * 4 + 0] + mat[1 * 4 + 0] * a.mat[1 * 4 + 1] + mat[2 * 4 + 0] * a.mat[1 * 4 + 2];
tmp[2] = mat[0 * 4 + 0] * a.mat[2 * 4 + 0] + mat[1 * 4 + 0] * a.mat[2 * 4 + 1] + mat[2 * 4 + 0] * a.mat[2 * 4 + 2];
mat[0 * 4 + 0] = tmp[0];
mat[1 * 4 + 0] = tmp[1];
mat[2 * 4 + 0] = tmp[2];
tmp[0] = mat[0 * 4 + 1] * a.mat[0 * 4 + 0] + mat[1 * 4 + 1] * a.mat[0 * 4 + 1] + mat[2 * 4 + 1] * a.mat[0 * 4 + 2];
tmp[1] = mat[0 * 4 + 1] * a.mat[1 * 4 + 0] + mat[1 * 4 + 1] * a.mat[1 * 4 + 1] + mat[2 * 4 + 1] * a.mat[1 * 4 + 2];
tmp[2] = mat[0 * 4 + 1] * a.mat[2 * 4 + 0] + mat[1 * 4 + 1] * a.mat[2 * 4 + 1] + mat[2 * 4 + 1] * a.mat[2 * 4 + 2];
mat[0 * 4 + 1] = tmp[0];
mat[1 * 4 + 1] = tmp[1];
mat[2 * 4 + 1] = tmp[2];
tmp[0] = mat[0 * 4 + 2] * a.mat[0 * 4 + 0] + mat[1 * 4 + 2] * a.mat[0 * 4 + 1] + mat[2 * 4 + 2] * a.mat[0 * 4 + 2];
tmp[1] = mat[0 * 4 + 2] * a.mat[1 * 4 + 0] + mat[1 * 4 + 2] * a.mat[1 * 4 + 1] + mat[2 * 4 + 2] * a.mat[1 * 4 + 2];
tmp[2] = mat[0 * 4 + 2] * a.mat[2 * 4 + 0] + mat[1 * 4 + 2] * a.mat[2 * 4 + 1] + mat[2 * 4 + 2] * a.mat[2 * 4 + 2];
mat[0 * 4 + 2] = tmp[0];
mat[1 * 4 + 2] = tmp[1];
mat[2 * 4 + 2] = tmp[2];
tmp[0] = mat[0 * 4 + 3] * a.mat[0 * 4 + 0] + mat[1 * 4 + 3] * a.mat[0 * 4 + 1] + mat[2 * 4 + 3] * a.mat[0 * 4 + 2];
tmp[1] = mat[0 * 4 + 3] * a.mat[1 * 4 + 0] + mat[1 * 4 + 3] * a.mat[1 * 4 + 1] + mat[2 * 4 + 3] * a.mat[1 * 4 + 2];
tmp[2] = mat[0 * 4 + 3] * a.mat[2 * 4 + 0] + mat[1 * 4 + 3] * a.mat[2 * 4 + 1] + mat[2 * 4 + 3] * a.mat[2 * 4 + 2];
mat[0 * 4 + 3] = tmp[0];
mat[1 * 4 + 3] = tmp[1];
mat[2 * 4 + 3] = tmp[2];
mat[0 * 4 + 3] += a.mat[0 * 4 + 3];
mat[1 * 4 + 3] += a.mat[1 * 4 + 3];
mat[2 * 4 + 3] += a.mat[2 * 4 + 3];
return *this;
}
/*
========================
idJointMat::operator/=
========================
*/
ID_INLINE idJointMat &idJointMat::operator/=( const idJointMat &a ) {
float tmp[3];
mat[0 * 4 + 3] -= a.mat[0 * 4 + 3];
mat[1 * 4 + 3] -= a.mat[1 * 4 + 3];
mat[2 * 4 + 3] -= a.mat[2 * 4 + 3];
tmp[0] = mat[0 * 4 + 0] * a.mat[0 * 4 + 0] + mat[1 * 4 + 0] * a.mat[1 * 4 + 0] + mat[2 * 4 + 0] * a.mat[2 * 4 + 0];
tmp[1] = mat[0 * 4 + 0] * a.mat[0 * 4 + 1] + mat[1 * 4 + 0] * a.mat[1 * 4 + 1] + mat[2 * 4 + 0] * a.mat[2 * 4 + 1];
tmp[2] = mat[0 * 4 + 0] * a.mat[0 * 4 + 2] + mat[1 * 4 + 0] * a.mat[1 * 4 + 2] + mat[2 * 4 + 0] * a.mat[2 * 4 + 2];
mat[0 * 4 + 0] = tmp[0];
mat[1 * 4 + 0] = tmp[1];
mat[2 * 4 + 0] = tmp[2];
tmp[0] = mat[0 * 4 + 1] * a.mat[0 * 4 + 0] + mat[1 * 4 + 1] * a.mat[1 * 4 + 0] + mat[2 * 4 + 1] * a.mat[2 * 4 + 0];
tmp[1] = mat[0 * 4 + 1] * a.mat[0 * 4 + 1] + mat[1 * 4 + 1] * a.mat[1 * 4 + 1] + mat[2 * 4 + 1] * a.mat[2 * 4 + 1];
tmp[2] = mat[0 * 4 + 1] * a.mat[0 * 4 + 2] + mat[1 * 4 + 1] * a.mat[1 * 4 + 2] + mat[2 * 4 + 1] * a.mat[2 * 4 + 2];
mat[0 * 4 + 1] = tmp[0];
mat[1 * 4 + 1] = tmp[1];
mat[2 * 4 + 1] = tmp[2];
tmp[0] = mat[0 * 4 + 2] * a.mat[0 * 4 + 0] + mat[1 * 4 + 2] * a.mat[1 * 4 + 0] + mat[2 * 4 + 2] * a.mat[2 * 4 + 0];
tmp[1] = mat[0 * 4 + 2] * a.mat[0 * 4 + 1] + mat[1 * 4 + 2] * a.mat[1 * 4 + 1] + mat[2 * 4 + 2] * a.mat[2 * 4 + 1];
tmp[2] = mat[0 * 4 + 2] * a.mat[0 * 4 + 2] + mat[1 * 4 + 2] * a.mat[1 * 4 + 2] + mat[2 * 4 + 2] * a.mat[2 * 4 + 2];
mat[0 * 4 + 2] = tmp[0];
mat[1 * 4 + 2] = tmp[1];
mat[2 * 4 + 2] = tmp[2];
tmp[0] = mat[0 * 4 + 3] * a.mat[0 * 4 + 0] + mat[1 * 4 + 3] * a.mat[1 * 4 + 0] + mat[2 * 4 + 3] * a.mat[2 * 4 + 0];
tmp[1] = mat[0 * 4 + 3] * a.mat[0 * 4 + 1] + mat[1 * 4 + 3] * a.mat[1 * 4 + 1] + mat[2 * 4 + 3] * a.mat[2 * 4 + 1];
tmp[2] = mat[0 * 4 + 3] * a.mat[0 * 4 + 2] + mat[1 * 4 + 3] * a.mat[1 * 4 + 2] + mat[2 * 4 + 3] * a.mat[2 * 4 + 2];
mat[0 * 4 + 3] = tmp[0];
mat[1 * 4 + 3] = tmp[1];
mat[2 * 4 + 3] = tmp[2];
return *this;
}
/*
========================
idJointMat::Compare
========================
*/
ID_INLINE bool idJointMat::Compare( const idJointMat &a ) const {
int i;
for ( i = 0; i < 12; i++ ) {
if ( mat[i] != a.mat[i] ) {
return false;
}
}
return true;
}
/*
========================
idJointMat::Compare
========================
*/
ID_INLINE bool idJointMat::Compare( const idJointMat &a, const float epsilon ) const {
int i;
for ( i = 0; i < 12; i++ ) {
if ( idMath::Fabs( mat[i] - a.mat[i] ) > epsilon ) {
return false;
}
}
return true;
}
/*
========================
idJointMat::operator==
========================
*/
ID_INLINE bool idJointMat::operator==( const idJointMat &a ) const {
return Compare( a );
}
/*
========================
idJointMat::operator!=
========================
*/
ID_INLINE bool idJointMat::operator!=( const idJointMat &a ) const {
return !Compare( a );
}
/*
========================
idJointMat::Identity
========================
*/
ID_INLINE void idJointMat::Identity() {
mat[0 * 4 + 0] = 1.0f; mat[0 * 4 + 1] = 0.0f; mat[0 * 4 + 2] = 0.0f; mat[0 * 4 + 3] = 0.0f;
mat[1 * 4 + 0] = 0.0f; mat[1 * 4 + 1] = 1.0f; mat[1 * 4 + 2] = 0.0f; mat[1 * 4 + 3] = 0.0f;
mat[2 * 4 + 0] = 0.0f; mat[2 * 4 + 1] = 0.0f; mat[2 * 4 + 2] = 1.0f; mat[2 * 4 + 3] = 0.0f;
}
/*
========================
idJointMat::Invert
========================
*/
ID_INLINE void idJointMat::Invert() {
float tmp[3];
// negate inverse rotated translation part
tmp[0] = mat[0 * 4 + 0] * mat[0 * 4 + 3] + mat[1 * 4 + 0] * mat[1 * 4 + 3] + mat[2 * 4 + 0] * mat[2 * 4 + 3];
tmp[1] = mat[0 * 4 + 1] * mat[0 * 4 + 3] + mat[1 * 4 + 1] * mat[1 * 4 + 3] + mat[2 * 4 + 1] * mat[2 * 4 + 3];
tmp[2] = mat[0 * 4 + 2] * mat[0 * 4 + 3] + mat[1 * 4 + 2] * mat[1 * 4 + 3] + mat[2 * 4 + 2] * mat[2 * 4 + 3];
mat[0 * 4 + 3] = -tmp[0];
mat[1 * 4 + 3] = -tmp[1];
mat[2 * 4 + 3] = -tmp[2];
// transpose rotation part
tmp[0] = mat[0 * 4 + 1];
mat[0 * 4 + 1] = mat[1 * 4 + 0];
mat[1 * 4 + 0] = tmp[0];
tmp[1] = mat[0 * 4 + 2];
mat[0 * 4 + 2] = mat[2 * 4 + 0];
mat[2 * 4 + 0] = tmp[1];
tmp[2] = mat[1 * 4 + 2];
mat[1 * 4 + 2] = mat[2 * 4 + 1];
mat[2 * 4 + 1] = tmp[2];
}
/*
========================
idJointMat::ToMat3
========================
*/
ID_INLINE idMat3 idJointMat::ToMat3() const {
return idMat3( mat[0 * 4 + 0], mat[1 * 4 + 0], mat[2 * 4 + 0],
mat[0 * 4 + 1], mat[1 * 4 + 1], mat[2 * 4 + 1],
mat[0 * 4 + 2], mat[1 * 4 + 2], mat[2 * 4 + 2] );
}
/*
========================
idJointMat::ToMat4
========================
*/
ID_INLINE idMat4 idJointMat::ToMat4() const {
return idMat4(
mat[0 * 4 + 0], mat[0 * 4 + 1], mat[0 * 4 + 2], mat[0 * 4 + 3],
mat[1 * 4 + 0], mat[1 * 4 + 1], mat[1 * 4 + 2], mat[1 * 4 + 3],
mat[2 * 4 + 0], mat[2 * 4 + 1], mat[2 * 4 + 2], mat[2 * 4 + 3],
0.0f, 0.0f, 0.0f, 1.0f
);
}
/*
========================
idJointMat::FromMat4
========================
*/
ID_INLINE void idJointMat::FromMat4( const idMat4 & m ) {
mat[0*4+0] = m[0][0], mat[0*4+1] = m[0][1], mat[0*4+2] = m[0][2], mat[0*4+3] = m[0][3];
mat[1*4+0] = m[1][0], mat[1*4+1] = m[1][1], mat[1*4+2] = m[1][2], mat[1*4+3] = m[1][3];
mat[2*4+0] = m[2][0], mat[2*4+1] = m[2][1], mat[2*4+2] = m[2][2], mat[2*4+3] = m[2][3];
assert( m[3][0] == 0.0f );
assert( m[3][1] == 0.0f );
assert( m[3][2] == 0.0f );
assert( m[3][3] == 1.0f );
}
/*
========================
idJointMat::ToVec3
========================
*/
ID_INLINE idVec3 idJointMat::ToVec3() const {
return idVec3( mat[0 * 4 + 3], mat[1 * 4 + 3], mat[2 * 4 + 3] );
}
/*
========================
idJointMat::Transform
========================
*/
ID_INLINE void idJointMat::Transform( idVec3 &result, const idVec3 &v ) const {
result.x = mat[0 * 4 + 0] * v.x + mat[0 * 4 + 1] * v.y + mat[0 * 4 + 2] * v.z + mat[0 * 4 + 3];
result.y = mat[1 * 4 + 0] * v.x + mat[1 * 4 + 1] * v.y + mat[1 * 4 + 2] * v.z + mat[1 * 4 + 3];
result.z = mat[2 * 4 + 0] * v.x + mat[2 * 4 + 1] * v.y + mat[2 * 4 + 2] * v.z + mat[2 * 4 + 3];
}
/*
========================
idJointMat::Rotate
========================
*/
ID_INLINE void idJointMat::Rotate( idVec3 &result, const idVec3 &v ) const {
result.x = mat[0 * 4 + 0] * v.x + mat[0 * 4 + 1] * v.y + mat[0 * 4 + 2] * v.z;
result.y = mat[1 * 4 + 0] * v.x + mat[1 * 4 + 1] * v.y + mat[1 * 4 + 2] * v.z;
result.z = mat[2 * 4 + 0] * v.x + mat[2 * 4 + 1] * v.y + mat[2 * 4 + 2] * v.z;
}
/*
========================
idJointMat::Mul
========================
*/
ID_INLINE void idJointMat::Mul( idJointMat &result, const idJointMat &mat, const float s ) {
result.mat[0 * 4 + 0] = s * mat.mat[0 * 4 + 0];
result.mat[0 * 4 + 1] = s * mat.mat[0 * 4 + 1];
result.mat[0 * 4 + 2] = s * mat.mat[0 * 4 + 2];
result.mat[0 * 4 + 3] = s * mat.mat[0 * 4 + 3];
result.mat[1 * 4 + 0] = s * mat.mat[1 * 4 + 0];
result.mat[1 * 4 + 1] = s * mat.mat[1 * 4 + 1];
result.mat[1 * 4 + 2] = s * mat.mat[1 * 4 + 2];
result.mat[1 * 4 + 3] = s * mat.mat[1 * 4 + 3];
result.mat[2 * 4 + 0] = s * mat.mat[2 * 4 + 0];
result.mat[2 * 4 + 1] = s * mat.mat[2 * 4 + 1];
result.mat[2 * 4 + 2] = s * mat.mat[2 * 4 + 2];
result.mat[2 * 4 + 3] = s * mat.mat[2 * 4 + 3];
}
/*
========================
idJointMat::Mad
========================
*/
ID_INLINE void idJointMat::Mad( idJointMat &result, const idJointMat &mat, const float s ) {
result.mat[0 * 4 + 0] += s * mat.mat[0 * 4 + 0];
result.mat[0 * 4 + 1] += s * mat.mat[0 * 4 + 1];
result.mat[0 * 4 + 2] += s * mat.mat[0 * 4 + 2];
result.mat[0 * 4 + 3] += s * mat.mat[0 * 4 + 3];
result.mat[1 * 4 + 0] += s * mat.mat[1 * 4 + 0];
result.mat[1 * 4 + 1] += s * mat.mat[1 * 4 + 1];
result.mat[1 * 4 + 2] += s * mat.mat[1 * 4 + 2];
result.mat[1 * 4 + 3] += s * mat.mat[1 * 4 + 3];
result.mat[2 * 4 + 0] += s * mat.mat[2 * 4 + 0];
result.mat[2 * 4 + 1] += s * mat.mat[2 * 4 + 1];
result.mat[2 * 4 + 2] += s * mat.mat[2 * 4 + 2];
result.mat[2 * 4 + 3] += s * mat.mat[2 * 4 + 3];
}
/*
========================
idJointMat::Multiply
========================
*/
ID_INLINE void idJointMat::Multiply( idJointMat &result, const idJointMat &m1, const idJointMat &m2 ) {
result.mat[0 * 4 + 0] = m1.mat[0 * 4 + 0] * m2.mat[0 * 4 + 0] + m1.mat[0 * 4 + 1] * m2.mat[1 * 4 + 0] + m1.mat[0 * 4 + 2] * m2.mat[2 * 4 + 0];
result.mat[0 * 4 + 1] = m1.mat[0 * 4 + 0] * m2.mat[0 * 4 + 1] + m1.mat[0 * 4 + 1] * m2.mat[1 * 4 + 1] + m1.mat[0 * 4 + 2] * m2.mat[2 * 4 + 1];
result.mat[0 * 4 + 2] = m1.mat[0 * 4 + 0] * m2.mat[0 * 4 + 2] + m1.mat[0 * 4 + 1] * m2.mat[1 * 4 + 2] + m1.mat[0 * 4 + 2] * m2.mat[2 * 4 + 2];
result.mat[0 * 4 + 3] = m1.mat[0 * 4 + 0] * m2.mat[0 * 4 + 3] + m1.mat[0 * 4 + 1] * m2.mat[1 * 4 + 3] + m1.mat[0 * 4 + 2] * m2.mat[2 * 4 + 3] + m1.mat[0 * 4 + 3];
result.mat[1 * 4 + 0] = m1.mat[1 * 4 + 0] * m2.mat[0 * 4 + 0] + m1.mat[1 * 4 + 1] * m2.mat[1 * 4 + 0] + m1.mat[1 * 4 + 2] * m2.mat[2 * 4 + 0];
result.mat[1 * 4 + 1] = m1.mat[1 * 4 + 0] * m2.mat[0 * 4 + 1] + m1.mat[1 * 4 + 1] * m2.mat[1 * 4 + 1] + m1.mat[1 * 4 + 2] * m2.mat[2 * 4 + 1];
result.mat[1 * 4 + 2] = m1.mat[1 * 4 + 0] * m2.mat[0 * 4 + 2] + m1.mat[1 * 4 + 1] * m2.mat[1 * 4 + 2] + m1.mat[1 * 4 + 2] * m2.mat[2 * 4 + 2];
result.mat[1 * 4 + 3] = m1.mat[1 * 4 + 0] * m2.mat[0 * 4 + 3] + m1.mat[1 * 4 + 1] * m2.mat[1 * 4 + 3] + m1.mat[1 * 4 + 2] * m2.mat[2 * 4 + 3] + m1.mat[1 * 4 + 3];
result.mat[2 * 4 + 0] = m1.mat[2 * 4 + 0] * m2.mat[0 * 4 + 0] + m1.mat[2 * 4 + 1] * m2.mat[1 * 4 + 0] + m1.mat[2 * 4 + 2] * m2.mat[2 * 4 + 0];
result.mat[2 * 4 + 1] = m1.mat[2 * 4 + 0] * m2.mat[0 * 4 + 1] + m1.mat[2 * 4 + 1] * m2.mat[1 * 4 + 1] + m1.mat[2 * 4 + 2] * m2.mat[2 * 4 + 1];
result.mat[2 * 4 + 2] = m1.mat[2 * 4 + 0] * m2.mat[0 * 4 + 2] + m1.mat[2 * 4 + 1] * m2.mat[1 * 4 + 2] + m1.mat[2 * 4 + 2] * m2.mat[2 * 4 + 2];
result.mat[2 * 4 + 3] = m1.mat[2 * 4 + 0] * m2.mat[0 * 4 + 3] + m1.mat[2 * 4 + 1] * m2.mat[1 * 4 + 3] + m1.mat[2 * 4 + 2] * m2.mat[2 * 4 + 3] + m1.mat[2 * 4 + 3];
}
/*
========================
idJointMat::InverseMultiply
========================
*/
ID_INLINE void idJointMat::InverseMultiply( idJointMat &result, const idJointMat &m1, const idJointMat &m2 ) {
float dst[3];
result.mat[0 * 4 + 0] = m1.mat[0 * 4 + 0] * m2.mat[0 * 4 + 0] + m1.mat[0 * 4 + 1] * m2.mat[0 * 4 + 1] + m1.mat[0 * 4 + 2] * m2.mat[0 * 4 + 2];
result.mat[0 * 4 + 1] = m1.mat[0 * 4 + 0] * m2.mat[1 * 4 + 0] + m1.mat[0 * 4 + 1] * m2.mat[1 * 4 + 1] + m1.mat[0 * 4 + 2] * m2.mat[1 * 4 + 2];
result.mat[0 * 4 + 2] = m1.mat[0 * 4 + 0] * m2.mat[2 * 4 + 0] + m1.mat[0 * 4 + 1] * m2.mat[2 * 4 + 1] + m1.mat[0 * 4 + 2] * m2.mat[2 * 4 + 2];
result.mat[1 * 4 + 0] = m1.mat[1 * 4 + 0] * m2.mat[0 * 4 + 0] + m1.mat[1 * 4 + 1] * m2.mat[0 * 4 + 1] + m1.mat[1 * 4 + 2] * m2.mat[0 * 4 + 2];
result.mat[1 * 4 + 1] = m1.mat[1 * 4 + 0] * m2.mat[1 * 4 + 0] + m1.mat[1 * 4 + 1] * m2.mat[1 * 4 + 1] + m1.mat[1 * 4 + 2] * m2.mat[1 * 4 + 2];
result.mat[1 * 4 + 2] = m1.mat[1 * 4 + 0] * m2.mat[2 * 4 + 0] + m1.mat[1 * 4 + 1] * m2.mat[2 * 4 + 1] + m1.mat[1 * 4 + 2] * m2.mat[2 * 4 + 2];
result.mat[2 * 4 + 0] = m1.mat[2 * 4 + 0] * m2.mat[0 * 4 + 0] + m1.mat[2 * 4 + 1] * m2.mat[0 * 4 + 1] + m1.mat[2 * 4 + 2] * m2.mat[0 * 4 + 2];
result.mat[2 * 4 + 1] = m1.mat[2 * 4 + 0] * m2.mat[1 * 4 + 0] + m1.mat[2 * 4 + 1] * m2.mat[1 * 4 + 1] + m1.mat[2 * 4 + 2] * m2.mat[1 * 4 + 2];
result.mat[2 * 4 + 2] = m1.mat[2 * 4 + 0] * m2.mat[2 * 4 + 0] + m1.mat[2 * 4 + 1] * m2.mat[2 * 4 + 1] + m1.mat[2 * 4 + 2] * m2.mat[2 * 4 + 2];
dst[0] = - ( m2.mat[0 * 4 + 0] * m2.mat[0 * 4 + 3] + m2.mat[1 * 4 + 0] * m2.mat[1 * 4 + 3] + m2.mat[2 * 4 + 0] * m2.mat[2 * 4 + 3] );
dst[1] = - ( m2.mat[0 * 4 + 1] * m2.mat[0 * 4 + 3] + m2.mat[1 * 4 + 1] * m2.mat[1 * 4 + 3] + m2.mat[2 * 4 + 1] * m2.mat[2 * 4 + 3] );
dst[2] = - ( m2.mat[0 * 4 + 2] * m2.mat[0 * 4 + 3] + m2.mat[1 * 4 + 2] * m2.mat[1 * 4 + 3] + m2.mat[2 * 4 + 2] * m2.mat[2 * 4 + 3] );
result.mat[0 * 4 + 3] = m1.mat[0 * 4 + 0] * dst[0] + m1.mat[0 * 4 + 1] * dst[1] + m1.mat[0 * 4 + 2] * dst[2] + m1.mat[0 * 4 + 3];
result.mat[1 * 4 + 3] = m1.mat[1 * 4 + 0] * dst[0] + m1.mat[1 * 4 + 1] * dst[1] + m1.mat[1 * 4 + 2] * dst[2] + m1.mat[1 * 4 + 3];
result.mat[2 * 4 + 3] = m1.mat[2 * 4 + 0] * dst[0] + m1.mat[2 * 4 + 1] * dst[1] + m1.mat[2 * 4 + 2] * dst[2] + m1.mat[2 * 4 + 3];
}
#endif /* !__JOINTTRANSFORM_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,489 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#ifndef __RENDERMATRIX_H__
#define __RENDERMATRIX_H__
static const int NUM_FRUSTUM_CORNERS = 8;
struct frustumCorners_t {
float x[NUM_FRUSTUM_CORNERS];
float y[NUM_FRUSTUM_CORNERS];
float z[NUM_FRUSTUM_CORNERS];
};
enum frustumCull_t {
FRUSTUM_CULL_FRONT = 1,
FRUSTUM_CULL_BACK = 2,
FRUSTUM_CULL_CROSS = 3
};
/*
================================================================================================
idRenderMatrix
This is a row-major matrix and transforms are applied with left-multiplication.
================================================================================================
*/
class idRenderMatrix {
public:
idRenderMatrix() {}
ID_INLINE idRenderMatrix( float a0, float a1, float a2, float a3,
float b0, float b1, float b2, float b3,
float c0, float c1, float c2, float c3,
float d0, float d1, float d2, float d3 );
const float * operator[]( int index ) const { assert( index >= 0 && index < 4 ); return &m[index*4]; }
float * operator[]( int index ) { assert( index >= 0 && index < 4 ); return &m[index*4]; }
void Zero() { memset( m, 0, sizeof( m ) ); }
ID_INLINE void Identity();
// Matrix classification (only meant to be used for asserts).
ID_INLINE bool IsZero( float epsilon ) const;
ID_INLINE bool IsIdentity( float epsilon ) const;
ID_INLINE bool IsAffineTransform( float epsilon ) const;
ID_INLINE bool IsUniformScale( float epsilon ) const;
// Transform a point.
// NOTE: the idVec3 out variant does not divide by W.
ID_INLINE void TransformPoint( const idVec3 & in, idVec3 & out ) const;
ID_INLINE void TransformPoint( const idVec3 & in, idVec4 & out ) const;
ID_INLINE void TransformPoint( const idVec4 & in, idVec4 & out ) const;
// These assume the matrix has no non-uniform scaling or shearing.
// NOTE: a direction will only stay normalized if the matrix has no skewing or scaling.
ID_INLINE void TransformDir( const idVec3 & in, idVec3 & out, bool normalize ) const;
ID_INLINE void TransformPlane( const idPlane & in, idPlane & out, bool normalize ) const;
// These transforms work with non-uniform scaling and shearing by multiplying
// with 'transpose(inverse(M))' where this matrix is assumed to be 'inverse(M)'.
ID_INLINE void InverseTransformDir( const idVec3 & in, idVec3 & out, bool normalize ) const;
ID_INLINE void InverseTransformPlane( const idPlane & in, idPlane & out, bool normalize ) const;
// Project a point.
static ID_INLINE void TransformModelToClip( const idVec3 & src, const idRenderMatrix & modelMatrix, const idRenderMatrix & projectionMatrix, idVec4 & eye, idVec4 & clip );
static ID_INLINE void TransformClipToDevice( const idVec4 & clip, idVec3 & ndc );
// Create a matrix that goes from local space to the space defined by the 'origin' and 'axis'.
static void CreateFromOriginAxis( const idVec3 & origin, const idMat3 & axis, idRenderMatrix & out );
static void CreateFromOriginAxisScale( const idVec3 & origin, const idMat3 & axis, const idVec3 & scale, idRenderMatrix & out );
// Create a matrix that goes from a global coordinate to a view coordinate (OpenGL looking down -Z) based on the given view origin/axis.
static void CreateViewMatrix( const idVec3 & origin, const idMat3 & axis, idRenderMatrix & out );
// Create a projection matrix.
static void CreateProjectionMatrix( float xMin, float xMax, float yMin, float yMax, float zNear, float zFar, idRenderMatrix & out );
static void CreateProjectionMatrixFov( float xFovDegrees, float yFovDegrees, float zNear, float zFar, float xOffset, float yOffset, idRenderMatrix & out );
// Apply depth hacks to a projection matrix.
static ID_INLINE void ApplyDepthHack( idRenderMatrix & src );
static ID_INLINE void ApplyModelDepthHack( idRenderMatrix & src, float value );
// Offset and scale the given matrix such that the result matrix transforms the unit-cube to exactly cover the given bounds (and the inverse).
static void OffsetScaleForBounds( const idRenderMatrix & src, const idBounds & bounds, idRenderMatrix & out );
static void InverseOffsetScaleForBounds( const idRenderMatrix & src, const idBounds & bounds, idRenderMatrix & out );
// Basic matrix operations.
static void Transpose( const idRenderMatrix & src, idRenderMatrix & out );
static void Multiply( const idRenderMatrix & a, const idRenderMatrix & b, idRenderMatrix & out );
static bool Inverse( const idRenderMatrix & src, idRenderMatrix & out );
static void InverseByTranspose( const idRenderMatrix & src, idRenderMatrix & out );
static bool InverseByDoubles( const idRenderMatrix & src, idRenderMatrix & out );
// Copy or create a matrix that is stored directly into four float4 vectors which is useful for directly setting vertex program uniforms.
static void CopyMatrix( const idRenderMatrix & matrix, idVec4 & row0, idVec4 & row1, idVec4 & row2, idVec4 & row3 );
static void SetMVP( const idRenderMatrix & mvp, idVec4 & row0, idVec4 & row1, idVec4 & row2, idVec4 & row3, bool & negativeDeterminant );
static void SetMVPForBounds( const idRenderMatrix & mvp, const idBounds & bounds, idVec4 & row0, idVec4 & row1, idVec4 & row2, idVec4 & row3, bool & negativeDeterminant );
static void SetMVPForInverseProject( const idRenderMatrix & mvp, const idRenderMatrix & inverseProject, idVec4 & row0, idVec4 & row1, idVec4 & row2, idVec4 & row3, bool & negativeDeterminant );
// Cull to a Model-View-Projection (MVP) matrix.
static bool CullPointToMVP( const idRenderMatrix & mvp, const idVec3 & point, bool zeroToOne = false );
static bool CullPointToMVPbits( const idRenderMatrix & mvp, const idVec3 & point, byte * outBits, bool zeroToOne = false );
static bool CullBoundsToMVP( const idRenderMatrix & mvp, const idBounds & bounds, bool zeroToOne = false );
static bool CullBoundsToMVPbits( const idRenderMatrix & mvp, const idBounds & bounds, byte * outBits, bool zeroToOne = false );
static bool CullExtrudedBoundsToMVP( const idRenderMatrix & mvp, const idBounds & bounds, const idVec3 & extrudeDirection, const idPlane & clipPlane, bool zeroToOne = false );
static bool CullExtrudedBoundsToMVPbits( const idRenderMatrix & mvp, const idBounds & bounds, const idVec3 & extrudeDirection, const idPlane & clipPlane, byte * outBits, bool zeroToOne = false );
// Calculate the projected bounds.
static void ProjectedBounds( idBounds & projected, const idRenderMatrix & mvp, const idBounds & bounds, bool windowSpace = true );
static void ProjectedNearClippedBounds( idBounds & projected, const idRenderMatrix & mvp, const idBounds & bounds, bool windowSpace = true );
static void ProjectedFullyClippedBounds( idBounds & projected, const idRenderMatrix & mvp, const idBounds & bounds, bool windowSpace = true );
// Calculate the projected depth bounds.
static void DepthBoundsForBounds( float & min, float & max, const idRenderMatrix & mvp, const idBounds & bounds, bool windowSpace = true );
static void DepthBoundsForExtrudedBounds( float & min, float & max, const idRenderMatrix & mvp, const idBounds & bounds, const idVec3 & extrudeDirection, const idPlane & clipPlane, bool windowSpace = true );
static void DepthBoundsForShadowBounds( float & min, float & max, const idRenderMatrix & mvp, const idBounds & bounds, const idVec3 & localLightOrigin, bool windowSpace = true );
// Create frustum planes and corners from a matrix.
static void GetFrustumPlanes( idPlane planes[6], const idRenderMatrix & frustum, bool zeroToOne, bool normalize );
static void GetFrustumCorners( frustumCorners_t & corners, const idRenderMatrix & frustumTransform, const idBounds & frustumBounds );
static frustumCull_t CullFrustumCornersToPlane( const frustumCorners_t & corners, const idPlane & plane );
private:
float m[16];
};
extern const idRenderMatrix renderMatrix_identity;
extern const idRenderMatrix renderMatrix_flipToOpenGL;
extern const idRenderMatrix renderMatrix_windowSpaceToClipSpace;
/*
========================
idRenderMatrix::idRenderMatrix
========================
*/
ID_INLINE idRenderMatrix::idRenderMatrix( float a0, float a1, float a2, float a3,
float b0, float b1, float b2, float b3,
float c0, float c1, float c2, float c3,
float d0, float d1, float d2, float d3 ) {
m[0*4+0] = a0; m[0*4+1] = a1; m[0*4+2] = a2; m[0*4+3] = a3;
m[1*4+0] = b0; m[1*4+1] = b1; m[1*4+2] = b2; m[1*4+3] = b3;
m[2*4+0] = c0; m[2*4+1] = c1; m[2*4+2] = c2; m[2*4+3] = c3;
m[3*4+0] = d0; m[3*4+1] = d1; m[3*4+2] = d2; m[3*4+3] = d3;
}
/*
========================
idRenderMatrix::Identity
========================
*/
ID_INLINE void idRenderMatrix::Identity() {
m[0*4+0] = 1.0f;
m[0*4+1] = 0.0f;
m[0*4+2] = 0.0f;
m[0*4+3] = 0.0f;
m[1*4+0] = 0.0f;
m[1*4+1] = 1.0f;
m[1*4+2] = 0.0f;
m[1*4+3] = 0.0f;
m[2*4+0] = 0.0f;
m[2*4+1] = 0.0f;
m[2*4+2] = 1.0f;
m[2*4+3] = 0.0f;
m[3*4+0] = 0.0f;
m[3*4+1] = 0.0f;
m[3*4+2] = 0.0f;
m[3*4+3] = 1.0f;
}
/*
========================
idRenderMatrix::IsZero
========================
*/
ID_INLINE bool idRenderMatrix::IsZero( float epsilon ) const {
for ( int i = 0; i < 16; i++ ) {
if ( idMath::Fabs( m[i] ) > epsilon ) {
return false;
}
}
return true;
}
/*
========================
idRenderMatrix::IsIdentity
========================
*/
ID_INLINE bool idRenderMatrix::IsIdentity( float epsilon ) const {
for ( int i = 0; i < 4; i++ ) {
for ( int j = 0; j < 4; j++ ) {
if ( i == j ) {
if ( idMath::Fabs( m[i * 4 + j] - 1.0f ) > epsilon ) {
return false;
}
} else {
if ( idMath::Fabs( m[i * 4 + j] ) > epsilon ) {
return false;
}
}
}
}
return true;
}
/*
========================
idRenderMatrix::IsAffineTransform
========================
*/
ID_INLINE bool idRenderMatrix::IsAffineTransform( float epsilon ) const {
if ( idMath::Fabs( m[3 * 4 + 0] ) > epsilon ||
idMath::Fabs( m[3 * 4 + 1] ) > epsilon ||
idMath::Fabs( m[3 * 4 + 2] ) > epsilon ||
idMath::Fabs( m[3 * 4 + 3] - 1.0f ) > epsilon ) {
return false;
}
return true;
}
/*
========================
idRenderMatrix::IsUniformScale
========================
*/
ID_INLINE bool idRenderMatrix::IsUniformScale( float epsilon ) const {
float d0 = idMath::InvSqrt( m[0*4+0] * m[0*4+0] + m[1*4+0] * m[1*4+0] + m[2*4+0] * m[2*4+0] );
float d1 = idMath::InvSqrt( m[0*4+1] * m[0*4+1] + m[1*4+1] * m[1*4+1] + m[2*4+1] * m[2*4+1] );
float d2 = idMath::InvSqrt( m[0*4+2] * m[0*4+2] + m[1*4+2] * m[1*4+2] + m[2*4+2] * m[2*4+2] );
if ( idMath::Fabs( d0 - d1 ) > epsilon ) { return false; }
if ( idMath::Fabs( d1 - d2 ) > epsilon ) { return false; }
if ( idMath::Fabs( d0 - d2 ) > epsilon ) { return false; }
return true;
}
/*
========================
idRenderMatrix::TransformPoint
========================
*/
ID_INLINE void idRenderMatrix::TransformPoint( const idVec3 & in, idVec3 & out ) const {
assert( in.ToFloatPtr() != out.ToFloatPtr() );
const idRenderMatrix & matrix = *this;
out[0] = in[0] * matrix[0][0] + in[1] * matrix[0][1] + in[2] * matrix[0][2] + matrix[0][3];
out[1] = in[0] * matrix[1][0] + in[1] * matrix[1][1] + in[2] * matrix[1][2] + matrix[1][3];
out[2] = in[0] * matrix[2][0] + in[1] * matrix[2][1] + in[2] * matrix[2][2] + matrix[2][3];
assert( idMath::Fabs( in[0] * matrix[3][0] + in[1] * matrix[3][1] + in[2] * matrix[3][2] + matrix[3][3] - 1.0f ) < 0.01f );
}
/*
========================
idRenderMatrix::TransformPoint
========================
*/
ID_INLINE void idRenderMatrix::TransformPoint( const idVec3 & in, idVec4 & out ) const {
assert( in.ToFloatPtr() != out.ToFloatPtr() );
const idRenderMatrix & matrix = *this;
out[0] = in[0] * matrix[0][0] + in[1] * matrix[0][1] + in[2] * matrix[0][2] + matrix[0][3];
out[1] = in[0] * matrix[1][0] + in[1] * matrix[1][1] + in[2] * matrix[1][2] + matrix[1][3];
out[2] = in[0] * matrix[2][0] + in[1] * matrix[2][1] + in[2] * matrix[2][2] + matrix[2][3];
out[3] = in[0] * matrix[3][0] + in[1] * matrix[3][1] + in[2] * matrix[3][2] + matrix[3][3];
}
/*
========================
idRenderMatrix::TransformPoint
========================
*/
ID_INLINE void idRenderMatrix::TransformPoint( const idVec4 & in, idVec4 & out ) const {
assert( in.ToFloatPtr() != out.ToFloatPtr() );
const idRenderMatrix & matrix = *this;
out[0] = in[0] * matrix[0][0] + in[1] * matrix[0][1] + in[2] * matrix[0][2] + in[3] * matrix[0][3];
out[1] = in[0] * matrix[1][0] + in[1] * matrix[1][1] + in[2] * matrix[1][2] + in[3] * matrix[1][3];
out[2] = in[0] * matrix[2][0] + in[1] * matrix[2][1] + in[2] * matrix[2][2] + in[3] * matrix[2][3];
out[3] = in[0] * matrix[3][0] + in[1] * matrix[3][1] + in[2] * matrix[3][2] + in[3] * matrix[3][3];
}
/*
========================
idRenderMatrix::TransformDir
========================
*/
ID_INLINE void idRenderMatrix::TransformDir( const idVec3 & in, idVec3 & out, bool normalize ) const {
const idRenderMatrix & matrix = *this;
float p0 = in[0] * matrix[0][0] + in[1] * matrix[0][1] + in[2] * matrix[0][2];
float p1 = in[0] * matrix[1][0] + in[1] * matrix[1][1] + in[2] * matrix[1][2];
float p2 = in[0] * matrix[2][0] + in[1] * matrix[2][1] + in[2] * matrix[2][2];
if ( normalize ) {
float r = idMath::InvSqrt( p0 * p0 + p1 * p1 + p2 * p2 );
p0 *= r;
p1 *= r;
p2 *= r;
}
out[0] = p0;
out[1] = p1;
out[2] = p2;
}
/*
========================
idRenderMatrix::TransformPlane
========================
*/
ID_INLINE void idRenderMatrix::TransformPlane( const idPlane & in, idPlane & out, bool normalize ) const {
assert( IsUniformScale( 0.01f ) );
const idRenderMatrix & matrix = *this;
float p0 = in[0] * matrix[0][0] + in[1] * matrix[0][1] + in[2] * matrix[0][2];
float p1 = in[0] * matrix[1][0] + in[1] * matrix[1][1] + in[2] * matrix[1][2];
float p2 = in[0] * matrix[2][0] + in[1] * matrix[2][1] + in[2] * matrix[2][2];
float d0 = matrix[0][3] - p0 * in[3];
float d1 = matrix[1][3] - p1 * in[3];
float d2 = matrix[2][3] - p2 * in[3];
if ( normalize ) {
float r = idMath::InvSqrt( p0 * p0 + p1 * p1 + p2 * p2 );
p0 *= r;
p1 *= r;
p2 *= r;
}
out[0] = p0;
out[1] = p1;
out[2] = p2;
out[3] = - p0 * d0 - p1 * d1 - p2 * d2;
}
/*
========================
idRenderMatrix::InverseTransformDir
========================
*/
ID_INLINE void idRenderMatrix::InverseTransformDir( const idVec3 & in, idVec3 & out, bool normalize ) const {
assert( in.ToFloatPtr() != out.ToFloatPtr() );
const idRenderMatrix & matrix = *this;
float p0 = in[0] * matrix[0][0] + in[1] * matrix[1][0] + in[2] * matrix[2][0];
float p1 = in[0] * matrix[0][1] + in[1] * matrix[1][1] + in[2] * matrix[2][1];
float p2 = in[0] * matrix[0][2] + in[1] * matrix[1][2] + in[2] * matrix[2][2];
if ( normalize ) {
float r = idMath::InvSqrt( p0 * p0 + p1 * p1 + p2 * p2 );
p0 *= r;
p1 *= r;
p2 *= r;
}
out[0] = p0;
out[1] = p1;
out[2] = p2;
}
/*
========================
idRenderMatrix::InverseTransformPlane
========================
*/
ID_INLINE void idRenderMatrix::InverseTransformPlane( const idPlane & in, idPlane & out, bool normalize ) const {
assert( in.ToFloatPtr() != out.ToFloatPtr() );
const idRenderMatrix & matrix = *this;
float p0 = in[0] * matrix[0][0] + in[1] * matrix[1][0] + in[2] * matrix[2][0] + in[3] * matrix[3][0];
float p1 = in[0] * matrix[0][1] + in[1] * matrix[1][1] + in[2] * matrix[2][1] + in[3] * matrix[3][1];
float p2 = in[0] * matrix[0][2] + in[1] * matrix[1][2] + in[2] * matrix[2][2] + in[3] * matrix[3][2];
float p3 = in[0] * matrix[0][3] + in[1] * matrix[1][3] + in[2] * matrix[2][3] + in[3] * matrix[3][3];
if ( normalize ) {
float r = idMath::InvSqrt( p0 * p0 + p1 * p1 + p2 * p2 );
p0 *= r;
p1 *= r;
p2 *= r;
p3 *= r;
}
out[0] = p0;
out[1] = p1;
out[2] = p2;
out[3] = p3;
}
/*
========================
idRenderMatrix::TransformModelToClip
========================
*/
ID_INLINE void idRenderMatrix::TransformModelToClip( const idVec3 & src, const idRenderMatrix & modelMatrix, const idRenderMatrix & projectionMatrix, idVec4 & eye, idVec4 & clip ) {
for ( int i = 0; i < 4; i++ ) {
eye[i] = modelMatrix[i][0] * src[0] +
modelMatrix[i][1] * src[1] +
modelMatrix[i][2] * src[2] +
modelMatrix[i][3];
}
for ( int i = 0; i < 4; i++ ) {
clip[i] = projectionMatrix[i][0] * eye[0] +
projectionMatrix[i][1] * eye[1] +
projectionMatrix[i][2] * eye[2] +
projectionMatrix[i][3] * eye[3];
}
}
/*
========================
idRenderMatrix::TransformClipToDevice
Clip to normalized device coordinates.
========================
*/
ID_INLINE void idRenderMatrix::TransformClipToDevice( const idVec4 & clip, idVec3 & ndc ) {
assert( idMath::Fabs( clip[3] ) > idMath::FLT_SMALLEST_NON_DENORMAL );
float r = 1.0f / clip[3];
ndc[0] = clip[0] * r;
ndc[1] = clip[1] * r;
ndc[2] = clip[2] * r;
}
/*
========================
idRenderMatrix::ApplyDepthHack
========================
*/
ID_INLINE void idRenderMatrix::ApplyDepthHack( idRenderMatrix & src ) {
// scale projected z by 25%
src.m[2*4+0] *= 0.25f;
src.m[2*4+1] *= 0.25f;
src.m[2*4+2] *= 0.25f;
src.m[2*4+3] *= 0.25f;
}
/*
========================
idRenderMatrix::ApplyModelDepthHack
========================
*/
ID_INLINE void idRenderMatrix::ApplyModelDepthHack( idRenderMatrix & src, float value ) {
// offset projected z
src.m[2*4+3] -= value;
}
/*
========================
idRenderMatrix::CullPointToMVP
========================
*/
ID_INLINE bool idRenderMatrix::CullPointToMVP( const idRenderMatrix & mvp, const idVec3 & point, bool zeroToOne ) {
byte bits;
return CullPointToMVPbits( mvp, point, &bits, zeroToOne );
}
/*
========================
idRenderMatrix::CullBoundsToMVP
========================
*/
ID_INLINE bool idRenderMatrix::CullBoundsToMVP( const idRenderMatrix & mvp, const idBounds & bounds, bool zeroToOne ) {
byte bits;
return CullBoundsToMVPbits( mvp, bounds, &bits, zeroToOne );
}
/*
========================
idRenderMatrix::CullExtrudedBoundsToMVP
========================
*/
ID_INLINE bool idRenderMatrix::CullExtrudedBoundsToMVP( const idRenderMatrix & mvp, const idBounds & bounds, const idVec3 & extrudeDirection, const idPlane & clipPlane, bool zeroToOne ) {
byte bits;
return CullExtrudedBoundsToMVPbits( mvp, bounds, extrudeDirection, clipPlane, &bits, zeroToOne );
}
#endif // !__RENDERMATRIX_H__

View File

@@ -0,0 +1,929 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#pragma hdrstop
#include "../precompiled.h"
/*
=================
UpdateVertexIndex
=================
*/
ID_INLINE int UpdateVertexIndex( int vertexIndexNum[2], int *vertexRemap, int *vertexCopyIndex, int vertNum ) {
int s = INT32_SIGNBITSET( vertexRemap[vertNum] );
vertexIndexNum[0] = vertexRemap[vertNum];
vertexRemap[vertNum] = vertexIndexNum[s];
vertexIndexNum[1] += s;
vertexCopyIndex[vertexRemap[vertNum]] = vertNum;
return vertexRemap[vertNum];
}
/*
=================
idSurface::Split
=================
*/
int idSurface::Split( const idPlane &plane, const float epsilon, idSurface **front, idSurface **back, int *frontOnPlaneEdges, int *backOnPlaneEdges ) const {
float * dists;
float f;
byte * sides;
int counts[3];
int * edgeSplitVertex;
int numEdgeSplitVertexes;
int * vertexRemap[2];
int vertexIndexNum[2][2];
int * vertexCopyIndex[2];
int * indexPtr[2];
int indexNum[2];
int * index;
int * onPlaneEdges[2];
int numOnPlaneEdges[2];
int maxOnPlaneEdges;
int i;
idSurface * surface[2];
idDrawVert v;
dists = (float *) _alloca( verts.Num() * sizeof( float ) );
sides = (byte *) _alloca( verts.Num() * sizeof( byte ) );
counts[0] = counts[1] = counts[2] = 0;
// determine side for each vertex
for ( i = 0; i < verts.Num(); i++ ) {
dists[i] = f = plane.Distance( verts[i].xyz );
if ( f > epsilon ) {
sides[i] = SIDE_FRONT;
} else if ( f < -epsilon ) {
sides[i] = SIDE_BACK;
} else {
sides[i] = SIDE_ON;
}
counts[sides[i]]++;
}
*front = *back = NULL;
// if coplanar, put on the front side if the normals match
if ( !counts[SIDE_FRONT] && !counts[SIDE_BACK] ) {
f = ( verts[indexes[1]].xyz - verts[indexes[0]].xyz ).Cross( verts[indexes[0]].xyz - verts[indexes[2]].xyz ) * plane.Normal();
if ( IEEE_FLT_SIGNBITSET( f ) ) {
*back = new (TAG_IDLIB_SURFACE) idSurface( *this );
return SIDE_BACK;
} else {
*front = new (TAG_IDLIB_SURFACE) idSurface( *this );
return SIDE_FRONT;
}
}
// if nothing at the front of the clipping plane
if ( !counts[SIDE_FRONT] ) {
*back = new (TAG_IDLIB_SURFACE) idSurface( *this );
return SIDE_BACK;
}
// if nothing at the back of the clipping plane
if ( !counts[SIDE_BACK] ) {
*front = new (TAG_IDLIB_SURFACE) idSurface( *this );
return SIDE_FRONT;
}
// allocate front and back surface
*front = surface[0] = new (TAG_IDLIB_SURFACE) idSurface();
*back = surface[1] = new (TAG_IDLIB_SURFACE) idSurface();
edgeSplitVertex = (int *) _alloca( edges.Num() * sizeof( int ) );
numEdgeSplitVertexes = 0;
maxOnPlaneEdges = 4 * counts[SIDE_ON];
counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
// split edges
for ( i = 0; i < edges.Num(); i++ ) {
int v0 = edges[i].verts[0];
int v1 = edges[i].verts[1];
int sidesOr = ( sides[v0] | sides[v1] );
// if both vertexes are on the same side or one is on the clipping plane
if ( !( sides[v0] ^ sides[v1] ) || ( sidesOr & SIDE_ON ) ) {
edgeSplitVertex[i] = -1;
counts[sidesOr & SIDE_BACK]++;
counts[SIDE_ON] += ( sidesOr & SIDE_ON ) >> 1;
} else {
f = dists[v0] / ( dists[v0] - dists[v1] );
v.LerpAll( verts[v0], verts[v1], f );
edgeSplitVertex[i] = numEdgeSplitVertexes++;
surface[0]->verts.Append( v );
surface[1]->verts.Append( v );
}
}
// each edge is shared by at most two triangles, as such there can never be more indexes than twice the number of edges
surface[0]->indexes.Resize( ( ( counts[SIDE_FRONT] + counts[SIDE_ON] ) * 2 ) + ( numEdgeSplitVertexes * 4 ) );
surface[1]->indexes.Resize( ( ( counts[SIDE_BACK] + counts[SIDE_ON] ) * 2 ) + ( numEdgeSplitVertexes * 4 ) );
// allocate indexes to construct the triangle indexes for the front and back surface
vertexRemap[0] = (int *) _alloca( verts.Num() * sizeof( int ) );
memset( vertexRemap[0], -1, verts.Num() * sizeof( int ) );
vertexRemap[1] = (int *) _alloca( verts.Num() * sizeof( int ) );
memset( vertexRemap[1], -1, verts.Num() * sizeof( int ) );
vertexCopyIndex[0] = (int *) _alloca( ( numEdgeSplitVertexes + verts.Num() ) * sizeof( int ) );
vertexCopyIndex[1] = (int *) _alloca( ( numEdgeSplitVertexes + verts.Num() ) * sizeof( int ) );
vertexIndexNum[0][0] = vertexIndexNum[1][0] = 0;
vertexIndexNum[0][1] = vertexIndexNum[1][1] = numEdgeSplitVertexes;
indexPtr[0] = surface[0]->indexes.Ptr();
indexPtr[1] = surface[1]->indexes.Ptr();
indexNum[0] = surface[0]->indexes.Num();
indexNum[1] = surface[1]->indexes.Num();
maxOnPlaneEdges += 4 * numEdgeSplitVertexes;
// allocate one more in case no triangles are actually split which may happen for a disconnected surface
onPlaneEdges[0] = (int *) _alloca( ( maxOnPlaneEdges + 1 ) * sizeof( int ) );
onPlaneEdges[1] = (int *) _alloca( ( maxOnPlaneEdges + 1 ) * sizeof( int ) );
numOnPlaneEdges[0] = numOnPlaneEdges[1] = 0;
// split surface triangles
for ( i = 0; i < edgeIndexes.Num(); i += 3 ) {
int e0, e1, e2, v0, v1, v2, s, n;
e0 = abs( edgeIndexes[i+0] );
e1 = abs( edgeIndexes[i+1] );
e2 = abs( edgeIndexes[i+2] );
v0 = indexes[i+0];
v1 = indexes[i+1];
v2 = indexes[i+2];
switch( ( INT32_SIGNBITSET( edgeSplitVertex[e0] ) | ( INT32_SIGNBITSET( edgeSplitVertex[e1] ) << 1 ) | ( INT32_SIGNBITSET( edgeSplitVertex[e2] ) << 2 ) ) ^ 7 ) {
case 0: { // no edges split
if ( ( sides[v0] & sides[v1] & sides[v2] ) & SIDE_ON ) {
// coplanar
f = ( verts[v1].xyz - verts[v0].xyz ).Cross( verts[v0].xyz - verts[v2].xyz ) * plane.Normal();
s = IEEE_FLT_SIGNBITSET( f );
} else {
s = ( sides[v0] | sides[v1] | sides[v2] ) & SIDE_BACK;
}
n = indexNum[s];
onPlaneEdges[s][numOnPlaneEdges[s]] = n;
numOnPlaneEdges[s] += ( sides[v0] & sides[v1] ) >> 1;
onPlaneEdges[s][numOnPlaneEdges[s]] = n+1;
numOnPlaneEdges[s] += ( sides[v1] & sides[v2] ) >> 1;
onPlaneEdges[s][numOnPlaneEdges[s]] = n+2;
numOnPlaneEdges[s] += ( sides[v2] & sides[v0] ) >> 1;
index = indexPtr[s];
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v0 );
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v1 );
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v2 );
indexNum[s] = n;
break;
}
case 1: { // first edge split
s = sides[v0] & SIDE_BACK;
n = indexNum[s];
onPlaneEdges[s][numOnPlaneEdges[s]++] = n;
index = indexPtr[s];
index[n++] = edgeSplitVertex[e0];
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v2 );
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v0 );
indexNum[s] = n;
s ^= 1;
n = indexNum[s];
onPlaneEdges[s][numOnPlaneEdges[s]++] = n;
index = indexPtr[s];
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v2 );
index[n++] = edgeSplitVertex[e0];
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v1 );
indexNum[s] = n;
break;
}
case 2: { // second edge split
s = sides[v1] & SIDE_BACK;
n = indexNum[s];
onPlaneEdges[s][numOnPlaneEdges[s]++] = n;
index = indexPtr[s];
index[n++] = edgeSplitVertex[e1];
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v0 );
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v1 );
indexNum[s] = n;
s ^= 1;
n = indexNum[s];
onPlaneEdges[s][numOnPlaneEdges[s]++] = n;
index = indexPtr[s];
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v0 );
index[n++] = edgeSplitVertex[e1];
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v2 );
indexNum[s] = n;
break;
}
case 3: { // first and second edge split
s = sides[v1] & SIDE_BACK;
n = indexNum[s];
onPlaneEdges[s][numOnPlaneEdges[s]++] = n;
index = indexPtr[s];
index[n++] = edgeSplitVertex[e1];
index[n++] = edgeSplitVertex[e0];
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v1 );
indexNum[s] = n;
s ^= 1;
n = indexNum[s];
onPlaneEdges[s][numOnPlaneEdges[s]++] = n;
index = indexPtr[s];
index[n++] = edgeSplitVertex[e0];
index[n++] = edgeSplitVertex[e1];
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v0 );
index[n++] = edgeSplitVertex[e1];
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v2 );
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v0 );
indexNum[s] = n;
break;
}
case 4: { // third edge split
s = sides[v2] & SIDE_BACK;
n = indexNum[s];
onPlaneEdges[s][numOnPlaneEdges[s]++] = n;
index = indexPtr[s];
index[n++] = edgeSplitVertex[e2];
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v1 );
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v2 );
indexNum[s] = n;
s ^= 1;
n = indexNum[s];
onPlaneEdges[s][numOnPlaneEdges[s]++] = n;
index = indexPtr[s];
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v1 );
index[n++] = edgeSplitVertex[e2];
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v0 );
indexNum[s] = n;
break;
}
case 5: { // first and third edge split
s = sides[v0] & SIDE_BACK;
n = indexNum[s];
onPlaneEdges[s][numOnPlaneEdges[s]++] = n;
index = indexPtr[s];
index[n++] = edgeSplitVertex[e0];
index[n++] = edgeSplitVertex[e2];
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v0 );
indexNum[s] = n;
s ^= 1;
n = indexNum[s];
onPlaneEdges[s][numOnPlaneEdges[s]++] = n;
index = indexPtr[s];
index[n++] = edgeSplitVertex[e2];
index[n++] = edgeSplitVertex[e0];
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v1 );
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v1 );
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v2 );
index[n++] = edgeSplitVertex[e2];
indexNum[s] = n;
break;
}
case 6: { // second and third edge split
s = sides[v2] & SIDE_BACK;
n = indexNum[s];
onPlaneEdges[s][numOnPlaneEdges[s]++] = n;
index = indexPtr[s];
index[n++] = edgeSplitVertex[e2];
index[n++] = edgeSplitVertex[e1];
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v2 );
indexNum[s] = n;
s ^= 1;
n = indexNum[s];
onPlaneEdges[s][numOnPlaneEdges[s]++] = n;
index = indexPtr[s];
index[n++] = edgeSplitVertex[e1];
index[n++] = edgeSplitVertex[e2];
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v1 );
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v0 );
index[n++] = UpdateVertexIndex( vertexIndexNum[s], vertexRemap[s], vertexCopyIndex[s], v1 );
index[n++] = edgeSplitVertex[e2];
indexNum[s] = n;
break;
}
}
}
surface[0]->indexes.SetNum( indexNum[0] );
surface[1]->indexes.SetNum( indexNum[1] );
// copy vertexes
surface[0]->verts.SetNum( vertexIndexNum[0][1] );
index = vertexCopyIndex[0];
for ( i = numEdgeSplitVertexes; i < surface[0]->verts.Num(); i++ ) {
surface[0]->verts[i] = verts[index[i]];
}
surface[1]->verts.SetNum( vertexIndexNum[1][1] );
index = vertexCopyIndex[1];
for ( i = numEdgeSplitVertexes; i < surface[1]->verts.Num(); i++ ) {
surface[1]->verts[i] = verts[index[i]];
}
// generate edge indexes
surface[0]->GenerateEdgeIndexes();
surface[1]->GenerateEdgeIndexes();
if ( frontOnPlaneEdges ) {
memcpy( frontOnPlaneEdges, onPlaneEdges[0], numOnPlaneEdges[0] * sizeof( int ) );
frontOnPlaneEdges[numOnPlaneEdges[0]] = -1;
}
if ( backOnPlaneEdges ) {
memcpy( backOnPlaneEdges, onPlaneEdges[1], numOnPlaneEdges[1] * sizeof( int ) );
backOnPlaneEdges[numOnPlaneEdges[1]] = -1;
}
return SIDE_CROSS;
}
/*
=================
idSurface::ClipInPlace
=================
*/
bool idSurface::ClipInPlace( const idPlane &plane, const float epsilon, const bool keepOn ) {
float * dists;
float f;
byte * sides;
int counts[3];
int i;
int * edgeSplitVertex;
int * vertexRemap;
int vertexIndexNum[2];
int * vertexCopyIndex;
int * indexPtr;
int indexNum;
int numEdgeSplitVertexes;
idDrawVert v;
idList<idDrawVert, TAG_IDLIB_LIST_SURFACE> newVerts;
idList<int, TAG_IDLIB_LIST_SURFACE> newIndexes;
dists = (float *) _alloca( verts.Num() * sizeof( float ) );
sides = (byte *) _alloca( verts.Num() * sizeof( byte ) );
counts[0] = counts[1] = counts[2] = 0;
// determine side for each vertex
for ( i = 0; i < verts.Num(); i++ ) {
dists[i] = f = plane.Distance( verts[i].xyz );
if ( f > epsilon ) {
sides[i] = SIDE_FRONT;
} else if ( f < -epsilon ) {
sides[i] = SIDE_BACK;
} else {
sides[i] = SIDE_ON;
}
counts[sides[i]]++;
}
// if coplanar, put on the front side if the normals match
if ( !counts[SIDE_FRONT] && !counts[SIDE_BACK] ) {
f = ( verts[indexes[1]].xyz - verts[indexes[0]].xyz ).Cross( verts[indexes[0]].xyz - verts[indexes[2]].xyz ) * plane.Normal();
if ( IEEE_FLT_SIGNBITSET( f ) ) {
Clear();
return false;
} else {
return true;
}
}
// if nothing at the front of the clipping plane
if ( !counts[SIDE_FRONT] ) {
Clear();
return false;
}
// if nothing at the back of the clipping plane
if ( !counts[SIDE_BACK] ) {
return true;
}
edgeSplitVertex = (int *) _alloca( edges.Num() * sizeof( int ) );
numEdgeSplitVertexes = 0;
counts[SIDE_FRONT] = counts[SIDE_BACK] = 0;
// split edges
for ( i = 0; i < edges.Num(); i++ ) {
int v0 = edges[i].verts[0];
int v1 = edges[i].verts[1];
// if both vertexes are on the same side or one is on the clipping plane
if ( !( sides[v0] ^ sides[v1] ) || ( ( sides[v0] | sides[v1] ) & SIDE_ON ) ) {
edgeSplitVertex[i] = -1;
counts[(sides[v0]|sides[v1]) & SIDE_BACK]++;
} else {
f = dists[v0] / ( dists[v0] - dists[v1] );
v.LerpAll( verts[v0], verts[v1], f );
edgeSplitVertex[i] = numEdgeSplitVertexes++;
newVerts.Append( v );
}
}
// each edge is shared by at most two triangles, as such there can never be
// more indexes than twice the number of edges
newIndexes.Resize( ( counts[SIDE_FRONT] << 1 ) + ( numEdgeSplitVertexes << 2 ) );
// allocate indexes to construct the triangle indexes for the front and back surface
vertexRemap = (int *) _alloca( verts.Num() * sizeof( int ) );
memset( vertexRemap, -1, verts.Num() * sizeof( int ) );
vertexCopyIndex = (int *) _alloca( ( numEdgeSplitVertexes + verts.Num() ) * sizeof( int ) );
vertexIndexNum[0] = 0;
vertexIndexNum[1] = numEdgeSplitVertexes;
indexPtr = newIndexes.Ptr();
indexNum = newIndexes.Num();
// split surface triangles
for ( i = 0; i < edgeIndexes.Num(); i += 3 ) {
int e0, e1, e2, v0, v1, v2;
e0 = abs( edgeIndexes[i+0] );
e1 = abs( edgeIndexes[i+1] );
e2 = abs( edgeIndexes[i+2] );
v0 = indexes[i+0];
v1 = indexes[i+1];
v2 = indexes[i+2];
switch( ( INT32_SIGNBITSET( edgeSplitVertex[e0] ) | ( INT32_SIGNBITSET( edgeSplitVertex[e1] ) << 1 ) | ( INT32_SIGNBITSET( edgeSplitVertex[e2] ) << 2 ) ) ^ 7 ) {
case 0: { // no edges split
if ( ( sides[v0] | sides[v1] | sides[v2] ) & SIDE_BACK ) {
break;
}
if ( ( sides[v0] & sides[v1] & sides[v2] ) & SIDE_ON ) {
// coplanar
if ( !keepOn ) {
break;
}
f = ( verts[v1].xyz - verts[v0].xyz ).Cross( verts[v0].xyz - verts[v2].xyz ) * plane.Normal();
if ( IEEE_FLT_SIGNBITSET( f ) ) {
break;
}
}
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v0 );
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v1 );
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v2 );
break;
}
case 1: { // first edge split
if ( !( sides[v0] & SIDE_BACK ) ) {
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v0 );
indexPtr[indexNum++] = edgeSplitVertex[e0];
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v2 );
} else {
indexPtr[indexNum++] = edgeSplitVertex[e0];
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v1 );
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v2 );
}
break;
}
case 2: { // second edge split
if ( !( sides[v1] & SIDE_BACK ) ) {
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v1 );
indexPtr[indexNum++] = edgeSplitVertex[e1];
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v0 );
} else {
indexPtr[indexNum++] = edgeSplitVertex[e1];
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v2 );
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v0 );
}
break;
}
case 3: { // first and second edge split
if ( !( sides[v1] & SIDE_BACK ) ) {
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v1 );
indexPtr[indexNum++] = edgeSplitVertex[e1];
indexPtr[indexNum++] = edgeSplitVertex[e0];
} else {
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v0 );
indexPtr[indexNum++] = edgeSplitVertex[e0];
indexPtr[indexNum++] = edgeSplitVertex[e1];
indexPtr[indexNum++] = edgeSplitVertex[e1];
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v2 );
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v0 );
}
break;
}
case 4: { // third edge split
if ( !( sides[v2] & SIDE_BACK ) ) {
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v2 );
indexPtr[indexNum++] = edgeSplitVertex[e2];
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v1 );
} else {
indexPtr[indexNum++] = edgeSplitVertex[e2];
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v0 );
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v1 );
}
break;
}
case 5: { // first and third edge split
if ( !( sides[v0] & SIDE_BACK ) ) {
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v0 );
indexPtr[indexNum++] = edgeSplitVertex[e0];
indexPtr[indexNum++] = edgeSplitVertex[e2];
} else {
indexPtr[indexNum++] = edgeSplitVertex[e0];
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v1 );
indexPtr[indexNum++] = edgeSplitVertex[e2];
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v1 );
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v2 );
indexPtr[indexNum++] = edgeSplitVertex[e2];
}
break;
}
case 6: { // second and third edge split
if ( !( sides[v2] & SIDE_BACK ) ) {
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v2 );
indexPtr[indexNum++] = edgeSplitVertex[e2];
indexPtr[indexNum++] = edgeSplitVertex[e1];
} else {
indexPtr[indexNum++] = edgeSplitVertex[e2];
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v1 );
indexPtr[indexNum++] = edgeSplitVertex[e1];
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v0 );
indexPtr[indexNum++] = UpdateVertexIndex( vertexIndexNum, vertexRemap, vertexCopyIndex, v1 );
indexPtr[indexNum++] = edgeSplitVertex[e2];
}
break;
}
}
}
newIndexes.SetNum( indexNum );
// copy vertexes
newVerts.SetNum( vertexIndexNum[1] );
for ( i = numEdgeSplitVertexes; i < newVerts.Num(); i++ ) {
newVerts[i] = verts[vertexCopyIndex[i]];
}
// copy back to this surface
indexes = newIndexes;
verts = newVerts;
GenerateEdgeIndexes();
return true;
}
/*
=============
idSurface::IsConnected
=============
*/
bool idSurface::IsConnected() const {
int i, j, numIslands, numTris;
int queueStart, queueEnd;
int *queue, *islandNum;
int curTri, nextTri, edgeNum;
const int *index;
numIslands = 0;
numTris = indexes.Num() / 3;
islandNum = (int *) _alloca16( numTris * sizeof( int ) );
memset( islandNum, -1, numTris * sizeof( int ) );
queue = (int *) _alloca16( numTris * sizeof( int ) );
for ( i = 0; i < numTris; i++ ) {
if ( islandNum[i] != -1 ) {
continue;
}
queueStart = 0;
queueEnd = 1;
queue[0] = i;
islandNum[i] = numIslands;
for ( curTri = queue[queueStart]; queueStart < queueEnd; curTri = queue[++queueStart] ) {
index = &edgeIndexes[curTri * 3];
for ( j = 0; j < 3; j++ ) {
edgeNum = index[j];
nextTri = edges[abs(edgeNum)].tris[INT32_SIGNBITNOTSET(edgeNum)];
if ( nextTri == -1 ) {
continue;
}
nextTri /= 3;
if ( islandNum[nextTri] != -1 ) {
continue;
}
queue[queueEnd++] = nextTri;
islandNum[nextTri] = numIslands;
}
}
numIslands++;
}
return ( numIslands == 1 );
}
/*
=================
idSurface::IsClosed
=================
*/
bool idSurface::IsClosed() const {
for ( int i = 0; i < edges.Num(); i++ ) {
if ( edges[i].tris[0] < 0 || edges[i].tris[1] < 0 ) {
return false;
}
}
return true;
}
/*
=============
idSurface::IsPolytope
=============
*/
bool idSurface::IsPolytope( const float epsilon ) const {
int i, j;
idPlane plane;
if ( !IsClosed() ) {
return false;
}
for ( i = 0; i < indexes.Num(); i += 3 ) {
plane.FromPoints( verts[indexes[i+0]].xyz, verts[indexes[i+1]].xyz, verts[indexes[i+2]].xyz );
for ( j = 0; j < verts.Num(); j++ ) {
if ( plane.Side( verts[j].xyz, epsilon ) == SIDE_FRONT ) {
return false;
}
}
}
return true;
}
/*
=============
idSurface::PlaneDistance
=============
*/
float idSurface::PlaneDistance( const idPlane &plane ) const {
int i;
float d, min, max;
min = idMath::INFINITY;
max = -min;
for ( i = 0; i < verts.Num(); i++ ) {
d = plane.Distance( verts[i].xyz );
if ( d < min ) {
min = d;
if ( IEEE_FLT_SIGNBITSET( min ) & IEEE_FLT_SIGNBITNOTSET( max ) ) {
return 0.0f;
}
}
if ( d > max ) {
max = d;
if ( IEEE_FLT_SIGNBITSET( min ) & IEEE_FLT_SIGNBITNOTSET( max ) ) {
return 0.0f;
}
}
}
if ( IEEE_FLT_SIGNBITNOTSET( min ) ) {
return min;
}
if ( IEEE_FLT_SIGNBITSET( max ) ) {
return max;
}
return 0.0f;
}
/*
=============
idSurface::PlaneSide
=============
*/
int idSurface::PlaneSide( const idPlane &plane, const float epsilon ) const {
bool front, back;
int i;
float d;
front = false;
back = false;
for ( i = 0; i < verts.Num(); i++ ) {
d = plane.Distance( verts[i].xyz );
if ( d < -epsilon ) {
if ( front ) {
return SIDE_CROSS;
}
back = true;
continue;
}
else if ( d > epsilon ) {
if ( back ) {
return SIDE_CROSS;
}
front = true;
continue;
}
}
if ( back ) {
return SIDE_BACK;
}
if ( front ) {
return SIDE_FRONT;
}
return SIDE_ON;
}
/*
=================
idSurface::LineIntersection
=================
*/
bool idSurface::LineIntersection( const idVec3 &start, const idVec3 &end, bool backFaceCull ) const {
float scale;
RayIntersection( start, end - start, scale, false );
return ( scale >= 0.0f && scale <= 1.0f );
}
/*
=================
idSurface::RayIntersection
=================
*/
bool idSurface::RayIntersection( const idVec3 &start, const idVec3 &dir, float &scale, bool backFaceCull ) const {
int i, i0, i1, i2, s0, s1, s2;
float d, s;
byte *sidedness;
idPluecker rayPl, pl;
idPlane plane;
sidedness = (byte *)_alloca( edges.Num() * sizeof(byte) );
scale = idMath::INFINITY;
rayPl.FromRay( start, dir );
// ray sidedness for edges
for ( i = 0; i < edges.Num(); i++ ) {
pl.FromLine( verts[ edges[i].verts[1] ].xyz, verts[ edges[i].verts[0] ].xyz );
d = pl.PermutedInnerProduct( rayPl );
sidedness[ i ] = IEEE_FLT_SIGNBITSET( d );
}
// test triangles
for ( i = 0; i < edgeIndexes.Num(); i += 3 ) {
i0 = edgeIndexes[i+0];
i1 = edgeIndexes[i+1];
i2 = edgeIndexes[i+2];
s0 = sidedness[abs(i0)] ^ INT32_SIGNBITSET( i0 );
s1 = sidedness[abs(i1)] ^ INT32_SIGNBITSET( i1 );
s2 = sidedness[abs(i2)] ^ INT32_SIGNBITSET( i2 );
if ( s0 & s1 & s2 ) {
plane.FromPoints( verts[indexes[i+0]].xyz, verts[indexes[i+1]].xyz, verts[indexes[i+2]].xyz );
plane.RayIntersection( start, dir, s );
if ( idMath::Fabs( s ) < idMath::Fabs( scale ) ) {
scale = s;
}
} else if ( !backFaceCull && !(s0 | s1 | s2) ) {
plane.FromPoints( verts[indexes[i+0]].xyz, verts[indexes[i+1]].xyz, verts[indexes[i+2]].xyz );
plane.RayIntersection( start, dir, s );
if ( idMath::Fabs( s ) < idMath::Fabs( scale ) ) {
scale = s;
}
}
}
if ( idMath::Fabs( scale ) < idMath::INFINITY ) {
return true;
}
return false;
}
/*
=================
idSurface::GenerateEdgeIndexes
Assumes each edge is shared by at most two triangles.
=================
*/
void idSurface::GenerateEdgeIndexes() {
int i, j, i0, i1, i2, s, v0, v1, edgeNum;
int *index, *vertexEdges, *edgeChain;
surfaceEdge_t e[3];
vertexEdges = (int *) _alloca16( verts.Num() * sizeof( int ) );
memset( vertexEdges, -1, verts.Num() * sizeof( int ) );
edgeChain = (int *) _alloca16( indexes.Num() * sizeof( int ) );
edgeIndexes.SetNum( indexes.Num() );
edges.Clear();
// the first edge is a dummy
e[0].verts[0] = e[0].verts[1] = e[0].tris[0] = e[0].tris[1] = 0;
edges.Append( e[0] );
for ( i = 0; i < indexes.Num(); i += 3 ) {
index = indexes.Ptr() + i;
// vertex numbers
i0 = index[0];
i1 = index[1];
i2 = index[2];
// setup edges each with smallest vertex number first
s = INT32_SIGNBITSET(i1 - i0);
e[0].verts[0] = index[s];
e[0].verts[1] = index[s^1];
s = INT32_SIGNBITSET(i2 - i1) + 1;
e[1].verts[0] = index[s];
e[1].verts[1] = index[s^3];
s = INT32_SIGNBITSET(i2 - i0) << 1;
e[2].verts[0] = index[s];
e[2].verts[1] = index[s^2];
// get edges
for ( j = 0; j < 3; j++ ) {
v0 = e[j].verts[0];
v1 = e[j].verts[1];
for ( edgeNum = vertexEdges[v0]; edgeNum >= 0; edgeNum = edgeChain[edgeNum] ) {
if ( edges[edgeNum].verts[1] == v1 ) {
break;
}
}
// if the edge does not yet exist
if ( edgeNum < 0 ) {
e[j].tris[0] = e[j].tris[1] = -1;
edgeNum = edges.Append( e[j] );
edgeChain[edgeNum] = vertexEdges[v0];
vertexEdges[v0] = edgeNum;
}
// update edge index and edge tri references
if ( index[j] == v0 ) {
assert( edges[edgeNum].tris[0] == -1 ); // edge may not be shared by more than two triangles
edges[edgeNum].tris[0] = i;
edgeIndexes[i+j] = edgeNum;
} else {
assert( edges[edgeNum].tris[1] == -1 ); // edge may not be shared by more than two triangles
edges[edgeNum].tris[1] = i;
edgeIndexes[i+j] = -edgeNum;
}
}
}
}
/*
=================
idSurface::FindEdge
=================
*/
int idSurface::FindEdge( int v1, int v2 ) const {
int i, firstVert, secondVert;
if ( v1 < v2 ) {
firstVert = v1;
secondVert = v2;
} else {
firstVert = v2;
secondVert = v1;
}
for ( i = 1; i < edges.Num(); i++ ) {
if ( edges[i].verts[0] == firstVert ) {
if ( edges[i].verts[1] == secondVert ) {
break;
}
}
}
if ( i < edges.Num() ) {
return v1 < v2 ? i : -i;
}
return 0;
}

View File

@@ -0,0 +1,219 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#ifndef __SURFACE_H__
#define __SURFACE_H__
/*
===============================================================================
Surface base class.
A surface is tesselated to a triangle mesh with each edge shared by
at most two triangles.
===============================================================================
*/
typedef struct surfaceEdge_s {
int verts[2]; // edge vertices always with ( verts[0] < verts[1] )
int tris[2]; // edge triangles
} surfaceEdge_t;
class idSurface {
public:
idSurface();
explicit idSurface( const idSurface &surf );
explicit idSurface( const idDrawVert *verts, const int numVerts, const int *indexes, const int numIndexes );
~idSurface();
const idDrawVert & operator[]( const int index ) const;
idDrawVert & operator[]( const int index );
idSurface & operator+=( const idSurface &surf );
int GetNumIndexes() const { return indexes.Num(); }
const int * GetIndexes() const { return indexes.Ptr(); }
int GetNumVertices() const { return verts.Num(); }
const idDrawVert * GetVertices() const { return verts.Ptr(); }
const int * GetEdgeIndexes() const { return edgeIndexes.Ptr(); }
const surfaceEdge_t * GetEdges() const { return edges.Ptr(); }
void Clear();
void TranslateSelf( const idVec3 &translation );
void RotateSelf( const idMat3 &rotation );
// splits the surface into a front and back surface, the surface itself stays unchanged
// frontOnPlaneEdges and backOnPlaneEdges optionally store the indexes to the edges that lay on the split plane
// returns a SIDE_?
int Split( const idPlane &plane, const float epsilon, idSurface **front, idSurface **back, int *frontOnPlaneEdges = NULL, int *backOnPlaneEdges = NULL ) const;
// cuts off the part at the back side of the plane, returns true if some part was at the front
// if there is nothing at the front the number of points is set to zero
bool ClipInPlace( const idPlane &plane, const float epsilon = ON_EPSILON, const bool keepOn = false );
// returns true if each triangle can be reached from any other triangle by a traversal
bool IsConnected() const;
// returns true if the surface is closed
bool IsClosed() const;
// returns true if the surface is a convex hull
bool IsPolytope( const float epsilon = 0.1f ) const;
float PlaneDistance( const idPlane &plane ) const;
int PlaneSide( const idPlane &plane, const float epsilon = ON_EPSILON ) const;
// returns true if the line intersects one of the surface triangles
bool LineIntersection( const idVec3 &start, const idVec3 &end, bool backFaceCull = false ) const;
// intersection point is start + dir * scale
bool RayIntersection( const idVec3 &start, const idVec3 &dir, float &scale, bool backFaceCull = false ) const;
protected:
idList<idDrawVert, TAG_IDLIB_LIST_SURFACE> verts; // vertices
idList<int, TAG_IDLIB_LIST_SURFACE> indexes; // 3 references to vertices for each triangle
idList<surfaceEdge_t, TAG_IDLIB_LIST_SURFACE> edges; // edges
idList<int, TAG_IDLIB_LIST_SURFACE> edgeIndexes; // 3 references to edges for each triangle, may be negative for reversed edge
protected:
void GenerateEdgeIndexes();
int FindEdge( int v1, int v2 ) const;
};
/*
====================
idSurface::idSurface
====================
*/
ID_INLINE idSurface::idSurface() {
}
/*
=================
idSurface::idSurface
=================
*/
ID_INLINE idSurface::idSurface( const idDrawVert *verts, const int numVerts, const int *indexes, const int numIndexes ) {
assert( verts != NULL && indexes != NULL && numVerts > 0 && numIndexes > 0 );
this->verts.SetNum( numVerts );
memcpy( this->verts.Ptr(), verts, numVerts * sizeof( verts[0] ) );
this->indexes.SetNum( numIndexes );
memcpy( this->indexes.Ptr(), indexes, numIndexes * sizeof( indexes[0] ) );
GenerateEdgeIndexes();
}
/*
====================
idSurface::idSurface
====================
*/
ID_INLINE idSurface::idSurface( const idSurface &surf ) {
this->verts = surf.verts;
this->indexes = surf.indexes;
this->edges = surf.edges;
this->edgeIndexes = surf.edgeIndexes;
}
/*
====================
idSurface::~idSurface
====================
*/
ID_INLINE idSurface::~idSurface() {
}
/*
=================
idSurface::operator[]
=================
*/
ID_INLINE const idDrawVert &idSurface::operator[]( const int index ) const {
return verts[ index ];
};
/*
=================
idSurface::operator[]
=================
*/
ID_INLINE idDrawVert &idSurface::operator[]( const int index ) {
return verts[ index ];
};
/*
=================
idSurface::operator+=
=================
*/
ID_INLINE idSurface &idSurface::operator+=( const idSurface &surf ) {
int i, m, n;
n = verts.Num();
m = indexes.Num();
verts.Append( surf.verts ); // merge verts where possible ?
indexes.Append( surf.indexes );
for ( i = m; i < indexes.Num(); i++ ) {
indexes[i] += n;
}
GenerateEdgeIndexes();
return *this;
}
/*
=================
idSurface::Clear
=================
*/
ID_INLINE void idSurface::Clear() {
verts.Clear();
indexes.Clear();
edges.Clear();
edgeIndexes.Clear();
}
/*
=================
idSurface::TranslateSelf
=================
*/
ID_INLINE void idSurface::TranslateSelf( const idVec3 &translation ) {
for ( int i = 0; i < verts.Num(); i++ ) {
verts[i].xyz += translation;
}
}
/*
=================
idSurface::RotateSelf
=================
*/
ID_INLINE void idSurface::RotateSelf( const idMat3 &rotation ) {
for ( int i = 0; i < verts.Num(); i++ ) {
verts[i].xyz *= rotation;
verts[i].SetNormal( verts[i].GetNormal() * rotation );
verts[i].SetTangent( verts[i].GetTangent() * rotation );
}
}
#endif /* !__SURFACE_H__ */

View File

@@ -0,0 +1,698 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#pragma hdrstop
#include "../precompiled.h"
/*
=================
idSurface_Patch::SetSize
=================
*/
void idSurface_Patch::SetSize( int patchWidth, int patchHeight ) {
if ( patchWidth < 1 || patchWidth > maxWidth ) {
idLib::common->FatalError("idSurface_Patch::SetSize: invalid patchWidth");
}
if ( patchHeight < 1 || patchHeight > maxHeight ) {
idLib::common->FatalError("idSurface_Patch::SetSize: invalid patchHeight");
}
width = patchWidth;
height = patchHeight;
verts.SetNum( width * height );
}
/*
=================
idSurface_Patch::PutOnCurve
Expects an expanded patch.
=================
*/
void idSurface_Patch::PutOnCurve() {
int i, j;
idDrawVert prev, next;
assert( expanded == true );
// put all the approximating points on the curve
for ( i = 0; i < width; i++ ) {
for ( j = 1; j < height; j += 2 ) {
LerpVert( verts[j*maxWidth+i], verts[(j+1)*maxWidth+i], prev );
LerpVert( verts[j*maxWidth+i], verts[(j-1)*maxWidth+i], next );
LerpVert( prev, next, verts[j*maxWidth+i] );
}
}
for ( j = 0; j < height; j++ ) {
for ( i = 1; i < width; i += 2 ) {
LerpVert( verts[j*maxWidth+i], verts[j*maxWidth+i+1], prev );
LerpVert( verts[j*maxWidth+i], verts[j*maxWidth+i-1], next );
LerpVert( prev, next, verts[j*maxWidth+i] );
}
}
}
/*
================
idSurface_Patch::ProjectPointOntoVector
================
*/
void idSurface_Patch::ProjectPointOntoVector( const idVec3 &point, const idVec3 &vStart, const idVec3 &vEnd, idVec3 &vProj ) {
idVec3 pVec, vec;
pVec = point - vStart;
vec = vEnd - vStart;
vec.Normalize();
// project onto the directional vector for this segment
vProj = vStart + (pVec * vec) * vec;
}
/*
================
idSurface_Patch::RemoveLinearColumnsRows
Expects an expanded patch.
================
*/
void idSurface_Patch::RemoveLinearColumnsRows() {
int i, j, k;
float len, maxLength;
idVec3 proj, dir;
assert( expanded == true );
for ( j = 1; j < width - 1; j++ ) {
maxLength = 0;
for ( i = 0; i < height; i++ ) {
idSurface_Patch::ProjectPointOntoVector( verts[i*maxWidth + j].xyz,
verts[i*maxWidth + j-1].xyz, verts[i*maxWidth + j+1].xyz, proj);
dir = verts[i*maxWidth + j].xyz - proj;
len = dir.LengthSqr();
if ( len > maxLength ) {
maxLength = len;
}
}
if ( maxLength < Square( 0.2f ) ) {
width--;
for ( i = 0; i < height; i++ ) {
for ( k = j; k < width; k++ ) {
verts[i*maxWidth + k] = verts[i*maxWidth + k+1];
}
}
j--;
}
}
for ( j = 1; j < height - 1; j++ ) {
maxLength = 0;
for ( i = 0; i < width; i++ ) {
idSurface_Patch::ProjectPointOntoVector( verts[j*maxWidth + i].xyz,
verts[(j-1)*maxWidth + i].xyz, verts[(j+1)*maxWidth + i].xyz, proj);
dir = verts[j*maxWidth + i].xyz - proj;
len = dir.LengthSqr();
if ( len > maxLength ) {
maxLength = len;
}
}
if ( maxLength < Square( 0.2f ) ) {
height--;
for ( i = 0; i < width; i++ ) {
for ( k = j; k < height; k++ ) {
verts[k*maxWidth + i] = verts[(k+1)*maxWidth + i];
}
}
j--;
}
}
}
/*
================
idSurface_Patch::ResizeExpanded
================
*/
void idSurface_Patch::ResizeExpanded( int newHeight, int newWidth ) {
int i, j;
assert( expanded == true );
if ( newHeight <= maxHeight && newWidth <= maxWidth ) {
return;
}
if ( newHeight * newWidth > maxHeight * maxWidth ) {
verts.SetNum( newHeight * newWidth );
}
// space out verts for new height and width
for ( j = maxHeight-1; j >= 0; j-- ) {
for ( i = maxWidth-1; i >= 0; i-- ) {
verts[j*newWidth + i] = verts[j*maxWidth + i];
}
}
maxHeight = newHeight;
maxWidth = newWidth;
}
/*
================
idSurface_Patch::Collapse
================
*/
void idSurface_Patch::Collapse() {
int i, j;
if ( !expanded ) {
idLib::common->FatalError("idSurface_Patch::Collapse: patch not expanded");
}
expanded = false;
if ( width != maxWidth ) {
for ( j = 0; j < height; j++ ) {
for ( i = 0; i < width; i++ ) {
verts[j*width + i] = verts[j*maxWidth + i];
}
}
}
verts.SetNum( width * height );
}
/*
================
idSurface_Patch::Expand
================
*/
void idSurface_Patch::Expand() {
int i, j;
if ( expanded ) {
idLib::common->FatalError("idSurface_Patch::Expand: patch alread expanded");
}
expanded = true;
verts.SetNum( maxWidth * maxHeight );
if ( width != maxWidth ) {
for ( j = height-1; j >= 0; j-- ) {
for ( i = width-1; i >= 0; i-- ) {
verts[j*maxWidth + i] = verts[j*width + i];
}
}
}
}
/*
============
idSurface_Patch::LerpVert
============
*/
void idSurface_Patch::LerpVert( const idDrawVert &a, const idDrawVert &b, idDrawVert &out ) const {
out.xyz[0] = 0.5f * ( a.xyz[0] + b.xyz[0] );
out.xyz[1] = 0.5f * ( a.xyz[1] + b.xyz[1] );
out.xyz[2] = 0.5f * ( a.xyz[2] + b.xyz[2] );
out.SetNormal( ( a.GetNormal() + b.GetNormal() ) * 0.5f );
out.SetTexCoord( ( a.GetTexCoord() + b.GetTexCoord() ) * 0.5f );
}
/*
=================
idSurface_Patch::GenerateNormals
Handles all the complicated wrapping and degenerate cases
Expects a Not expanded patch.
=================
*/
#define COPLANAR_EPSILON 0.1f
void idSurface_Patch::GenerateNormals() {
int i, j, k, dist;
idVec3 norm;
idVec3 sum;
int count;
idVec3 base;
idVec3 delta;
int x, y;
idVec3 around[8], temp;
bool good[8];
bool wrapWidth, wrapHeight;
static int neighbors[8][2] = {
{0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1}
};
assert( expanded == false );
//
// if all points are coplanar, set all normals to that plane
//
idVec3 extent[3];
float offset;
extent[0] = verts[width - 1].xyz - verts[0].xyz;
extent[1] = verts[(height-1) * width + width - 1].xyz - verts[0].xyz;
extent[2] = verts[(height-1) * width].xyz - verts[0].xyz;
norm = extent[0].Cross( extent[1] );
if ( norm.LengthSqr() == 0.0f ) {
norm = extent[0].Cross( extent[2] );
if ( norm.LengthSqr() == 0.0f ) {
norm = extent[1].Cross( extent[2] );
}
}
// wrapped patched may not get a valid normal here
if ( norm.Normalize() != 0.0f ) {
offset = verts[0].xyz * norm;
for ( i = 1; i < width * height; i++ ) {
float d = verts[i].xyz * norm;
if ( idMath::Fabs( d - offset ) > COPLANAR_EPSILON ) {
break;
}
}
if ( i == width * height ) {
// all are coplanar
for ( i = 0; i < width * height; i++ ) {
verts[i].SetNormal( norm );
}
return;
}
}
// check for wrapped edge cases, which should smooth across themselves
wrapWidth = false;
for ( i = 0; i < height; i++ ) {
delta = verts[i * width].xyz - verts[i * width + width-1].xyz;
if ( delta.LengthSqr() > Square( 1.0f ) ) {
break;
}
}
if ( i == height ) {
wrapWidth = true;
}
wrapHeight = false;
for ( i = 0; i < width; i++ ) {
delta = verts[i].xyz - verts[(height-1) * width + i].xyz;
if ( delta.LengthSqr() > Square( 1.0f ) ) {
break;
}
}
if ( i == width ) {
wrapHeight = true;
}
for ( i = 0; i < width; i++ ) {
for ( j = 0; j < height; j++ ) {
count = 0;
base = verts[j * width + i].xyz;
for ( k = 0; k < 8; k++ ) {
around[k] = vec3_origin;
good[k] = false;
for ( dist = 1; dist <= 3; dist++ ) {
x = i + neighbors[k][0] * dist;
y = j + neighbors[k][1] * dist;
if ( wrapWidth ) {
if ( x < 0 ) {
x = width - 1 + x;
} else if ( x >= width ) {
x = 1 + x - width;
}
}
if ( wrapHeight ) {
if ( y < 0 ) {
y = height - 1 + y;
} else if ( y >= height ) {
y = 1 + y - height;
}
}
if ( x < 0 || x >= width || y < 0 || y >= height ) {
break; // edge of patch
}
temp = verts[y * width + x].xyz - base;
if ( temp.Normalize() == 0.0f ) {
continue; // degenerate edge, get more dist
} else {
good[k] = true;
around[k] = temp;
break; // good edge
}
}
}
sum = vec3_origin;
for ( k = 0; k < 8; k++ ) {
if ( !good[k] || !good[(k+1)&7] ) {
continue; // didn't get two points
}
norm = around[(k+1)&7].Cross( around[k] );
if ( norm.Normalize() == 0.0f ) {
continue;
}
sum += norm;
count++;
}
if ( count == 0 ) {
//idLib::common->Printf("bad normal\n");
count = 1;
}
sum.Normalize();
verts[j * width + i].SetNormal( sum );
}
}
}
/*
=================
idSurface_Patch::GenerateIndexes
=================
*/
void idSurface_Patch::GenerateIndexes() {
int i, j, v1, v2, v3, v4, index;
indexes.SetNum( (width-1) * (height-1) * 2 * 3 );
index = 0;
for ( i = 0; i < width - 1; i++ ) {
for ( j = 0; j < height - 1; j++ ) {
v1 = j * width + i;
v2 = v1 + 1;
v3 = v1 + width + 1;
v4 = v1 + width;
indexes[index++] = v1;
indexes[index++] = v3;
indexes[index++] = v2;
indexes[index++] = v1;
indexes[index++] = v4;
indexes[index++] = v3;
}
}
GenerateEdgeIndexes();
}
/*
===============
idSurface_Patch::SampleSinglePatchPoint
===============
*/
void idSurface_Patch::SampleSinglePatchPoint( const idDrawVert ctrl[3][3], float u, float v, idDrawVert *out ) const {
float vCtrl[3][8];
int vPoint;
int axis;
// find the control points for the v coordinate
for ( vPoint = 0; vPoint < 3; vPoint++ ) {
for ( axis = 0; axis < 8; axis++ ) {
float a, b, c;
float qA, qB, qC;
if ( axis < 3 ) {
a = ctrl[0][vPoint].xyz[axis];
b = ctrl[1][vPoint].xyz[axis];
c = ctrl[2][vPoint].xyz[axis];
} else if ( axis < 6 ) {
a = ctrl[0][vPoint].GetNormal()[axis-3];
b = ctrl[1][vPoint].GetNormal()[axis-3];
c = ctrl[2][vPoint].GetNormal()[axis-3];
} else {
a = ctrl[0][vPoint].GetTexCoord()[axis-6];
b = ctrl[1][vPoint].GetTexCoord()[axis-6];
c = ctrl[2][vPoint].GetTexCoord()[axis-6];
}
qA = a - 2.0f * b + c;
qB = 2.0f * b - 2.0f * a;
qC = a;
vCtrl[vPoint][axis] = qA * u * u + qB * u + qC;
}
}
// interpolate the v value
for ( axis = 0; axis < 8; axis++ ) {
float a, b, c;
float qA, qB, qC;
a = vCtrl[0][axis];
b = vCtrl[1][axis];
c = vCtrl[2][axis];
qA = a - 2.0f * b + c;
qB = 2.0f * b - 2.0f * a;
qC = a;
if ( axis < 3 ) {
out->xyz[axis] = qA * v * v + qB * v + qC;
} else if ( axis < 6 ) {
idVec3 tempNormal = out->GetNormal();
tempNormal[axis-3] = qA * v * v + qB * v + qC;
out->SetNormal( tempNormal );
//out->normal[axis-3] = qA * v * v + qB * v + qC;
} else {
idVec2 tempST = out->GetTexCoord();
tempST[axis-6] = qA * v * v + qB * v + qC;
out->SetTexCoord( tempST );
}
}
}
/*
===================
idSurface_Patch::SampleSinglePatch
===================
*/
void idSurface_Patch::SampleSinglePatch( const idDrawVert ctrl[3][3], int baseCol, int baseRow, int width, int horzSub, int vertSub, idDrawVert *outVerts ) const {
int i, j;
float u, v;
horzSub++;
vertSub++;
for ( i = 0; i < horzSub; i++ ) {
for ( j = 0; j < vertSub; j++ ) {
u = (float) i / ( horzSub - 1 );
v = (float) j / ( vertSub - 1 );
SampleSinglePatchPoint( ctrl, u, v, &outVerts[((baseRow + j) * width) + i + baseCol] );
}
}
}
/*
=================
idSurface_Patch::SubdivideExplicit
=================
*/
void idSurface_Patch::SubdivideExplicit( int horzSubdivisions, int vertSubdivisions, bool genNormals, bool removeLinear ) {
int i, j, k, l;
idDrawVert sample[3][3];
int outWidth = ((width - 1) / 2 * horzSubdivisions) + 1;
int outHeight = ((height - 1) / 2 * vertSubdivisions) + 1;
idDrawVert *dv = new (TAG_IDLIB_SURFACE) idDrawVert[ outWidth * outHeight ];
// generate normals for the control mesh
if ( genNormals ) {
GenerateNormals();
}
int baseCol = 0;
for ( i = 0; i + 2 < width; i += 2 ) {
int baseRow = 0;
for ( j = 0; j + 2 < height; j += 2 ) {
for ( k = 0; k < 3; k++ ) {
for ( l = 0; l < 3; l++ ) {
sample[k][l] = verts[ ((j + l) * width) + i + k ];
}
}
SampleSinglePatch( sample, baseCol, baseRow, outWidth, horzSubdivisions, vertSubdivisions, dv );
baseRow += vertSubdivisions;
}
baseCol += horzSubdivisions;
}
verts.SetNum( outWidth * outHeight );
for ( i = 0; i < outWidth * outHeight; i++ ) {
verts[i] = dv[i];
}
delete[] dv;
width = maxWidth = outWidth;
height = maxHeight = outHeight;
expanded = false;
if ( removeLinear ) {
Expand();
RemoveLinearColumnsRows();
Collapse();
}
// normalize all the lerped normals
if ( genNormals ) {
idVec3 tempNormal;
for ( i = 0; i < width * height; i++ ) {
tempNormal= verts[i].GetNormal();
tempNormal.Normalize();
verts[i].SetNormal( tempNormal );
}
}
GenerateIndexes();
}
/*
=================
idSurface_Patch::Subdivide
=================
*/
void idSurface_Patch::Subdivide( float maxHorizontalError, float maxVerticalError, float maxLength, bool genNormals ) {
int i, j, k, l;
idDrawVert prev, next, mid;
idVec3 prevxyz, nextxyz, midxyz;
idVec3 delta;
float maxHorizontalErrorSqr, maxVerticalErrorSqr, maxLengthSqr;
// generate normals for the control mesh
if ( genNormals ) {
GenerateNormals();
}
maxHorizontalErrorSqr = Square( maxHorizontalError );
maxVerticalErrorSqr = Square( maxVerticalError );
maxLengthSqr = Square( maxLength );
Expand();
// horizontal subdivisions
for ( j = 0; j + 2 < width; j += 2 ) {
// check subdivided midpoints against control points
for ( i = 0; i < height; i++ ) {
for ( l = 0; l < 3; l++ ) {
prevxyz[l] = verts[i*maxWidth + j+1].xyz[l] - verts[i*maxWidth + j ].xyz[l];
nextxyz[l] = verts[i*maxWidth + j+2].xyz[l] - verts[i*maxWidth + j+1].xyz[l];
midxyz[l] = (verts[i*maxWidth + j ].xyz[l] + verts[i*maxWidth + j+1].xyz[l] * 2.0f +
verts[i*maxWidth + j+2].xyz[l] ) * 0.25f;
}
if ( maxLength > 0.0f ) {
// if the span length is too long, force a subdivision
if ( prevxyz.LengthSqr() > maxLengthSqr || nextxyz.LengthSqr() > maxLengthSqr ) {
break;
}
}
// see if this midpoint is off far enough to subdivide
delta = verts[i*maxWidth + j+1].xyz - midxyz;
if ( delta.LengthSqr() > maxHorizontalErrorSqr ) {
break;
}
}
if ( i == height ) {
continue; // didn't need subdivision
}
if ( width + 2 >= maxWidth ) {
ResizeExpanded( maxHeight, maxWidth + 4 );
}
// insert two columns and replace the peak
width += 2;
for ( i = 0; i < height; i++ ) {
idSurface_Patch::LerpVert( verts[i*maxWidth + j ], verts[i*maxWidth + j+1], prev );
idSurface_Patch::LerpVert( verts[i*maxWidth + j+1], verts[i*maxWidth + j+2], next );
idSurface_Patch::LerpVert( prev, next, mid );
for ( k = width - 1; k > j + 3; k-- ) {
verts[i*maxWidth + k] = verts[i*maxWidth + k-2];
}
verts[i*maxWidth + j+1] = prev;
verts[i*maxWidth + j+2] = mid;
verts[i*maxWidth + j+3] = next;
}
// back up and recheck this set again, it may need more subdivision
j -= 2;
}
// vertical subdivisions
for ( j = 0; j + 2 < height; j += 2 ) {
// check subdivided midpoints against control points
for ( i = 0; i < width; i++ ) {
for ( l = 0; l < 3; l++ ) {
prevxyz[l] = verts[(j+1)*maxWidth + i].xyz[l] - verts[j*maxWidth + i].xyz[l];
nextxyz[l] = verts[(j+2)*maxWidth + i].xyz[l] - verts[(j+1)*maxWidth + i].xyz[l];
midxyz[l] = (verts[j*maxWidth + i].xyz[l] + verts[(j+1)*maxWidth + i].xyz[l] * 2.0f +
verts[(j+2)*maxWidth + i].xyz[l] ) * 0.25f;
}
if ( maxLength > 0.0f ) {
// if the span length is too long, force a subdivision
if ( prevxyz.LengthSqr() > maxLengthSqr || nextxyz.LengthSqr() > maxLengthSqr ) {
break;
}
}
// see if this midpoint is off far enough to subdivide
delta = verts[(j+1)*maxWidth + i].xyz - midxyz;
if ( delta.LengthSqr() > maxVerticalErrorSqr ) {
break;
}
}
if ( i == width ) {
continue; // didn't need subdivision
}
if ( height + 2 >= maxHeight ) {
ResizeExpanded( maxHeight + 4, maxWidth );
}
// insert two columns and replace the peak
height += 2;
for ( i = 0; i < width; i++ ) {
LerpVert( verts[j*maxWidth + i], verts[(j+1)*maxWidth + i], prev );
LerpVert( verts[(j+1)*maxWidth + i], verts[(j+2)*maxWidth + i], next );
LerpVert( prev, next, mid );
for ( k = height - 1; k > j + 3; k-- ) {
verts[k*maxWidth + i] = verts[(k-2)*maxWidth + i];
}
verts[(j+1)*maxWidth + i] = prev;
verts[(j+2)*maxWidth + i] = mid;
verts[(j+3)*maxWidth + i] = next;
}
// back up and recheck this set again, it may need more subdivision
j -= 2;
}
PutOnCurve();
RemoveLinearColumnsRows();
Collapse();
// normalize all the lerped normals
if ( genNormals ) {
idVec3 tempNormal;
for ( i = 0; i < width * height; i++ ) {
tempNormal = verts[i].GetNormal();
tempNormal.Normalize();
verts[i].SetNormal( tempNormal );
}
}
GenerateIndexes();
}

View File

@@ -0,0 +1,146 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#ifndef __SURFACE_PATCH_H__
#define __SURFACE_PATCH_H__
/*
===============================================================================
Bezier patch surface.
===============================================================================
*/
class idSurface_Patch : public idSurface {
public:
idSurface_Patch();
idSurface_Patch( int maxPatchWidth, int maxPatchHeight );
idSurface_Patch( const idSurface_Patch &patch );
~idSurface_Patch();
void SetSize( int patchWidth, int patchHeight );
int GetWidth() const;
int GetHeight() const;
// subdivide the patch mesh based on error
void Subdivide( float maxHorizontalError, float maxVerticalError, float maxLength, bool genNormals = false );
// subdivide the patch up to an explicit number of horizontal and vertical subdivisions
void SubdivideExplicit( int horzSubdivisions, int vertSubdivisions, bool genNormals, bool removeLinear = false );
protected:
int width; // width of patch
int height; // height of patch
int maxWidth; // maximum width allocated for
int maxHeight; // maximum height allocated for
bool expanded; // true if vertices are spaced out
private:
// put the approximation points on the curve
void PutOnCurve();
// remove columns and rows with all points on one line
void RemoveLinearColumnsRows();
// resize verts buffer
void ResizeExpanded( int height, int width );
// space points out over maxWidth * maxHeight buffer
void Expand();
// move all points to the start of the verts buffer
void Collapse();
// project a point onto a vector to calculate maximum curve error
void ProjectPointOntoVector( const idVec3 &point, const idVec3 &vStart, const idVec3 &vEnd, idVec3 &vProj );
// generate normals
void GenerateNormals();
// generate triangle indexes
void GenerateIndexes();
// lerp point from two patch point
void LerpVert( const idDrawVert &a, const idDrawVert &b, idDrawVert &out ) const;
// sample a single 3x3 patch
void SampleSinglePatchPoint( const idDrawVert ctrl[3][3], float u, float v, idDrawVert *out ) const;
void SampleSinglePatch( const idDrawVert ctrl[3][3], int baseCol, int baseRow, int width, int horzSub, int vertSub, idDrawVert *outVerts ) const;
};
/*
=================
idSurface_Patch::idSurface_Patch
=================
*/
ID_INLINE idSurface_Patch::idSurface_Patch() {
height = width = maxHeight = maxWidth = 0;
expanded = false;
}
/*
=================
idSurface_Patch::idSurface_Patch
=================
*/
ID_INLINE idSurface_Patch::idSurface_Patch( int maxPatchWidth, int maxPatchHeight ) {
width = height = 0;
maxWidth = maxPatchWidth;
maxHeight = maxPatchHeight;
verts.SetNum( maxWidth * maxHeight );
expanded = false;
}
/*
=================
idSurface_Patch::idSurface_Patch
=================
*/
ID_INLINE idSurface_Patch::idSurface_Patch( const idSurface_Patch &patch ) {
(*this) = patch;
}
/*
=================
idSurface_Patch::~idSurface_Patch
=================
*/
ID_INLINE idSurface_Patch::~idSurface_Patch() {
}
/*
=================
idSurface_Patch::GetWidth
=================
*/
ID_INLINE int idSurface_Patch::GetWidth() const {
return width;
}
/*
=================
idSurface_Patch::GetHeight
=================
*/
ID_INLINE int idSurface_Patch::GetHeight() const {
return height;
}
#endif /* !__SURFACE_PATCH_H__ */

View File

@@ -0,0 +1,334 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#pragma hdrstop
#include "../precompiled.h"
#define POLYTOPE_VERTEX_EPSILON 0.1f
/*
====================
idSurface_Polytope::FromPlanes
====================
*/
void idSurface_Polytope::FromPlanes( const idPlane *planes, const int numPlanes ) {
int i, j, k, *windingVerts;
idFixedWinding w;
idDrawVert newVert;
windingVerts = (int *) _alloca( MAX_POINTS_ON_WINDING * sizeof( int ) );
memset( &newVert, 0, sizeof( newVert ) );
for ( i = 0; i < numPlanes; i++ ) {
w.BaseForPlane( planes[i] );
for ( j = 0; j < numPlanes; j++ ) {
if ( j == i ) {
continue;
}
if ( !w.ClipInPlace( -planes[j], ON_EPSILON, true ) ) {
break;
}
}
if ( !w.GetNumPoints() ) {
continue;
}
for ( j = 0; j < w.GetNumPoints(); j++ ) {
for ( k = 0; k < verts.Num(); k++ ) {
if ( verts[k].xyz.Compare( w[j].ToVec3(), POLYTOPE_VERTEX_EPSILON ) ) {
break;
}
}
if ( k >= verts.Num() ) {
newVert.xyz = w[j].ToVec3();
k = verts.Append( newVert );
}
windingVerts[j] = k;
}
for ( j = 2; j < w.GetNumPoints(); j++ ) {
indexes.Append( windingVerts[0] );
indexes.Append( windingVerts[j-1] );
indexes.Append( windingVerts[j] );
}
}
GenerateEdgeIndexes();
}
/*
====================
idSurface_Polytope::SetupTetrahedron
====================
*/
void idSurface_Polytope::SetupTetrahedron( const idBounds &bounds ) {
idVec3 center, scale;
float c1, c2, c3;
c1 = 0.4714045207f;
c2 = 0.8164965809f;
c3 = -0.3333333333f;
center = bounds.GetCenter();
scale = bounds[1] - center;
verts.SetNum( 4 );
verts[0].xyz = center + idVec3( 0.0f, 0.0f, scale.z );
verts[1].xyz = center + idVec3( 2.0f * c1 * scale.x, 0.0f, c3 * scale.z );
verts[2].xyz = center + idVec3( -c1 * scale.x, c2 * scale.y, c3 * scale.z );
verts[3].xyz = center + idVec3( -c1 * scale.x, -c2 * scale.y, c3 * scale.z );
indexes.SetNum( 4*3 );
indexes[0*3+0] = 0;
indexes[0*3+1] = 1;
indexes[0*3+2] = 2;
indexes[1*3+0] = 0;
indexes[1*3+1] = 2;
indexes[1*3+2] = 3;
indexes[2*3+0] = 0;
indexes[2*3+1] = 3;
indexes[2*3+2] = 1;
indexes[3*3+0] = 1;
indexes[3*3+1] = 3;
indexes[3*3+2] = 2;
GenerateEdgeIndexes();
}
/*
====================
idSurface_Polytope::SetupHexahedron
====================
*/
void idSurface_Polytope::SetupHexahedron( const idBounds &bounds ) {
idVec3 center, scale;
center = bounds.GetCenter();
scale = bounds[1] - center;
verts.SetNum( 8 );
verts[0].xyz = center + idVec3( -scale.x, -scale.y, -scale.z );
verts[1].xyz = center + idVec3( scale.x, -scale.y, -scale.z );
verts[2].xyz = center + idVec3( scale.x, scale.y, -scale.z );
verts[3].xyz = center + idVec3( -scale.x, scale.y, -scale.z );
verts[4].xyz = center + idVec3( -scale.x, -scale.y, scale.z );
verts[5].xyz = center + idVec3( scale.x, -scale.y, scale.z );
verts[6].xyz = center + idVec3( scale.x, scale.y, scale.z );
verts[7].xyz = center + idVec3( -scale.x, scale.y, scale.z );
indexes.SetNum( 12*3 );
indexes[ 0*3+0] = 0;
indexes[ 0*3+1] = 3;
indexes[ 0*3+2] = 2;
indexes[ 1*3+0] = 0;
indexes[ 1*3+1] = 2;
indexes[ 1*3+2] = 1;
indexes[ 2*3+0] = 0;
indexes[ 2*3+1] = 1;
indexes[ 2*3+2] = 5;
indexes[ 3*3+0] = 0;
indexes[ 3*3+1] = 5;
indexes[ 3*3+2] = 4;
indexes[ 4*3+0] = 0;
indexes[ 4*3+1] = 4;
indexes[ 4*3+2] = 7;
indexes[ 5*3+0] = 0;
indexes[ 5*3+1] = 7;
indexes[ 5*3+2] = 3;
indexes[ 6*3+0] = 6;
indexes[ 6*3+1] = 5;
indexes[ 6*3+2] = 1;
indexes[ 7*3+0] = 6;
indexes[ 7*3+1] = 1;
indexes[ 7*3+2] = 2;
indexes[ 8*3+0] = 6;
indexes[ 8*3+1] = 2;
indexes[ 8*3+2] = 3;
indexes[ 9*3+0] = 6;
indexes[ 9*3+1] = 3;
indexes[ 9*3+2] = 7;
indexes[10*3+0] = 6;
indexes[10*3+1] = 7;
indexes[10*3+2] = 4;
indexes[11*3+0] = 6;
indexes[11*3+1] = 4;
indexes[11*3+2] = 5;
GenerateEdgeIndexes();
}
/*
====================
idSurface_Polytope::SetupOctahedron
====================
*/
void idSurface_Polytope::SetupOctahedron( const idBounds &bounds ) {
idVec3 center, scale;
center = bounds.GetCenter();
scale = bounds[1] - center;
verts.SetNum( 6 );
verts[0].xyz = center + idVec3( scale.x, 0.0f, 0.0f );
verts[1].xyz = center + idVec3( -scale.x, 0.0f, 0.0f );
verts[2].xyz = center + idVec3( 0.0f, scale.y, 0.0f );
verts[3].xyz = center + idVec3( 0.0f, -scale.y, 0.0f );
verts[4].xyz = center + idVec3( 0.0f, 0.0f, scale.z );
verts[5].xyz = center + idVec3( 0.0f, 0.0f, -scale.z );
indexes.SetNum( 8*3 );
indexes[0*3+0] = 4;
indexes[0*3+1] = 0;
indexes[0*3+2] = 2;
indexes[1*3+0] = 4;
indexes[1*3+1] = 2;
indexes[1*3+2] = 1;
indexes[2*3+0] = 4;
indexes[2*3+1] = 1;
indexes[2*3+2] = 3;
indexes[3*3+0] = 4;
indexes[3*3+1] = 3;
indexes[3*3+2] = 0;
indexes[4*3+0] = 5;
indexes[4*3+1] = 2;
indexes[4*3+2] = 0;
indexes[5*3+0] = 5;
indexes[5*3+1] = 1;
indexes[5*3+2] = 2;
indexes[6*3+0] = 5;
indexes[6*3+1] = 3;
indexes[6*3+2] = 1;
indexes[7*3+0] = 5;
indexes[7*3+1] = 0;
indexes[7*3+2] = 3;
GenerateEdgeIndexes();
}
/*
====================
idSurface_Polytope::SetupDodecahedron
====================
*/
void idSurface_Polytope::SetupDodecahedron( const idBounds &bounds ) {
}
/*
====================
idSurface_Polytope::SetupIcosahedron
====================
*/
void idSurface_Polytope::SetupIcosahedron( const idBounds &bounds ) {
}
/*
====================
idSurface_Polytope::SetupCylinder
====================
*/
void idSurface_Polytope::SetupCylinder( const idBounds &bounds, const int numSides ) {
}
/*
====================
idSurface_Polytope::SetupCone
====================
*/
void idSurface_Polytope::SetupCone( const idBounds &bounds, const int numSides ) {
}
/*
====================
idSurface_Polytope::SplitPolytope
====================
*/
int idSurface_Polytope::SplitPolytope( const idPlane &plane, const float epsilon, idSurface_Polytope **front, idSurface_Polytope **back ) const {
int side, i, j, s, v0, v1, v2, edgeNum;
idSurface *surface[2];
idSurface_Polytope *polytopeSurfaces[2], *surf;
int *onPlaneEdges[2];
onPlaneEdges[0] = (int *) _alloca( indexes.Num() / 3 * sizeof( int ) );
onPlaneEdges[1] = (int *) _alloca( indexes.Num() / 3 * sizeof( int ) );
side = Split( plane, epsilon, &surface[0], &surface[1], onPlaneEdges[0], onPlaneEdges[1] );
*front = polytopeSurfaces[0] = new (TAG_IDLIB_SURFACE) idSurface_Polytope;
*back = polytopeSurfaces[1] = new (TAG_IDLIB_SURFACE) idSurface_Polytope;
for ( s = 0; s < 2; s++ ) {
if ( surface[s] ) {
polytopeSurfaces[s] = new idSurface_Polytope( *surface[s] );
delete surface[s];
surface[s] = NULL;
}
}
*front = polytopeSurfaces[0];
*back = polytopeSurfaces[1];
if ( side != SIDE_CROSS ) {
return side;
}
// add triangles to close off the front and back polytope
for ( s = 0; s < 2; s++ ) {
surf = polytopeSurfaces[s];
edgeNum = surf->edgeIndexes[onPlaneEdges[s][0]];
v0 = surf->edges[abs(edgeNum)].verts[INT32_SIGNBITSET(edgeNum)];
v1 = surf->edges[abs(edgeNum)].verts[INT32_SIGNBITNOTSET(edgeNum)];
for ( i = 1; onPlaneEdges[s][i] >= 0; i++ ) {
for ( j = i+1; onPlaneEdges[s][j] >= 0; j++ ) {
edgeNum = surf->edgeIndexes[onPlaneEdges[s][j]];
if ( v1 == surf->edges[abs(edgeNum)].verts[INT32_SIGNBITSET(edgeNum)] ) {
v1 = surf->edges[abs(edgeNum)].verts[INT32_SIGNBITNOTSET(edgeNum)];
SwapValues( onPlaneEdges[s][i], onPlaneEdges[s][j] );
break;
}
}
}
for ( i = 2; onPlaneEdges[s][i] >= 0; i++ ) {
edgeNum = surf->edgeIndexes[onPlaneEdges[s][i]];
v1 = surf->edges[abs(edgeNum)].verts[INT32_SIGNBITNOTSET(edgeNum)];
v2 = surf->edges[abs(edgeNum)].verts[INT32_SIGNBITSET(edgeNum)];
surf->indexes.Append( v0 );
surf->indexes.Append( v1 );
surf->indexes.Append( v2 );
}
surf->GenerateEdgeIndexes();
}
return side;
}

View File

@@ -0,0 +1,71 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#ifndef __SURFACE_POLYTOPE_H__
#define __SURFACE_POLYTOPE_H__
/*
===============================================================================
Polytope surface.
NOTE: vertexes are not duplicated for texture coordinates.
===============================================================================
*/
class idSurface_Polytope : public idSurface {
public:
idSurface_Polytope();
explicit idSurface_Polytope( const idSurface &surface ) : idSurface( surface ) {}
void FromPlanes( const idPlane *planes, const int numPlanes );
void SetupTetrahedron( const idBounds &bounds );
void SetupHexahedron( const idBounds &bounds );
void SetupOctahedron( const idBounds &bounds );
void SetupDodecahedron( const idBounds &bounds );
void SetupIcosahedron( const idBounds &bounds );
void SetupCylinder( const idBounds &bounds, const int numSides );
void SetupCone( const idBounds &bounds, const int numSides );
int SplitPolytope( const idPlane &plane, const float epsilon, idSurface_Polytope **front, idSurface_Polytope **back ) const;
protected:
};
/*
====================
idSurface_Polytope::idSurface_Polytope
====================
*/
ID_INLINE idSurface_Polytope::idSurface_Polytope() {
}
#endif /* !__SURFACE_POLYTOPE_H__ */

View File

@@ -0,0 +1,223 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#pragma hdrstop
#include "../precompiled.h"
/*
====================
idSurface_SweptSpline::SetSpline
====================
*/
void idSurface_SweptSpline::SetSpline( idCurve_Spline<idVec4> *spline ) {
if ( this->spline ) {
delete this->spline;
}
this->spline = spline;
}
/*
====================
idSurface_SweptSpline::SetSweptSpline
====================
*/
void idSurface_SweptSpline::SetSweptSpline( idCurve_Spline<idVec4> *sweptSpline ) {
if ( this->sweptSpline ) {
delete this->sweptSpline;
}
this->sweptSpline = sweptSpline;
}
/*
====================
idSurface_SweptSpline::SetSweptCircle
Sets the swept spline to a NURBS circle.
====================
*/
void idSurface_SweptSpline::SetSweptCircle( const float radius ) {
idCurve_NURBS<idVec4> *nurbs = new (TAG_IDLIB_SURFACE) idCurve_NURBS<idVec4>();
nurbs->Clear();
nurbs->AddValue( 0.0f, idVec4( radius, radius, 0.0f, 0.00f ) );
nurbs->AddValue( 100.0f, idVec4( -radius, radius, 0.0f, 0.25f ) );
nurbs->AddValue( 200.0f, idVec4( -radius, -radius, 0.0f, 0.50f ) );
nurbs->AddValue( 300.0f, idVec4( radius, -radius, 0.0f, 0.75f ) );
nurbs->SetBoundaryType( idCurve_NURBS<idVec4>::BT_CLOSED );
nurbs->SetCloseTime( 100.0f );
if ( sweptSpline ) {
delete sweptSpline;
}
sweptSpline = nurbs;
}
/*
====================
idSurface_SweptSpline::GetFrame
====================
*/
void idSurface_SweptSpline::GetFrame( const idMat3 &previousFrame, const idVec3 dir, idMat3 &newFrame ) {
float wx, wy, wz;
float xx, yy, yz;
float xy, xz, zz;
float x2, y2, z2;
float a, c, s, x, y, z;
idVec3 d, v;
idMat3 axis;
d = dir;
d.Normalize();
v = d.Cross( previousFrame[2] );
v.Normalize();
a = idMath::ACos( previousFrame[2] * d ) * 0.5f;
c = idMath::Cos( a );
s = idMath::Sqrt( 1.0f - c * c );
x = v[0] * s;
y = v[1] * s;
z = v[2] * s;
x2 = x + x;
y2 = y + y;
z2 = z + z;
xx = x * x2;
xy = x * y2;
xz = x * z2;
yy = y * y2;
yz = y * z2;
zz = z * z2;
wx = c * x2;
wy = c * y2;
wz = c * z2;
axis[0][0] = 1.0f - ( yy + zz );
axis[0][1] = xy - wz;
axis[0][2] = xz + wy;
axis[1][0] = xy + wz;
axis[1][1] = 1.0f - ( xx + zz );
axis[1][2] = yz - wx;
axis[2][0] = xz - wy;
axis[2][1] = yz + wx;
axis[2][2] = 1.0f - ( xx + yy );
newFrame = previousFrame * axis;
newFrame[2] = dir;
newFrame[2].Normalize();
newFrame[1].Cross( newFrame[ 2 ], newFrame[ 0 ] );
newFrame[1].Normalize();
newFrame[0].Cross( newFrame[ 1 ], newFrame[ 2 ] );
newFrame[0].Normalize();
}
/*
====================
idSurface_SweptSpline::Tessellate
tesselate the surface
====================
*/
void idSurface_SweptSpline::Tessellate( const int splineSubdivisions, const int sweptSplineSubdivisions ) {
int i, j, offset, baseOffset, splineDiv, sweptSplineDiv;
int i0, i1, j0, j1;
float totalTime, t;
idVec4 splinePos, splineD1;
idMat3 splineMat;
if ( !spline || !sweptSpline ) {
idSurface::Clear();
return;
}
verts.SetNum( splineSubdivisions * sweptSplineSubdivisions );
// calculate the points and first derivatives for the swept spline
totalTime = sweptSpline->GetTime( sweptSpline->GetNumValues() - 1 ) - sweptSpline->GetTime( 0 ) + sweptSpline->GetCloseTime();
sweptSplineDiv = sweptSpline->GetBoundaryType() == idCurve_Spline<idVec3>::BT_CLOSED ? sweptSplineSubdivisions : sweptSplineSubdivisions - 1;
baseOffset = (splineSubdivisions-1) * sweptSplineSubdivisions;
for ( i = 0; i < sweptSplineSubdivisions; i++ ) {
t = totalTime * i / sweptSplineDiv;
splinePos = sweptSpline->GetCurrentValue( t );
splineD1 = sweptSpline->GetCurrentFirstDerivative( t );
verts[baseOffset+i].xyz = splinePos.ToVec3();
verts[baseOffset+i].SetTexCoordS( splinePos.w );
verts[baseOffset+i].SetTangent( splineD1.ToVec3() );
}
// sweep the spline
totalTime = spline->GetTime( spline->GetNumValues() - 1 ) - spline->GetTime( 0 ) + spline->GetCloseTime();
splineDiv = spline->GetBoundaryType() == idCurve_Spline<idVec3>::BT_CLOSED ? splineSubdivisions : splineSubdivisions - 1;
splineMat.Identity();
idVec3 tempNormal;
for ( i = 0; i < splineSubdivisions; i++ ) {
t = totalTime * i / splineDiv;
splinePos = spline->GetCurrentValue( t );
splineD1 = spline->GetCurrentFirstDerivative( t );
GetFrame( splineMat, splineD1.ToVec3(), splineMat );
offset = i * sweptSplineSubdivisions;
for ( j = 0; j < sweptSplineSubdivisions; j++ ) {
idDrawVert *v = &verts[offset+j];
v->xyz = splinePos.ToVec3() + verts[baseOffset+j].xyz * splineMat;
v->SetTexCoord( verts[baseOffset+j].GetTexCoord().x, splinePos.w );
v->SetTangent( verts[baseOffset+j].GetTangent() * splineMat );
v->SetBiTangent( splineD1.ToVec3() );
tempNormal = v->GetBiTangent().Cross( v->GetTangent() );
tempNormal.Normalize();
v->SetNormal( tempNormal );
v->color[0] = v->color[1] = v->color[2] = v->color[3] = 0;
}
}
indexes.SetNum( splineDiv * sweptSplineDiv * 2 * 3 );
// create indexes for the triangles
for ( offset = i = 0; i < splineDiv; i++ ) {
i0 = (i+0) * sweptSplineSubdivisions;
i1 = (i+1) % splineSubdivisions * sweptSplineSubdivisions;
for ( j = 0; j < sweptSplineDiv; j++ ) {
j0 = (j+0);
j1 = (j+1) % sweptSplineSubdivisions;
indexes[offset++] = i0 + j0;
indexes[offset++] = i0 + j1;
indexes[offset++] = i1 + j1;
indexes[offset++] = i1 + j1;
indexes[offset++] = i1 + j0;
indexes[offset++] = i0 + j0;
}
}
GenerateEdgeIndexes();
}

View File

@@ -0,0 +1,93 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#ifndef __SURFACE_SWEPTSPLINE_H__
#define __SURFACE_SWEPTSPLINE_H__
/*
===============================================================================
Swept Spline surface.
===============================================================================
*/
class idSurface_SweptSpline : public idSurface {
public:
idSurface_SweptSpline();
~idSurface_SweptSpline();
void SetSpline( idCurve_Spline<idVec4> *spline );
void SetSweptSpline( idCurve_Spline<idVec4> *sweptSpline );
void SetSweptCircle( const float radius );
void Tessellate( const int splineSubdivisions, const int sweptSplineSubdivisions );
void Clear();
protected:
idCurve_Spline<idVec4> *spline;
idCurve_Spline<idVec4> *sweptSpline;
void GetFrame( const idMat3 &previousFrame, const idVec3 dir, idMat3 &newFrame );
};
/*
====================
idSurface_SweptSpline::idSurface_SweptSpline
====================
*/
ID_INLINE idSurface_SweptSpline::idSurface_SweptSpline() {
spline = NULL;
sweptSpline = NULL;
}
/*
====================
idSurface_SweptSpline::~idSurface_SweptSpline
====================
*/
ID_INLINE idSurface_SweptSpline::~idSurface_SweptSpline() {
delete spline;
delete sweptSpline;
}
/*
====================
idSurface_SweptSpline::Clear
====================
*/
ID_INLINE void idSurface_SweptSpline::Clear() {
idSurface::Clear();
delete spline;
spline = NULL;
delete sweptSpline;
sweptSpline = NULL;
}
#endif /* !__SURFACE_SWEPTSPLINE_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,189 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#ifndef __TRACEMODEL_H__
#define __TRACEMODEL_H__
/*
===============================================================================
A trace model is an arbitrary polygonal model which is used by the
collision detection system to find collisions, contacts or the contents
of a volume. For collision detection speed reasons the number of vertices
and edges are limited. The trace model can have any shape. However convex
models are usually preferred.
===============================================================================
*/
class idVec3;
class idMat3;
class idBounds;
// trace model type
typedef enum {
TRM_INVALID, // invalid trm
TRM_BOX, // box
TRM_OCTAHEDRON, // octahedron
TRM_DODECAHEDRON, // dodecahedron
TRM_CYLINDER, // cylinder approximation
TRM_CONE, // cone approximation
TRM_BONE, // two tetrahedrons attached to each other
TRM_POLYGON, // arbitrary convex polygon
TRM_POLYGONVOLUME, // volume for arbitrary convex polygon
TRM_CUSTOM // loaded from map model or ASE/LWO
} traceModel_t;
// these are bit cache limits
#define MAX_TRACEMODEL_VERTS 32
#define MAX_TRACEMODEL_EDGES 32
#define MAX_TRACEMODEL_POLYS 16
#define MAX_TRACEMODEL_POLYEDGES 16
typedef idVec3 traceModelVert_t;
typedef struct {
int v[2];
idVec3 normal;
} traceModelEdge_t;
typedef struct {
idVec3 normal;
float dist;
idBounds bounds;
int numEdges;
int edges[MAX_TRACEMODEL_POLYEDGES];
} traceModelPoly_t;
class idTraceModel {
public:
traceModel_t type;
int numVerts;
traceModelVert_t verts[MAX_TRACEMODEL_VERTS];
int numEdges;
traceModelEdge_t edges[MAX_TRACEMODEL_EDGES+1];
int numPolys;
traceModelPoly_t polys[MAX_TRACEMODEL_POLYS];
idVec3 offset; // offset to center of model
idBounds bounds; // bounds of model
bool isConvex; // true when model is convex
public:
idTraceModel();
// axial bounding box
idTraceModel( const idBounds &boxBounds );
// cylinder approximation
idTraceModel( const idBounds &cylBounds, const int numSides );
// bone
idTraceModel( const float length, const float width );
// axial box
void SetupBox( const idBounds &boxBounds );
void SetupBox( const float size );
// octahedron
void SetupOctahedron( const idBounds &octBounds );
void SetupOctahedron( const float size );
// dodecahedron
void SetupDodecahedron( const idBounds &dodBounds );
void SetupDodecahedron( const float size );
// cylinder approximation
void SetupCylinder( const idBounds &cylBounds, const int numSides );
void SetupCylinder( const float height, const float width, const int numSides );
// cone approximation
void SetupCone( const idBounds &coneBounds, const int numSides );
void SetupCone( const float height, const float width, const int numSides );
// two tetrahedrons attached to each other
void SetupBone( const float length, const float width );
// arbitrary convex polygon
void SetupPolygon( const idVec3 *v, const int count );
void SetupPolygon( const idWinding &w );
// generate edge normals
int GenerateEdgeNormals();
// translate the trm
void Translate( const idVec3 &translation );
// rotate the trm
void Rotate( const idMat3 &rotation );
// shrink the model m units on all sides
void Shrink( const float m );
// compare
bool Compare( const idTraceModel &trm ) const;
bool operator==( const idTraceModel &trm ) const;
bool operator!=( const idTraceModel &trm ) const;
// get the area of one of the polygons
float GetPolygonArea( int polyNum ) const;
// get the silhouette edges
int GetProjectionSilhouetteEdges( const idVec3 &projectionOrigin, int silEdges[MAX_TRACEMODEL_EDGES] ) const;
int GetParallelProjectionSilhouetteEdges( const idVec3 &projectionDir, int silEdges[MAX_TRACEMODEL_EDGES] ) const;
// calculate mass properties assuming an uniform density
void GetMassProperties( const float density, float &mass, idVec3 &centerOfMass, idMat3 &inertiaTensor ) const;
private:
void InitBox();
void InitOctahedron();
void InitDodecahedron();
void InitBone();
void ProjectionIntegrals( int polyNum, int a, int b, struct projectionIntegrals_s &integrals ) const;
void PolygonIntegrals( int polyNum, int a, int b, int c, struct polygonIntegrals_s &integrals ) const;
void VolumeIntegrals( struct volumeIntegrals_s &integrals ) const;
void VolumeFromPolygon( idTraceModel &trm, float thickness ) const;
int GetOrderedSilhouetteEdges( const int edgeIsSilEdge[MAX_TRACEMODEL_EDGES+1], int silEdges[MAX_TRACEMODEL_EDGES] ) const;
};
ID_INLINE idTraceModel::idTraceModel() {
type = TRM_INVALID;
numVerts = numEdges = numPolys = 0;
bounds.Zero();
}
ID_INLINE idTraceModel::idTraceModel( const idBounds &boxBounds ) {
InitBox();
SetupBox( boxBounds );
}
ID_INLINE idTraceModel::idTraceModel( const idBounds &cylBounds, const int numSides ) {
SetupCylinder( cylBounds, numSides );
}
ID_INLINE idTraceModel::idTraceModel( const float length, const float width ) {
InitBone();
SetupBone( length, width );
}
ID_INLINE bool idTraceModel::operator==( const idTraceModel &trm ) const {
return Compare( trm );
}
ID_INLINE bool idTraceModel::operator!=( const idTraceModel &trm ) const {
return !Compare( trm );
}
#endif /* !__TRACEMODEL_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,401 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#ifndef __WINDING_H__
#define __WINDING_H__
/*
===============================================================================
A winding is an arbitrary convex polygon defined by an array of points.
===============================================================================
*/
class idWinding {
public:
idWinding();
explicit idWinding( const int n ); // allocate for n points
explicit idWinding( const idVec3 *verts, const int n ); // winding from points
explicit idWinding( const idVec3 &normal, const float dist ); // base winding for plane
explicit idWinding( const idPlane &plane ); // base winding for plane
explicit idWinding( const idWinding &winding );
virtual ~idWinding();
idWinding & operator=( const idWinding &winding );
const idVec5 & operator[]( const int index ) const;
idVec5 & operator[]( const int index );
// add a point to the end of the winding point array
idWinding & operator+=( const idVec3 &v );
idWinding & operator+=( const idVec5 &v );
void AddPoint( const idVec3 &v );
void AddPoint( const idVec5 &v );
// number of points on winding
int GetNumPoints() const;
void SetNumPoints( int n );
virtual void Clear();
// huge winding for plane, the points go counter clockwise when facing the front of the plane
void BaseForPlane( const idVec3 &normal, const float dist );
void BaseForPlane( const idPlane &plane );
// splits the winding into a front and back winding, the winding itself stays unchanged
// returns a SIDE_?
int Split( const idPlane &plane, const float epsilon, idWinding **front, idWinding **back ) const;
// returns the winding fragment at the front of the clipping plane,
// if there is nothing at the front the winding itself is destroyed and NULL is returned
idWinding * Clip( const idPlane &plane, const float epsilon = ON_EPSILON, const bool keepOn = false );
// cuts off the part at the back side of the plane, returns true if some part was at the front
// if there is nothing at the front the number of points is set to zero
bool ClipInPlace( const idPlane &plane, const float epsilon = ON_EPSILON, const bool keepOn = false );
// returns a copy of the winding
idWinding * Copy() const;
idWinding * Reverse() const;
void ReverseSelf();
void RemoveEqualPoints( const float epsilon = ON_EPSILON );
void RemoveColinearPoints( const idVec3 &normal, const float epsilon = ON_EPSILON );
void RemovePoint( int point );
void InsertPoint( const idVec5 &point, int spot );
bool InsertPointIfOnEdge( const idVec5 &point, const idPlane &plane, const float epsilon = ON_EPSILON );
// add a winding to the convex hull
void AddToConvexHull( const idWinding *winding, const idVec3 &normal, const float epsilon = ON_EPSILON );
// add a point to the convex hull
void AddToConvexHull( const idVec3 &point, const idVec3 &normal, const float epsilon = ON_EPSILON );
// tries to merge 'this' with the given winding, returns NULL if merge fails, both 'this' and 'w' stay intact
// 'keep' tells if the contacting points should stay even if they create colinear edges
idWinding * TryMerge( const idWinding &w, const idVec3 &normal, int keep = false ) const;
// check whether the winding is valid or not
bool Check( bool print = true ) const;
float GetArea() const;
idVec3 GetCenter() const;
float GetRadius( const idVec3 &center ) const;
void GetPlane( idVec3 &normal, float &dist ) const;
void GetPlane( idPlane &plane ) const;
void GetBounds( idBounds &bounds ) const;
bool IsTiny() const;
bool IsHuge() const; // base winding for a plane is typically huge
void Print() const;
float PlaneDistance( const idPlane &plane ) const;
int PlaneSide( const idPlane &plane, const float epsilon = ON_EPSILON ) const;
bool PlanesConcave( const idWinding &w2, const idVec3 &normal1, const idVec3 &normal2, float dist1, float dist2 ) const;
bool PointInside( const idVec3 &normal, const idVec3 &point, const float epsilon ) const;
// returns true if the line or ray intersects the winding
bool LineIntersection( const idPlane &windingPlane, const idVec3 &start, const idVec3 &end, bool backFaceCull = false ) const;
// intersection point is start + dir * scale
bool RayIntersection( const idPlane &windingPlane, const idVec3 &start, const idVec3 &dir, float &scale, bool backFaceCull = false ) const;
static float TriangleArea( const idVec3 &a, const idVec3 &b, const idVec3 &c );
protected:
int numPoints; // number of points
idVec5 * p; // pointer to point data
int allocedSize;
bool EnsureAlloced( int n, bool keep = false );
virtual bool ReAllocate( int n, bool keep = false );
};
ID_INLINE idWinding::idWinding() {
numPoints = allocedSize = 0;
p = NULL;
}
ID_INLINE idWinding::idWinding( int n ) {
numPoints = allocedSize = 0;
p = NULL;
EnsureAlloced( n );
}
ID_INLINE idWinding::idWinding( const idVec3 *verts, const int n ) {
int i;
numPoints = allocedSize = 0;
p = NULL;
if ( !EnsureAlloced( n ) ) {
numPoints = 0;
return;
}
for ( i = 0; i < n; i++ ) {
p[i].ToVec3() = verts[i];
p[i].s = p[i].t = 0.0f;
}
numPoints = n;
}
ID_INLINE idWinding::idWinding( const idVec3 &normal, const float dist ) {
numPoints = allocedSize = 0;
p = NULL;
BaseForPlane( normal, dist );
}
ID_INLINE idWinding::idWinding( const idPlane &plane ) {
numPoints = allocedSize = 0;
p = NULL;
BaseForPlane( plane );
}
ID_INLINE idWinding::idWinding( const idWinding &winding ) {
int i;
if ( !EnsureAlloced( winding.GetNumPoints() ) ) {
numPoints = 0;
return;
}
for ( i = 0; i < winding.GetNumPoints(); i++ ) {
p[i] = winding[i];
}
numPoints = winding.GetNumPoints();
}
ID_INLINE idWinding::~idWinding() {
delete[] p;
p = NULL;
}
ID_INLINE idWinding &idWinding::operator=( const idWinding &winding ) {
int i;
if ( !EnsureAlloced( winding.numPoints ) ) {
numPoints = 0;
return *this;
}
for ( i = 0; i < winding.numPoints; i++ ) {
p[i] = winding.p[i];
}
numPoints = winding.numPoints;
return *this;
}
ID_INLINE const idVec5 &idWinding::operator[]( const int index ) const {
//assert( index >= 0 && index < numPoints );
return p[ index ];
}
ID_INLINE idVec5 &idWinding::operator[]( const int index ) {
//assert( index >= 0 && index < numPoints );
return p[ index ];
}
ID_INLINE idWinding &idWinding::operator+=( const idVec3 &v ) {
AddPoint( v );
return *this;
}
ID_INLINE idWinding &idWinding::operator+=( const idVec5 &v ) {
AddPoint( v );
return *this;
}
ID_INLINE void idWinding::AddPoint( const idVec3 &v ) {
if ( !EnsureAlloced(numPoints+1, true) ) {
return;
}
p[numPoints] = v;
numPoints++;
}
ID_INLINE void idWinding::AddPoint( const idVec5 &v ) {
if ( !EnsureAlloced(numPoints+1, true) ) {
return;
}
p[numPoints] = v;
numPoints++;
}
ID_INLINE int idWinding::GetNumPoints() const {
return numPoints;
}
ID_INLINE void idWinding::SetNumPoints( int n ) {
if ( !EnsureAlloced( n, true ) ) {
return;
}
numPoints = n;
}
ID_INLINE void idWinding::Clear() {
numPoints = 0;
delete[] p;
p = NULL;
}
ID_INLINE void idWinding::BaseForPlane( const idPlane &plane ) {
BaseForPlane( plane.Normal(), plane.Dist() );
}
ID_INLINE bool idWinding::EnsureAlloced( int n, bool keep ) {
if ( n > allocedSize ) {
return ReAllocate( n, keep );
}
return true;
}
/*
===============================================================================
idFixedWinding is a fixed buffer size winding not using
memory allocations.
When an operation would overflow the fixed buffer a warning
is printed and the operation is safely cancelled.
===============================================================================
*/
#define MAX_POINTS_ON_WINDING 64
class idFixedWinding : public idWinding {
public:
idFixedWinding();
explicit idFixedWinding( const int n );
explicit idFixedWinding( const idVec3 *verts, const int n );
explicit idFixedWinding( const idVec3 &normal, const float dist );
explicit idFixedWinding( const idPlane &plane );
explicit idFixedWinding( const idWinding &winding );
explicit idFixedWinding( const idFixedWinding &winding );
virtual ~idFixedWinding();
idFixedWinding &operator=( const idWinding &winding );
virtual void Clear();
// splits the winding in a back and front part, 'this' becomes the front part
// returns a SIDE_?
int Split( idFixedWinding *back, const idPlane &plane, const float epsilon = ON_EPSILON );
protected:
idVec5 data[MAX_POINTS_ON_WINDING]; // point data
virtual bool ReAllocate( int n, bool keep = false );
};
ID_INLINE idFixedWinding::idFixedWinding() {
numPoints = 0;
p = data;
allocedSize = MAX_POINTS_ON_WINDING;
}
ID_INLINE idFixedWinding::idFixedWinding( int n ) {
numPoints = 0;
p = data;
allocedSize = MAX_POINTS_ON_WINDING;
}
ID_INLINE idFixedWinding::idFixedWinding( const idVec3 *verts, const int n ) {
int i;
numPoints = 0;
p = data;
allocedSize = MAX_POINTS_ON_WINDING;
if ( !EnsureAlloced( n ) ) {
numPoints = 0;
return;
}
for ( i = 0; i < n; i++ ) {
p[i].ToVec3() = verts[i];
p[i].s = p[i].t = 0;
}
numPoints = n;
}
ID_INLINE idFixedWinding::idFixedWinding( const idVec3 &normal, const float dist ) {
numPoints = 0;
p = data;
allocedSize = MAX_POINTS_ON_WINDING;
BaseForPlane( normal, dist );
}
ID_INLINE idFixedWinding::idFixedWinding( const idPlane &plane ) {
numPoints = 0;
p = data;
allocedSize = MAX_POINTS_ON_WINDING;
BaseForPlane( plane );
}
ID_INLINE idFixedWinding::idFixedWinding( const idWinding &winding ) {
int i;
p = data;
allocedSize = MAX_POINTS_ON_WINDING;
if ( !EnsureAlloced( winding.GetNumPoints() ) ) {
numPoints = 0;
return;
}
for ( i = 0; i < winding.GetNumPoints(); i++ ) {
p[i] = winding[i];
}
numPoints = winding.GetNumPoints();
}
ID_INLINE idFixedWinding::idFixedWinding( const idFixedWinding &winding ) {
int i;
p = data;
allocedSize = MAX_POINTS_ON_WINDING;
if ( !EnsureAlloced( winding.GetNumPoints() ) ) {
numPoints = 0;
return;
}
for ( i = 0; i < winding.GetNumPoints(); i++ ) {
p[i] = winding[i];
}
numPoints = winding.GetNumPoints();
}
ID_INLINE idFixedWinding::~idFixedWinding() {
p = NULL; // otherwise it tries to free the fixed buffer
}
ID_INLINE idFixedWinding &idFixedWinding::operator=( const idWinding &winding ) {
int i;
if ( !EnsureAlloced( winding.GetNumPoints() ) ) {
numPoints = 0;
return *this;
}
for ( i = 0; i < winding.GetNumPoints(); i++ ) {
p[i] = winding[i];
}
numPoints = winding.GetNumPoints();
return *this;
}
ID_INLINE void idFixedWinding::Clear() {
numPoints = 0;
}
#endif /* !__WINDING_H__ */

View File

@@ -0,0 +1,753 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#pragma hdrstop
#include "../precompiled.h"
#include "Winding2D.h"
/*
============
GetAxialBevel
============
*/
bool GetAxialBevel( const idVec3 &plane1, const idVec3 &plane2, const idVec2 &point, idVec3 &bevel ) {
if ( IEEE_FLT_SIGNBITSET( plane1.x ) ^ IEEE_FLT_SIGNBITSET( plane2.x ) ) {
if ( idMath::Fabs( plane1.x ) > 0.1f && idMath::Fabs( plane2.x ) > 0.1f ) {
bevel.x = 0.0f;
if ( IEEE_FLT_SIGNBITSET( plane1.y ) ) {
bevel.y = -1.0f;
}
else {
bevel.y = 1.0f;
}
bevel.z = - ( point.x * bevel.x + point.y * bevel.y );
return true;
}
}
if ( IEEE_FLT_SIGNBITSET( plane1.y ) ^ IEEE_FLT_SIGNBITSET( plane2.y ) ) {
if ( idMath::Fabs( plane1.y ) > 0.1f && idMath::Fabs( plane2.y ) > 0.1f ) {
bevel.y = 0.0f;
if ( IEEE_FLT_SIGNBITSET( plane1.x ) ) {
bevel.x = -1.0f;
}
else {
bevel.x = 1.0f;
}
bevel.z = - ( point.x * bevel.x + point.y * bevel.y );
return true;
}
}
return false;
}
/*
============
idWinding2D::ExpandForAxialBox
============
*/
void idWinding2D::ExpandForAxialBox( const idVec2 bounds[2] ) {
int i, j, numPlanes;
idVec2 v;
idVec3 planes[MAX_POINTS_ON_WINDING_2D], plane, bevel;
// get planes for the edges and add bevels
for ( numPlanes = i = 0; i < numPoints; i++ ) {
j = (i+1) % numPoints;
if ( ( p[j] - p[i] ).LengthSqr() < 0.01f ) {
continue;
}
plane = Plane2DFromPoints( p[i], p[j], true );
if ( i ) {
if ( GetAxialBevel( planes[numPlanes-1], plane, p[i], bevel ) ) {
planes[numPlanes++] = bevel;
}
}
assert( numPlanes < MAX_POINTS_ON_WINDING_2D );
planes[numPlanes++] = plane;
}
assert( numPlanes < MAX_POINTS_ON_WINDING_2D && numPlanes > 0 );
if ( GetAxialBevel( planes[numPlanes-1], planes[0], p[0], bevel ) ) {
planes[numPlanes++] = bevel;
}
// expand the planes
for ( i = 0; i < numPlanes; i++ ) {
v.x = bounds[ IEEE_FLT_SIGNBITSET( planes[i].x ) ].x;
v.y = bounds[ IEEE_FLT_SIGNBITSET( planes[i].y ) ].y;
planes[i].z += v.x * planes[i].x + v.y * planes[i].y;
}
// get intersection points of the planes
for ( numPoints = i = 0; i < numPlanes; i++ ) {
if ( Plane2DIntersection( planes[(i+numPlanes-1) % numPlanes], planes[i], p[numPoints] ) ) {
numPoints++;
}
}
}
/*
============
idWinding2D::Expand
============
*/
void idWinding2D::Expand( const float d ) {
int i;
idVec2 edgeNormals[MAX_POINTS_ON_WINDING_2D];
for ( i = 0; i < numPoints; i++ ) {
idVec2 &start = p[i];
idVec2 &end = p[(i+1)%numPoints];
edgeNormals[i].x = start.y - end.y;
edgeNormals[i].y = end.x - start.x;
edgeNormals[i].Normalize();
edgeNormals[i] *= d;
}
for ( i = 0; i < numPoints; i++ ) {
p[i] += edgeNormals[i] + edgeNormals[(i+numPoints-1)%numPoints];
}
}
/*
=============
idWinding2D::Split
=============
*/
int idWinding2D::Split( const idVec3 &plane, const float epsilon, idWinding2D **front, idWinding2D **back ) const {
float dists[MAX_POINTS_ON_WINDING_2D];
byte sides[MAX_POINTS_ON_WINDING_2D];
int counts[3];
float dot;
int i, j;
const idVec2 * p1, *p2;
idVec2 mid;
idWinding2D * f;
idWinding2D * b;
int maxpts;
counts[0] = counts[1] = counts[2] = 0;
// determine sides for each point
for ( i = 0; i < numPoints; i++ ) {
dists[i] = dot = plane.x * p[i].x + plane.y * p[i].y + plane.z;
if ( dot > epsilon ) {
sides[i] = SIDE_FRONT;
} else if ( dot < -epsilon ) {
sides[i] = SIDE_BACK;
} else {
sides[i] = SIDE_ON;
}
counts[sides[i]]++;
}
sides[i] = sides[0];
dists[i] = dists[0];
*front = *back = NULL;
// if nothing at the front of the clipping plane
if ( !counts[SIDE_FRONT] ) {
*back = Copy();
return SIDE_BACK;
}
// if nothing at the back of the clipping plane
if ( !counts[SIDE_BACK] ) {
*front = Copy();
return SIDE_FRONT;
}
maxpts = numPoints+4; // cant use counts[0]+2 because of fp grouping errors
*front = f = new (TAG_IDLIB_WINDING) idWinding2D;
*back = b = new (TAG_IDLIB_WINDING) idWinding2D;
for ( i = 0; i < numPoints; i++ ) {
p1 = &p[i];
if ( sides[i] == SIDE_ON ) {
f->p[f->numPoints] = *p1;
f->numPoints++;
b->p[b->numPoints] = *p1;
b->numPoints++;
continue;
}
if ( sides[i] == SIDE_FRONT ) {
f->p[f->numPoints] = *p1;
f->numPoints++;
}
if ( sides[i] == SIDE_BACK ) {
b->p[b->numPoints] = *p1;
b->numPoints++;
}
if ( sides[i+1] == SIDE_ON || sides[i+1] == sides[i] ) {
continue;
}
// generate a split point
p2 = &p[(i+1)%numPoints];
// always calculate the split going from the same side
// or minor epsilon issues can happen
if ( sides[i] == SIDE_FRONT ) {
dot = dists[i] / ( dists[i] - dists[i+1] );
for ( j = 0; j < 2; j++ ) {
// avoid round off error when possible
if ( plane[j] == 1.0f ) {
mid[j] = plane.z;
} else if ( plane[j] == -1.0f ) {
mid[j] = -plane.z;
} else {
mid[j] = (*p1)[j] + dot * ((*p2)[j] - (*p1)[j]);
}
}
} else {
dot = dists[i+1] / ( dists[i+1] - dists[i] );
for ( j = 0; j < 2; j++ ) {
// avoid round off error when possible
if ( plane[j] == 1.0f ) {
mid[j] = plane.z;
} else if ( plane[j] == -1.0f ) {
mid[j] = -plane.z;
} else {
mid[j] = (*p2)[j] + dot * ( (*p1)[j] - (*p2)[j] );
}
}
}
f->p[f->numPoints] = mid;
f->numPoints++;
b->p[b->numPoints] = mid;
b->numPoints++;
}
return SIDE_CROSS;
}
/*
============
idWinding2D::ClipInPlace
============
*/
bool idWinding2D::ClipInPlace( const idVec3 &plane, const float epsilon, const bool keepOn ) {
int i, j, maxpts, newNumPoints;
int sides[MAX_POINTS_ON_WINDING_2D+1], counts[3];
float dot, dists[MAX_POINTS_ON_WINDING_2D+1];
idVec2 *p1, *p2, mid, newPoints[MAX_POINTS_ON_WINDING_2D+4];
counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
for ( i = 0; i < numPoints; i++ ) {
dists[i] = dot = plane.x * p[i].x + plane.y * p[i].y + plane.z;
if ( dot > epsilon ) {
sides[i] = SIDE_FRONT;
} else if ( dot < -epsilon ) {
sides[i] = SIDE_BACK;
} else {
sides[i] = SIDE_ON;
}
counts[sides[i]]++;
}
sides[i] = sides[0];
dists[i] = dists[0];
// if the winding is on the plane and we should keep it
if ( keepOn && !counts[SIDE_FRONT] && !counts[SIDE_BACK] ) {
return true;
}
if ( !counts[SIDE_FRONT] ) {
numPoints = 0;
return false;
}
if ( !counts[SIDE_BACK] ) {
return true;
}
maxpts = numPoints + 4; // cant use counts[0]+2 because of fp grouping errors
newNumPoints = 0;
for ( i = 0; i < numPoints; i++ ) {
p1 = &p[i];
if ( newNumPoints+1 > maxpts ) {
return true; // can't split -- fall back to original
}
if ( sides[i] == SIDE_ON ) {
newPoints[newNumPoints] = *p1;
newNumPoints++;
continue;
}
if ( sides[i] == SIDE_FRONT ) {
newPoints[newNumPoints] = *p1;
newNumPoints++;
}
if ( sides[i+1] == SIDE_ON || sides[i+1] == sides[i] ) {
continue;
}
if ( newNumPoints+1 > maxpts ) {
return true; // can't split -- fall back to original
}
// generate a split point
p2 = &p[(i+1)%numPoints];
dot = dists[i] / (dists[i] - dists[i+1]);
for ( j = 0; j < 2; j++ ) {
// avoid round off error when possible
if ( plane[j] == 1.0f ) {
mid[j] = plane.z;
} else if ( plane[j] == -1.0f ) {
mid[j] = -plane.z;
} else {
mid[j] = (*p1)[j] + dot * ((*p2)[j] - (*p1)[j]);
}
}
newPoints[newNumPoints] = mid;
newNumPoints++;
}
if ( newNumPoints >= MAX_POINTS_ON_WINDING_2D ) {
return true;
}
numPoints = newNumPoints;
memcpy( p, newPoints, newNumPoints * sizeof(idVec2) );
return true;
}
/*
=============
idWinding2D::Copy
=============
*/
idWinding2D *idWinding2D::Copy() const {
idWinding2D *w;
w = new (TAG_IDLIB_WINDING) idWinding2D;
w->numPoints = numPoints;
memcpy( w->p, p, numPoints * sizeof( p[0] ) );
return w;
}
/*
=============
idWinding2D::Reverse
=============
*/
idWinding2D *idWinding2D::Reverse() const {
idWinding2D *w;
int i;
w = new (TAG_IDLIB_WINDING) idWinding2D;
w->numPoints = numPoints;
for ( i = 0; i < numPoints; i++ ) {
w->p[ numPoints - i - 1 ] = p[i];
}
return w;
}
/*
============
idWinding2D::GetArea
============
*/
float idWinding2D::GetArea() const {
int i;
idVec2 d1, d2;
float total;
total = 0.0f;
for ( i = 2; i < numPoints; i++ ) {
d1 = p[i-1] - p[0];
d2 = p[i] - p[0];
total += d1.x * d2.y - d1.y * d2.x;
}
return total * 0.5f;
}
/*
============
idWinding2D::GetCenter
============
*/
idVec2 idWinding2D::GetCenter() const {
int i;
idVec2 center;
center.Zero();
for ( i = 0; i < numPoints; i++ ) {
center += p[i];
}
center *= ( 1.0f / numPoints );
return center;
}
/*
============
idWinding2D::GetRadius
============
*/
float idWinding2D::GetRadius( const idVec2 &center ) const {
int i;
float radius, r;
idVec2 dir;
radius = 0.0f;
for ( i = 0; i < numPoints; i++ ) {
dir = p[i] - center;
r = dir * dir;
if ( r > radius ) {
radius = r;
}
}
return idMath::Sqrt( radius );
}
/*
============
idWinding2D::GetBounds
============
*/
void idWinding2D::GetBounds( idVec2 bounds[2] ) const {
int i;
if ( !numPoints ) {
bounds[0].x = bounds[0].y = idMath::INFINITY;
bounds[1].x = bounds[1].y = -idMath::INFINITY;
return;
}
bounds[0] = bounds[1] = p[0];
for ( i = 1; i < numPoints; i++ ) {
if ( p[i].x < bounds[0].x ) {
bounds[0].x = p[i].x;
} else if ( p[i].x > bounds[1].x ) {
bounds[1].x = p[i].x;
}
if ( p[i].y < bounds[0].y ) {
bounds[0].y = p[i].y;
} else if ( p[i].y > bounds[1].y ) {
bounds[1].y = p[i].y;
}
}
}
/*
=============
idWinding2D::IsTiny
=============
*/
#define EDGE_LENGTH 0.2f
bool idWinding2D::IsTiny() const {
int i;
float len;
idVec2 delta;
int edges;
edges = 0;
for ( i = 0; i < numPoints; i++ ) {
delta = p[(i+1)%numPoints] - p[i];
len = delta.Length();
if ( len > EDGE_LENGTH ) {
if ( ++edges == 3 ) {
return false;
}
}
}
return true;
}
/*
=============
idWinding2D::IsHuge
=============
*/
bool idWinding2D::IsHuge() const {
int i, j;
for ( i = 0; i < numPoints; i++ ) {
for ( j = 0; j < 2; j++ ) {
if ( p[i][j] <= MIN_WORLD_COORD || p[i][j] >= MAX_WORLD_COORD ) {
return true;
}
}
}
return false;
}
/*
=============
idWinding2D::Print
=============
*/
void idWinding2D::Print() const {
int i;
for ( i = 0; i < numPoints; i++ ) {
idLib::common->Printf( "(%5.1f, %5.1f)\n", p[i][0], p[i][1] );
}
}
/*
=============
idWinding2D::PlaneDistance
=============
*/
float idWinding2D::PlaneDistance( const idVec3 &plane ) const {
int i;
float d, min, max;
min = idMath::INFINITY;
max = -min;
for ( i = 0; i < numPoints; i++ ) {
d = plane.x * p[i].x + plane.y * p[i].y + plane.z;
if ( d < min ) {
min = d;
if ( IEEE_FLT_SIGNBITSET( min ) & IEEE_FLT_SIGNBITNOTSET( max ) ) {
return 0.0f;
}
}
if ( d > max ) {
max = d;
if ( IEEE_FLT_SIGNBITSET( min ) & IEEE_FLT_SIGNBITNOTSET( max ) ) {
return 0.0f;
}
}
}
if ( IEEE_FLT_SIGNBITNOTSET( min ) ) {
return min;
}
if ( IEEE_FLT_SIGNBITSET( max ) ) {
return max;
}
return 0.0f;
}
/*
=============
idWinding2D::PlaneSide
=============
*/
int idWinding2D::PlaneSide( const idVec3 &plane, const float epsilon ) const {
bool front, back;
int i;
float d;
front = false;
back = false;
for ( i = 0; i < numPoints; i++ ) {
d = plane.x * p[i].x + plane.y * p[i].y + plane.z;
if ( d < -epsilon ) {
if ( front ) {
return SIDE_CROSS;
}
back = true;
continue;
}
else if ( d > epsilon ) {
if ( back ) {
return SIDE_CROSS;
}
front = true;
continue;
}
}
if ( back ) {
return SIDE_BACK;
}
if ( front ) {
return SIDE_FRONT;
}
return SIDE_ON;
}
/*
============
idWinding2D::PointInside
============
*/
bool idWinding2D::PointInside( const idVec2 &point, const float epsilon ) const {
int i;
float d;
idVec3 plane;
for ( i = 0; i < numPoints; i++ ) {
plane = Plane2DFromPoints( p[i], p[(i+1) % numPoints] );
d = plane.x * point.x + plane.y * point.y + plane.z;
if ( d > epsilon ) {
return false;
}
}
return true;
}
/*
============
idWinding2D::LineIntersection
============
*/
bool idWinding2D::LineIntersection( const idVec2 &start, const idVec2 &end ) const {
int i, numEdges;
int sides[MAX_POINTS_ON_WINDING_2D+1], counts[3];
float d1, d2, epsilon = 0.1f;
idVec3 plane, edges[2];
counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
plane = Plane2DFromPoints( start, end );
for ( i = 0; i < numPoints; i++ ) {
d1 = plane.x * p[i].x + plane.y * p[i].y + plane.z;
if ( d1 > epsilon ) {
sides[i] = SIDE_FRONT;
}
else if ( d1 < -epsilon ) {
sides[i] = SIDE_BACK;
}
else {
sides[i] = SIDE_ON;
}
counts[sides[i]]++;
}
sides[i] = sides[0];
if ( !counts[SIDE_FRONT] ) {
return false;
}
if ( !counts[SIDE_BACK] ) {
return false;
}
numEdges = 0;
for ( i = 0; i < numPoints; i++ ) {
if ( sides[i] != sides[i+1] && sides[i+1] != SIDE_ON ) {
edges[numEdges++] = Plane2DFromPoints( p[i], p[(i+1)%numPoints] );
if ( numEdges >= 2 ) {
break;
}
}
}
if ( numEdges < 2 ) {
return false;
}
d1 = edges[0].x * start.x + edges[0].y * start.y + edges[0].z;
d2 = edges[0].x * end.x + edges[0].y * end.y + edges[0].z;
if ( IEEE_FLT_SIGNBITNOTSET( d1 ) & IEEE_FLT_SIGNBITNOTSET( d2 ) ) {
return false;
}
d1 = edges[1].x * start.x + edges[1].y * start.y + edges[1].z;
d2 = edges[1].x * end.x + edges[1].y * end.y + edges[1].z;
if ( IEEE_FLT_SIGNBITNOTSET( d1 ) & IEEE_FLT_SIGNBITNOTSET( d2 ) ) {
return false;
}
return true;
}
/*
============
idWinding2D::RayIntersection
============
*/
bool idWinding2D::RayIntersection( const idVec2 &start, const idVec2 &dir, float &scale1, float &scale2, int *edgeNums ) const {
int i, numEdges, localEdgeNums[2];
int sides[MAX_POINTS_ON_WINDING_2D+1], counts[3];
float d1, d2, epsilon = 0.1f;
idVec3 plane, edges[2];
scale1 = scale2 = 0.0f;
counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
plane = Plane2DFromVecs( start, dir );
for ( i = 0; i < numPoints; i++ ) {
d1 = plane.x * p[i].x + plane.y * p[i].y + plane.z;
if ( d1 > epsilon ) {
sides[i] = SIDE_FRONT;
}
else if ( d1 < -epsilon ) {
sides[i] = SIDE_BACK;
}
else {
sides[i] = SIDE_ON;
}
counts[sides[i]]++;
}
sides[i] = sides[0];
if ( !counts[SIDE_FRONT] ) {
return false;
}
if ( !counts[SIDE_BACK] ) {
return false;
}
numEdges = 0;
for ( i = 0; i < numPoints; i++ ) {
if ( sides[i] != sides[i+1] && sides[i+1] != SIDE_ON ) {
localEdgeNums[numEdges] = i;
edges[numEdges++] = Plane2DFromPoints( p[i], p[(i+1)%numPoints] );
if ( numEdges >= 2 ) {
break;
}
}
}
if ( numEdges < 2 ) {
return false;
}
d1 = edges[0].x * start.x + edges[0].y * start.y + edges[0].z;
d2 = - ( edges[0].x * dir.x + edges[0].y * dir.y );
if ( d2 == 0.0f ) {
return false;
}
scale1 = d1 / d2;
d1 = edges[1].x * start.x + edges[1].y * start.y + edges[1].z;
d2 = - ( edges[1].x * dir.x + edges[1].y * dir.y );
if ( d2 == 0.0f ) {
return false;
}
scale2 = d1 / d2;
if ( idMath::Fabs( scale1 ) > idMath::Fabs( scale2 ) ) {
SwapValues( scale1, scale2 );
SwapValues( localEdgeNums[0], localEdgeNums[1] );
}
if ( edgeNums ) {
edgeNums[0] = localEdgeNums[0];
edgeNums[1] = localEdgeNums[1];
}
return true;
}

View File

@@ -0,0 +1,169 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#ifndef __WINDING2D_H__
#define __WINDING2D_H__
/*
===============================================================================
A 2D winding is an arbitrary convex 2D polygon defined by an array of points.
===============================================================================
*/
#define MAX_POINTS_ON_WINDING_2D 16
class idWinding2D {
public:
idWinding2D();
idWinding2D & operator=( const idWinding2D &winding );
const idVec2 & operator[]( const int index ) const;
idVec2 & operator[]( const int index );
void Clear();
void AddPoint( const idVec2 &point );
int GetNumPoints() const;
void Expand( const float d );
void ExpandForAxialBox( const idVec2 bounds[2] );
// splits the winding into a front and back winding, the winding itself stays unchanged
// returns a SIDE_?
int Split( const idVec3 &plane, const float epsilon, idWinding2D **front, idWinding2D **back ) const;
// cuts off the part at the back side of the plane, returns true if some part was at the front
// if there is nothing at the front the number of points is set to zero
bool ClipInPlace( const idVec3 &plane, const float epsilon = ON_EPSILON, const bool keepOn = false );
idWinding2D * Copy() const;
idWinding2D * Reverse() const;
float GetArea() const;
idVec2 GetCenter() const;
float GetRadius( const idVec2 &center ) const;
void GetBounds( idVec2 bounds[2] ) const;
bool IsTiny() const;
bool IsHuge() const; // base winding for a plane is typically huge
void Print() const;
float PlaneDistance( const idVec3 &plane ) const;
int PlaneSide( const idVec3 &plane, const float epsilon = ON_EPSILON ) const;
bool PointInside( const idVec2 &point, const float epsilon ) const;
bool LineIntersection( const idVec2 &start, const idVec2 &end ) const;
bool RayIntersection( const idVec2 &start, const idVec2 &dir, float &scale1, float &scale2, int *edgeNums = NULL ) const;
static idVec3 Plane2DFromPoints( const idVec2 &start, const idVec2 &end, const bool normalize = false );
static idVec3 Plane2DFromVecs( const idVec2 &start, const idVec2 &dir, const bool normalize = false );
static bool Plane2DIntersection( const idVec3 &plane1, const idVec3 &plane2, idVec2 &point );
private:
int numPoints;
idVec2 p[MAX_POINTS_ON_WINDING_2D];
};
ID_INLINE idWinding2D::idWinding2D() {
numPoints = 0;
}
ID_INLINE idWinding2D &idWinding2D::operator=( const idWinding2D &winding ) {
int i;
for ( i = 0; i < winding.numPoints; i++ ) {
p[i] = winding.p[i];
}
numPoints = winding.numPoints;
return *this;
}
ID_INLINE const idVec2 &idWinding2D::operator[]( const int index ) const {
return p[ index ];
}
ID_INLINE idVec2 &idWinding2D::operator[]( const int index ) {
return p[ index ];
}
ID_INLINE void idWinding2D::Clear() {
numPoints = 0;
}
ID_INLINE void idWinding2D::AddPoint( const idVec2 &point ) {
p[numPoints++] = point;
}
ID_INLINE int idWinding2D::GetNumPoints() const {
return numPoints;
}
ID_INLINE idVec3 idWinding2D::Plane2DFromPoints( const idVec2 &start, const idVec2 &end, const bool normalize ) {
idVec3 plane;
plane.x = start.y - end.y;
plane.y = end.x - start.x;
if ( normalize ) {
plane.ToVec2().Normalize();
}
plane.z = - ( start.x * plane.x + start.y * plane.y );
return plane;
}
ID_INLINE idVec3 idWinding2D::Plane2DFromVecs( const idVec2 &start, const idVec2 &dir, const bool normalize ) {
idVec3 plane;
plane.x = -dir.y;
plane.y = dir.x;
if ( normalize ) {
plane.ToVec2().Normalize();
}
plane.z = - ( start.x * plane.x + start.y * plane.y );
return plane;
}
ID_INLINE bool idWinding2D::Plane2DIntersection( const idVec3 &plane1, const idVec3 &plane2, idVec2 &point ) {
float n00, n01, n11, det, invDet, f0, f1;
n00 = plane1.x * plane1.x + plane1.y * plane1.y;
n01 = plane1.x * plane2.x + plane1.y * plane2.y;
n11 = plane2.x * plane2.x + plane2.y * plane2.y;
det = n00 * n11 - n01 * n01;
if ( idMath::Fabs(det) < 1e-6f ) {
return false;
}
invDet = 1.0f / det;
f0 = ( n01 * plane2.z - n11 * plane1.z ) * invDet;
f1 = ( n01 * plane1.z - n00 * plane2.z ) * invDet;
point.x = f0 * plane1.x + f1 * plane2.x;
point.y = f0 * plane1.y + f1 * plane2.y;
return true;
}
#endif /* !__WINDING2D_H__ */