Beispiel #1
0
//
// Test a line against a mesh
// Selects the closest front-facing triangle
//
int CMesh::LineSelect( const CVec3 &LP1, const CVec3 &LP2 )
{
	CVec3 HitP;
	int nbHits = 0;
	int nSelTri = -1;
	float fDistance = 1000000000.0f;
	
	for (int nTri = 0; nTri < m_nbTris; nTri++ )
		{
		if ( m_pTriFlags[ nTri ] & TF_BACKFACING ) continue; // Check only front facing triangles
		
		int nV = nTri*3;	

		bool bHit = CheckLineTri( LP2, LP1, m_pVerts[ m_pTris[nV] ], m_pVerts[ m_pTris[nV+1] ], m_pVerts[ m_pTris[nV+2] ], HitP );
		if ( bHit ) {
			if ( HitP.Distance( LP1 ) < fDistance ) {
				fDistance = HitP.Distance( LP1 );
				nSelTri = nTri;
				}
			nbHits++;
			}
		}
		
	SelectTriangle( nSelTri );
	
	return nbHits;
}
Beispiel #2
0
void CControl::GetCorner( ECornerPosition type, float& x, float& y )
{
    CVec3 corner;
    switch ( type )
    {
    case eCP_BottomLeft:
        corner = m_quadp.bl;
        break;
    case eCP_BottomRight:
        corner = m_quadp.br;
        break;
    case eCP_TopLeft:
        corner = m_quadp.tl;
        break;
    case eCP_TopRight:
        corner = m_quadp.tr;
        break;
    default:
        BEATS_ASSERT( false, _T("the error ECornerPosition") );
        break;
    }
    corner.Transform( GetWorldTM());
    x = corner.x;
    y = corner.y;
}
Beispiel #3
0
CVertMeshViewer::CVertMeshViewer(UVertMesh* Mesh, CApplication* Window)
:	CMeshViewer(Mesh, Window)
,	AnimIndex(-1)
{
	CVertMeshInstance *VertInst = new CVertMeshInstance();
	VertInst->SetMesh(Mesh);
	Inst = VertInst;

	CVertMeshInstance *MeshInst = static_cast<CVertMeshInstance*>(Inst);
	// compute model center by Z-axis (vertical)
	CVec3 offset;
	const FBox &B = Mesh->BoundingBox;
#if 1
	VectorAdd(CVT(B.Min), CVT(B.Max), offset);
	offset.Scale(0.5f);
	MeshInst->BaseTransformScaled.TransformPointSlow(offset, offset);
#else
	// scale/translate origin
	float z = (B.Max.Z + B.Min.Z) / 2;
	z = (z - Mesh->MeshOrigin.Z) * Mesh->MeshScale.Z;	//!! bad formula
	offset.Set(0, 0, z);
#endif
	offset[2] += Mesh->BoundingSphere.R / 20;			// offset a bit up
	// offset view
	SetViewOffset(offset);
	// automatically scale view distance depending on model size
	float Radius = Mesh->BoundingSphere.R;
	if (Radius < 10) Radius = 10;
	SetDistScale(Mesh->MeshScale.X * Radius / 150);
}
Beispiel #4
0
int main()
{
	CVec3 a(.2,.5,.7);
	CVec3 b(-.2,-.2,.8);

	CVec3 c;

	c = a + b;
	a.print("a= ");
	b.print("b= ");
	c.print("a+b");
	c = a - b;
	c.print("a-b");
	c = b - a;
	c.print("b-a");
	(a-b).print("inline a-b: ");

	CVec3 d = c  + b - 3*c;
	(a^b).print("a^b" );

    CVec3 dd = CVec3(.2,.6,.9) + a;
	testvec(CVec3(.2,.2,.2)+CVec3(.1,.1,.1));
	testvec(dd^CVec3(.2,.3,.5));
	testvec(dd += CVec3(.3, .7. .2));

	return 0;
}
void CShapeModule::InitParticleForCone(CVec3& localPos, CVec3& direction, float fParticleScale) const
{
    CVec3 finalPos, finalDirection;
    float fClipAngle = m_fAngle;
    BEATS_CLIP_VALUE(fClipAngle, 0, 89.99f);
    float fRadius = m_fRadius * fParticleScale;
    float fConeLength = m_fConeLength * fParticleScale;
    float fTopRadius = fRadius + fConeLength * tanf(DegreesToRadians(fClipAngle));
    CVec3 randomDirection(PARTICLE_RAND_RANGE(-1, 1), PARTICLE_RAND_RANGE(-1, 1), 0);
    randomDirection.Normalize();

    float fRadiusOnBase = m_bEmitFromShell ? fRadius : PARTICLE_RAND_RANGE(0, fRadius);
    finalPos = randomDirection * fRadiusOnBase;
    BEATS_ASSERT(!BEATS_FLOAT_EQUAL(fRadius, 0));
    fTopRadius *= (fRadiusOnBase / fRadius);
    CVec3 topPos = (m_bRandomDirection ? GetRandomDirection() : randomDirection) * fTopRadius;
    topPos.Z() = fConeLength;
    finalDirection = topPos - finalPos;
    if (!m_bEmitFromBaseOrVolume)
    {
        finalPos += (finalDirection * PARTICLE_RAND_RANGE(0, 1));
        if (m_bRandomDirection)
        {
            finalDirection = GetRandomDirection();
        }
    }
    finalDirection.Normalize();
    localPos = finalPos;
    direction = finalDirection;
}
Beispiel #6
0
void S4r::Simulation::GetFields(const Vec3 &r, CVec3 &E, CVec3 &H){
	if(0 == layers.size()){
		// no layers
		E.setZero();
		H.setZero();
	}
	
	size_t ilayer = 0;
	double dz = r[2];
	{
		double z = 0;
		double t = layers[ilayer]->description.thickness;
		while(r[2] > z + t){
			z += t;
			dz -= t;
			if(ilayer+1 >= layers.size()){ break; }
			ilayer++;
			t = layers[ilayer]->description.thickness;
		}
	}
//fprintf(stderr, "(%f,%f,%f) in %s: dz = %f\n", r[0], r[1], r[2], L->name, dz);
	
	SolveLayer(ilayer);
	CVec2 phi = GetBlochPhaseFactors();
	layers[ilayer]->GetFields(omega, mesh, phi, Vec3(r[0], r[1], dz), E, H);
}
Beispiel #7
0
CVec3 CVec3::interpolateTo(const CVec3 &other, float fraction) const
{
	static CVec3 dir;
	dir.set(other);
	dir -= *(this);
	return (*this) + dir * fraction;
}
Beispiel #8
0
CVec3 CVec3::project(CVec3 normal) const
{
	float inv_denom = 1.0f / normal.dot(normal);
	float d = normal.dot((*this)) * inv_denom;

	CVec3 vec = normal * inv_denom;
	return (*this) - vec * d;
}
Beispiel #9
0
//
//  returns true if line (L1, L2) intersects with triangle( PV1, PV2, PV3 )
//  The point of intersection is returned in HitP
//
bool CheckLineTri( const CVec3 &L1, const CVec3 &L2, const CVec3 &PV1, const CVec3 &PV2, const CVec3 &PV3, CVec3 &HitP )
{
CVec3 VIntersect;

// Find Triangle Normal, would be quicker to have these computed already
CVec3 VNorm;
VNorm = ( PV2 - PV1 ).CrossProduct( PV3 - PV1 );
VNorm.Normalize();

// Find distance from L1 and L2 to the plane defined by the triangle
float fDst1 = (L1-PV1).Dot( VNorm );
float fDst2 = (L2-PV1).Dot( VNorm );

if ( (fDst1 * fDst2) >= 0.0f) return false;  // line doesn't cross the triangle.
if ( fDst1 == fDst2) {return false;} // line and plane are parallel

// Find point on the line that intersects with the plane
VIntersect = L1 + (L2-L1) * ( -fDst1/(fDst2-fDst1) );

// Find if the interesection point lies inside the triangle by testing it against all edges
CVec3 VTest;
VTest = VNorm.CrossProduct( PV2-PV1 );
if ( VTest.Dot( VIntersect-PV1 ) < 0.0f ) return false;
VTest = VNorm.CrossProduct( PV3-PV2 );
if ( VTest.Dot( VIntersect-PV2 ) < 0.0f ) return false;
VTest = VNorm.CrossProduct( PV1-PV3 );
if ( VTest.Dot( VIntersect-PV1 ) < 0.0f ) return false;

HitP = VIntersect;

return true;
}
Beispiel #10
0
///////////////////////////////////////////////////////////////////////////////
//		_Indirect_irradiance
// Estimate the in-direction irradiance (diffuse)
///////////////////////////////////////////////////////////////////////////////
CCol4 CMcBspTR::_Indirect_irradiance (const TBspCross &crs, int nPID, int nDepth)
{
	CCol4 cAccu = COLOR_BLACK;
	CBspMaterial *pm = &m_pMaterials[m_pTriangles[nPID].GetMaterialID()];

	if (m_nDistrRayNum <= 0) return cAccu;
	//-------------------------------------------------------------------------
	// If it is the first hit, we would need to estimate the indirect
	// illumination accurately. 
	//-------------------------------------------------------------------------
	if (nDepth == 0)
	{
		int nAccu = 0;
		BOOL bHitLig;
		TBspRay ray;

		// The local frame
		CVec3 vZ = crs.vNml.Normalized();
		CVec3 vY = RANDOM_VEC;
		CVec3 vX = (vY.Cross(vZ)).Normalized();
		vY = vZ.Cross(vX);

		// The inclination and azimuth resolution
		int m = (int) sqrt((double)(m_nDistrRayNum>>2));
		int n = m_nDistrRayNum / m;
		
		// Evenly distribute the sampling ray on the hemi-sphere according to
		// the inclination angle
		for (int i = 1; i <= n; i ++)
		{
			for (int j = 1; j <= m; j ++)
			{
				float fTheta = (float)asin(sqrt((j-RANDOM_0_1)/m));
				float fPhi   = 2 * PI * (i-RANDOM_0_1)/n;

				ray.vDir = vY * sin (fPhi) + vX * cos (fPhi);
				ray.vDir = vZ * sin (fTheta) + ray.vDir * cos (fTheta);
				ray.vOrg = crs.vPos;
				ray.vEnd = ray.vOrg + ray.vDir * m_fSceneSize;
				CCol4 cTemp = _PM_radiance_along_the_ray (ray, nPID, bHitLig);

				if (!bHitLig)
				{
					cAccu += cTemp;
					nAccu ++;
				}
			}
		}

		cAccu = cAccu * PI / nAccu;
		// cAccu = cAccu / nAccu;
	}
Beispiel #11
0
	////////////////////////////////////////////////////////////////////////////////////
	// Update - Changes wind when current target velocity expires
	////////////////////////////////////////////////////////////////////////////////////
	void		Update()
	{
		if (mTargetVelocityTimeRemaining==0)
		{
			if (FloatRand()<mChanceOfDeadTime)
			{
				mRDeadTime.Pick(mTargetVelocityTimeRemaining);
				mTargetVelocity.Clear();
			}
			else
			{
				mRDuration.Pick(mTargetVelocityTimeRemaining);
				mRVelocity.Pick(mTargetVelocity);
			}
		}
		else if (mTargetVelocityTimeRemaining!=-1)
		{
			mTargetVelocityTimeRemaining--;

			CVec3	DeltaVelocity(mTargetVelocity - mCurrentVelocity);
			float	DeltaVelocityLen = VectorNormalize(DeltaVelocity.v);
			if (DeltaVelocityLen > mMaxDeltaVelocityPerUpdate)
			{
				DeltaVelocityLen = mMaxDeltaVelocityPerUpdate;
			}
			DeltaVelocity *= (DeltaVelocityLen);
			mCurrentVelocity += DeltaVelocity;
		}
	}
void CShapeModule::InitParticleForBox(CVec3& localPos, CVec3& direction, float fParticleScale) const
{
    CVec3 halfSize = m_boxSize * 0.5f * fParticleScale;
    localPos.Fill(RANGR_RANDOM_FLOAT(-halfSize.X(), halfSize.X()),
        RANGR_RANDOM_FLOAT(-halfSize.Y(), halfSize.Y()),
        RANGR_RANDOM_FLOAT(-halfSize.Z(), halfSize.Z()));

    direction = CVec3(0, 0, 1);
    if (m_bRandomDirection)
    {
        direction = GetRandomDirection();
    }
}
Beispiel #13
0
	////////////////////////////////////////////////////////////////////////////////////
	// Initialize - Will setup default values for all data
	////////////////////////////////////////////////////////////////////////////////////
	void		Initialize()
	{
		mRBounds.Clear();
		mGlobal						= true;

		mRVelocity.mMins			= -1500.0f;
		mRVelocity.mMins[2]			= -10.0f;
		mRVelocity.mMaxs			= 1500.0f;
		mRVelocity.mMaxs[2]			= 10.0f;

		mMaxDeltaVelocityPerUpdate	= 10.0f;

		mRDuration.mMin				= 1000;
		mRDuration.mMax				= 2000;

		mChanceOfDeadTime			= 0.3f;
		mRDeadTime.mMin				= 1000;
		mRDeadTime.mMax				= 3000;

		mCurrentVelocity.Clear();
		mTargetVelocity.Clear();
		mTargetVelocityTimeRemaining = 0;
	}
Beispiel #14
0
////////////////////////////////////////////////////////////////////////////////////////
// Line Intersects Circle  (True/False)
//
//  r	- Radius Of The Circle
//  A	- Start Of Line Segment
//  B	- End Of Line Segment
//
//  P	- Projected Position Of Origin Onto Line AB
//
//
//               B
//             /
//           /
//         P
//       /   \      \
//     /     this-r->|
//   /              /
// A
//
////////////////////////////////////////////////////////////////////////////////////////
bool	CVec3::LineInCircle(const CVec3 &A, const CVec3 &B, float r, CVec3 &P)
{
	P = (*this);
	float	Scale = P.ProjectToLine(A, B);

	// If The Projected Position Is Not On The Line Segment,
	// Check If It Is Within Radius Of Endpoints A and B.
	//-------------------------------------------------------
	if (Scale<0 || Scale>1)
	{
		return (PtInCircle(A, r) || PtInCircle(B, r));
	}

	// Otherwise, Check To See If P Is Within The Radius Of This Circle
	//------------------------------------------------------------------
	return (PtInCircle(P, r));
}
Beispiel #15
0
float CVec3::distanceSquared(const CVec3 &other) const
{
	CVec3 d = other - *this;
	return d.lengthSquared();
}
Beispiel #16
0
CVec4::CVec4(const CVec3& vec3, float w)
{
    Fill(vec3.X(), vec3.Y(), vec3.Z(), w);
}
Beispiel #17
0
CVec3::CVec3(CVec3& vec)
{
    this->vec[0] = vec.x();
    this->vec[1] = vec.y();
    this->vec[2] = vec.z();
}
Beispiel #18
0
void USkeleton::ConvertAnims(UAnimSequence4* Seq)
{
	guard(USkeleton::ConvertAnims);

	CAnimSet* AnimSet = ConvertedAnim;

	if (!AnimSet)
	{
		AnimSet = new CAnimSet(this);
		ConvertedAnim = AnimSet;

		// Copy bone names
		AnimSet->TrackBoneNames.Empty(ReferenceSkeleton.RefBoneInfo.Num());
		for (int i = 0; i < ReferenceSkeleton.RefBoneInfo.Num(); i++)
		{
			AnimSet->TrackBoneNames.Add(ReferenceSkeleton.RefBoneInfo[i].Name);
		}

		//TODO: verify if UE4 has AnimRotationOnly stuff
		AnimSet->AnimRotationOnly = false;
	}

	if (!Seq) return; // allow calling ConvertAnims(NULL) to create empty AnimSet

//	DBG("----------- Skeleton %s: %d seq, %d bones -----------\n", Name, Anims.Num(), ReferenceSkeleton.RefBoneInfo.Num());

	int NumTracks = Seq->GetNumTracks();

#if DEBUG_DECOMPRESS
	appPrintf("Sequence %s: %d bones, %d offsets (%g per bone), %d frames, %d compressed data\n"
		   "          trans %s, rot %s, scale %s, key %s\n",
		Seq->Name, NumTracks, Seq->CompressedTrackOffsets.Num(), Seq->CompressedTrackOffsets.Num() / (float)NumTracks,
		Seq->NumFrames, Seq->CompressedByteStream.Num(),
		EnumToName(Seq->TranslationCompressionFormat),
		EnumToName(Seq->RotationCompressionFormat),
		EnumToName(Seq->ScaleCompressionFormat),
		EnumToName(Seq->KeyEncodingFormat)
	);
	for (int i2 = 0; i2 < Seq->CompressedTrackOffsets.Num(); /*empty*/)
	{
		if (Seq->KeyEncodingFormat != AKF_PerTrackCompression)
		{
			FName BoneName = ReferenceSkeleton.RefBoneInfo[Seq->GetTrackBoneIndex(i2/4)].Name;
			int TransOffset = Seq->CompressedTrackOffsets[i2  ];
			int TransKeys   = Seq->CompressedTrackOffsets[i2+1];
			int RotOffset   = Seq->CompressedTrackOffsets[i2+2];
			int RotKeys     = Seq->CompressedTrackOffsets[i2+3];
			appPrintf("    [%d] = trans %d[%d] rot %d[%d] - %s\n", i2/4, TransOffset, TransKeys, RotOffset, RotKeys, *BoneName);
			i2 += 4;
		}
		else
		{
			FName BoneName = ReferenceSkeleton.RefBoneInfo[Seq->GetTrackBoneIndex(i2/2)].Name;
			int TransOffset = Seq->CompressedTrackOffsets[i2  ];
			int RotOffset   = Seq->CompressedTrackOffsets[i2+1];
			appPrintf("    [%d] = trans %d rot %d - %s\n", i2/2, TransOffset, RotOffset, *BoneName);
			i2 += 2;
		}
	}
#endif // DEBUG_DECOMPRESS

	// some checks
	int offsetsPerBone = 4;
	if (Seq->KeyEncodingFormat == AKF_PerTrackCompression)
		offsetsPerBone = 2;

	if (Seq->CompressedTrackOffsets.Num() != NumTracks * offsetsPerBone && !Seq->RawAnimationData.Num())
	{
		appNotify("AnimSequence %s has wrong CompressedTrackOffsets size (has %d, expected %d), removing track",
			Seq->Name, Seq->CompressedTrackOffsets.Num(), NumTracks * offsetsPerBone);
		return;
	}

	// create CAnimSequence
	CAnimSequence *Dst = new CAnimSequence;
	AnimSet->Sequences.Add(Dst);
	Dst->Name      = Seq->Name;
	Dst->NumFrames = Seq->NumFrames;
	Dst->Rate      = Seq->NumFrames / Seq->SequenceLength * Seq->RateScale;

	// bone tracks ...
	Dst->Tracks.Empty(NumTracks);

	FMemReader Reader(Seq->CompressedByteStream.GetData(), Seq->CompressedByteStream.Num());
	Reader.SetupFrom(*Package);

	bool HasTimeTracks = (Seq->KeyEncodingFormat == AKF_VariableKeyLerp);

	for (int BoneIndex = 0; BoneIndex < ReferenceSkeleton.RefBoneInfo.Num(); BoneIndex++)
	{
		CAnimTrack *A = new (Dst->Tracks) CAnimTrack;
		int TrackIndex = Seq->FindTrackForBoneIndex(BoneIndex);

		if (TrackIndex < 0)
		{
			// this track has no animation, use static pose from ReferenceSkeleton
			const FTransform& RefPose = ReferenceSkeleton.RefBonePose[BoneIndex];
			A->KeyPos.Add(CVT(RefPose.Translation));
			A->KeyQuat.Add(CVT(RefPose.Rotation));
			//!! RefPose.Scale3D
			continue;
		}

		int k;

		if (!Seq->CompressedTrackOffsets.Num())	//?? or if RawAnimData.Num() != 0
		{
			// using RawAnimData array
			assert(Seq->RawAnimationData.Num() == NumTracks);
			CopyArray(A->KeyPos,  CVT(Seq->RawAnimationData[TrackIndex].PosKeys));
			CopyArray(A->KeyQuat, CVT(Seq->RawAnimationData[TrackIndex].RotKeys));
			CopyArray(A->KeyTime, Seq->RawAnimationData[TrackIndex].KeyTimes);	// may be empty
			for (int k = 0; k < A->KeyTime.Num(); k++)
				A->KeyTime[k] *= Dst->Rate;
			continue;
		}

		FVector Mins, Ranges;	// common ...
		static const CVec3 nullVec  = { 0, 0, 0 };
		static const CQuat nullQuat = { 0, 0, 0, 1 };

		int offsetIndex = TrackIndex * offsetsPerBone;

		// PARAGON has invalid data inside some animation tracks. Not sure if game engine ignores them
		// or trying to process (this game has holes in data due to wrong pointers in CompressedTrackOffsets).
		// This causes garbage data to appear instead of real animation track header, with wrong compression
		// method etc. We're going to skip such tracks with displaying a warning message.
		if (0) // this is just a placeholder for error handler - it should be located somewhere
		{
		track_error:
			AnimSet->Sequences.RemoveSingle(Dst);
			delete Dst;
			return;
		}

		//----------------------------------------------
		// decode AKF_PerTrackCompression data
		//----------------------------------------------
		if (Seq->KeyEncodingFormat == AKF_PerTrackCompression)
		{
			// this format uses different key storage
			guard(PerTrackCompression);
			assert(Seq->TranslationCompressionFormat == ACF_Identity);
			assert(Seq->RotationCompressionFormat == ACF_Identity);

			int TransOffset = Seq->CompressedTrackOffsets[offsetIndex  ];
			int RotOffset   = Seq->CompressedTrackOffsets[offsetIndex+1];

			uint32 PackedInfo;
			AnimationCompressionFormat KeyFormat;
			int ComponentMask;
			int NumKeys;

#define DECODE_PER_TRACK_INFO(info)									\
			KeyFormat = (AnimationCompressionFormat)(info >> 28);	\
			ComponentMask = (info >> 24) & 0xF;						\
			NumKeys       = info & 0xFFFFFF;						\
			HasTimeTracks = (ComponentMask & 8) != 0;

			guard(TransKeys);
			// read translation keys
			if (TransOffset == -1)
			{
				A->KeyPos.Add(nullVec);
				DBG("    [%d] no translation data\n", TrackIndex);
			}
			else
			{
				Reader.Seek(TransOffset);
				Reader << PackedInfo;
				DECODE_PER_TRACK_INFO(PackedInfo);
				A->KeyPos.Empty(NumKeys);
				DBG("    [%d] trans: fmt=%d (%s), %d keys, mask %d\n", TrackIndex,
					KeyFormat, EnumToName(KeyFormat), NumKeys, ComponentMask
				);
				if (KeyFormat == ACF_IntervalFixed32NoW)
				{
					// read mins/maxs
					Mins.Set(0, 0, 0);
					Ranges.Set(0, 0, 0);
					if (ComponentMask & 1) Reader << Mins.X << Ranges.X;
					if (ComponentMask & 2) Reader << Mins.Y << Ranges.Y;
					if (ComponentMask & 4) Reader << Mins.Z << Ranges.Z;
				}
				for (k = 0; k < NumKeys; k++)
				{
					switch (KeyFormat)
					{
//					case ACF_None:
					case ACF_Float96NoW:
						{
							FVector v;
							if (ComponentMask & 7)
							{
								v.Set(0, 0, 0);
								if (ComponentMask & 1) Reader << v.X;
								if (ComponentMask & 2) Reader << v.Y;
								if (ComponentMask & 4) Reader << v.Z;
							}
							else
							{
								// ACF_Float96NoW has a special case for ((ComponentMask & 7) == 0)
								Reader << v;
							}
							A->KeyPos.Add(CVT(v));
						}
						break;
					TPR(ACF_IntervalFixed32NoW, FVectorIntervalFixed32)
					case ACF_Fixed48NoW:
						{
							uint16 X, Y, Z;
							CVec3 v;
							v.Set(0, 0, 0);
							if (ComponentMask & 1)
							{
								Reader << X; v[0] = DecodeFixed48_PerTrackComponent<7>(X);
							}
							if (ComponentMask & 2)
							{
								Reader << Y; v[1] = DecodeFixed48_PerTrackComponent<7>(Y);
							}
							if (ComponentMask & 4)
							{
								Reader << Z; v[2] = DecodeFixed48_PerTrackComponent<7>(Z);
							}
							A->KeyPos.Add(v);
						}
						break;
					case ACF_Identity:
						A->KeyPos.Add(nullVec);
						break;
					default:
						{
							char buf[1024];
							Seq->GetFullName(buf, 1024);
							appNotify("%s: unknown translation compression method: %d (%s) - dropping track", buf, KeyFormat, EnumToName(KeyFormat));
							goto track_error;
						}
					}
				}
				// align to 4 bytes
				Reader.Seek(Align(Reader.Tell(), 4));
				if (HasTimeTracks)
					ReadTimeArray(Reader, NumKeys, A->KeyPosTime, Seq->NumFrames);
			}
			unguard;

			guard(RotKeys);
			// read rotation keys
			if (RotOffset == -1)
			{
				A->KeyQuat.Add(nullQuat);
				DBG("    [%d] no rotation data\n", TrackIndex);
			}
			else
			{
				Reader.Seek(RotOffset);
				Reader << PackedInfo;
				DECODE_PER_TRACK_INFO(PackedInfo);
				A->KeyQuat.Empty(NumKeys);
				DBG("    [%d] rot  : fmt=%d (%s), %d keys, mask %d\n", TrackIndex,
					KeyFormat, EnumToName(KeyFormat), NumKeys, ComponentMask
				);
				if (KeyFormat == ACF_IntervalFixed32NoW)
				{
					// read mins/maxs
					Mins.Set(0, 0, 0);
					Ranges.Set(0, 0, 0);
					if (ComponentMask & 1) Reader << Mins.X << Ranges.X;
					if (ComponentMask & 2) Reader << Mins.Y << Ranges.Y;
					if (ComponentMask & 4) Reader << Mins.Z << Ranges.Z;
				}
				for (k = 0; k < NumKeys; k++)
				{
					switch (KeyFormat)
					{
//					TR (ACF_None, FQuat)
					case ACF_Float96NoW:
						{
							FQuatFloat96NoW q;
							Reader << q;
							FQuat q2 = q;				// convert
							A->KeyQuat.Add(CVT(q2));
						}
						break;
					case ACF_Fixed48NoW:
						{
							FQuatFixed48NoW q;
							q.X = q.Y = q.Z = 32767;	// corresponds to 0
							if (ComponentMask & 1) Reader << q.X;
							if (ComponentMask & 2) Reader << q.Y;
							if (ComponentMask & 4) Reader << q.Z;
							FQuat q2 = q;				// convert
							A->KeyQuat.Add(CVT(q2));
						}
						break;
					TR (ACF_Fixed32NoW, FQuatFixed32NoW)
					TRR(ACF_IntervalFixed32NoW, FQuatIntervalFixed32NoW)
					TR (ACF_Float32NoW, FQuatFloat32NoW)
					case ACF_Identity:
						A->KeyQuat.Add(nullQuat);
						break;
					default:
						{
							char buf[1024];
							Seq->GetFullName(buf, 1024);
							appNotify("%s: unknown rotation compression method: %d (%s) - dropping track", buf, KeyFormat, EnumToName(KeyFormat));
							goto track_error;
						}
					}
				}
				// align to 4 bytes
				Reader.Seek(Align(Reader.Tell(), 4));
				if (HasTimeTracks)
					ReadTimeArray(Reader, NumKeys, A->KeyQuatTime, Seq->NumFrames);
			}
			unguard;

			unguard;
			continue;
			// end of AKF_PerTrackCompression block ...
		}

		//----------------------------------------------
		// end of AKF_PerTrackCompression decoder
		//----------------------------------------------

		// read animations
		int TransOffset = Seq->CompressedTrackOffsets[offsetIndex  ];
		int TransKeys   = Seq->CompressedTrackOffsets[offsetIndex+1];
		int RotOffset   = Seq->CompressedTrackOffsets[offsetIndex+2];
		int RotKeys     = Seq->CompressedTrackOffsets[offsetIndex+3];
//		appPrintf("[%d:%d:%d] :  %d[%d]  %d[%d]  %d[%d]\n", j, Seq->RotationCompressionFormat, Seq->TranslationCompressionFormat, TransOffset, TransKeys, RotOffset, RotKeys, ScaleOffset, ScaleKeys);

		A->KeyPos.Empty(TransKeys);
		A->KeyQuat.Empty(RotKeys);

		// read translation keys
		if (TransKeys)
		{
			Reader.Seek(TransOffset);
			AnimationCompressionFormat TranslationCompressionFormat = Seq->TranslationCompressionFormat;
			if (TransKeys == 1)
				TranslationCompressionFormat = ACF_None;	// single key is stored without compression
			// read mins/ranges
			if (TranslationCompressionFormat == ACF_IntervalFixed32NoW)
			{
				Reader << Mins << Ranges;
			}

			for (k = 0; k < TransKeys; k++)
			{
				switch (TranslationCompressionFormat)
				{
				TP (ACF_None,               FVector)
				TP (ACF_Float96NoW,         FVector)
				TPR(ACF_IntervalFixed32NoW, FVectorIntervalFixed32)
				TP (ACF_Fixed48NoW,         FVectorFixed48)
				case ACF_Identity:
					A->KeyPos.Add(nullVec);
					break;
				default:
					appError("Unknown translation compression method: %d (%s)", TranslationCompressionFormat, EnumToName(TranslationCompressionFormat));
				}
			}

			// align to 4 bytes
			Reader.Seek(Align(Reader.Tell(), 4));
			if (HasTimeTracks)
				ReadTimeArray(Reader, TransKeys, A->KeyPosTime, Seq->NumFrames);
		}
		else
		{
//			A->KeyPos.Add(nullVec);
//			appNotify("No translation keys!");
		}
Beispiel #19
0
//void testvec(CVec3& a)
//{
	//a.print("inside testvec CVec3&, a= ");
//}
void testvec(CVec3 a)
{
	a.print("inside testvec CVec3, a= ");
}
Beispiel #20
0
inline void TransformPosition(CVec3& pos)
{
	Exchange(pos[1], pos[2]);
	pos.Scale(0.01f);
}
Beispiel #21
0
static void SV_ClipMoveToEntities(trace_t &trace, const CVec3 &start, const CVec3 &end, const CBox &bounds, edict_t *passedict, int contentmask)
{
	guard(SV_ClipMoveToEntities);

	if (trace.allsolid) return;

	int		i;

	CVec3 amins, amaxs;
	for (i = 0; i < 3; i++)
	{
		if (start[i] < end[i])
		{
			amins[i] = bounds.mins[i] + start[i];
			amaxs[i] = bounds.maxs[i] + end[i];
		}
		else
		{
			amins[i] = bounds.mins[i] + end[i];
			amaxs[i] = bounds.maxs[i] + start[i];
		}
	}
	edict_t	*list[MAX_EDICTS];
	int num = SV_AreaEdicts(amins, amaxs, ARRAY_ARG(list), AREA_SOLID);
	if (!num) return;

	float b1 = dot(bounds.mins, bounds.mins);
	float b2 = dot(bounds.maxs, bounds.maxs);
	float t = max(b1, b2);
	float traceWidth = SQRTFAST(t);
	CVec3 traceDir;
	VectorSubtract(end, start, traceDir);
	float traceLen = traceDir.Normalize() + traceWidth;

	for (i = 0; i < num; i++)
	{
		edict_t *edict = list[i];
		entityHull_t &ent = ents[NUM_FOR_EDICT(edict)];
//		if (!ent->linked) continue;

		if (edict->solid == SOLID_NOT || edict == passedict) continue;
		if (passedict)
		{
		 	if (edict->owner == passedict)
				continue;	// don't clip against own missiles
			if (passedict->owner == edict)
				continue;	// don't clip against owner
		}
		if (!(contentmask & CONTENTS_DEADMONSTER) && (edict->svflags & SVF_DEADMONSTER))
			continue;

		CVec3 eCenter;
		VectorSubtract(ent.center, start, eCenter);
		// check position of point projection on line
		float entPos = dot(eCenter, traceDir);
		if (entPos < -traceWidth - ent.radius || entPos > traceLen + ent.radius)
			continue;		// too near / too far

		// check distance between point and line
		CVec3 tmp;
		VectorMA(eCenter, -entPos, traceDir, tmp);
		float dist2 = dot(tmp, tmp);
		float dist0 = ent.radius + traceWidth;
		if (dist2 >= dist0 * dist0) continue;

		trace_t	tr;
		if (ent.model)
			CM_TransformedBoxTrace(tr, start, end, bounds, ent.model->headnode, contentmask, edict->s.origin, ent.axis);
		else
			CM_TransformedBoxTrace(tr, start, end, bounds, CM_HeadnodeForBox(ent.bounds), contentmask, edict->s.origin, nullVec3);
		if (CM_CombineTrace(trace, tr))
			trace.ent = edict;
		if (trace.allsolid) return;
	}

	unguard;
}
Beispiel #22
0
	inline void	Clear()
	{
		mMins.Clear();
		mMaxs.Clear();
	}
Beispiel #23
0
///////////////////////////////////////////////////////////////////////////////
//		_Create_caustic_photonmap
// Build the caustic photon map after the creation of global photon map
///////////////////////////////////////////////////////////////////////////////
void CMcBspTR::_Create_caustic_photonmap(void)
{
	int     i, j;
	CCol4   cCurrPow;
	int     nStored = 0;
	TBspRay ray;
	float   fLen;

	printf("\nBuilding caustic photon map ...\n");

	TCausDir *p = m_tCausDir.next;

	for (i = 0; i < m_nCausDir; i ++)
	{
		printf (".");
		CCol4 cPow = m_tpLigPatches[p->nLID].cPhotonPow / m_nCauPhoSubd * 5;

		if (m_bDirectionalLight)
		{
			CBspTriangle *tri = &m_pTriangles[m_tpLigPatches[p->nLID].nTID];
			fLen = sqrt(tri->Calc_area() / m_tpLigPatches[p->nLID].nPhotonNum);
			CVec3 vZ = tri->Calc_normal();
			CVec3 vY = RANDOM_VEC;
			CVec3 vX = vY.Cross(vZ);
			vX.Normalize();
			vY = vZ.Cross(vX);

			vX *= fLen;
			vY *= fLen;

			for (j = 0; j < m_nCauPhoSubd; j ++)
			{
				ray.vOrg = p->vPos + vX * RANDOM_N1_P1 + vY * RANDOM_N1_P1;
				ray.vDir = p->vDir;
				ray.vEnd = ray.vOrg + ray.vDir * m_fSceneSize;
				_Trace_caustic_photon (ray, cPow, m_tpLigPatches[p->nLID].nTID,
					nStored);
			}
		}
		else
		{
			double fCos = cos(PI/sqrt(m_tpLigPatches[p->nLID].nPhotonNum*4.));
			fLen = 2.f * (float) tan( acos(fCos) );

			for (j = 0; j < m_nCauPhoSubd; j ++)
			{
				ray.vOrg = p->vPos;
				ray.vDir = p->vDir + (RANDOM_VEC * fLen);
				ray.vDir.Normalize();
				ray.vEnd = ray.vOrg + ray.vDir * m_fSceneSize;
				_Trace_caustic_photon (ray, cPow, m_tpLigPatches[p->nLID].nTID,
					nStored);
			}
		}
		
		m_tCausDir.next = p->next;
		delete p;
		p = m_tCausDir.next;
	}
	m_tCausDir.next = NULL;

	// Balance photon map kd-tree
	printf("\n Balance KD tree");
	m_pCausticPhotonMap->balance();
	printf("\nFinish building caustic photon map, %d photons stored\n", nStored);
}
Beispiel #24
0
sizebuf_t *SV_MulticastHook(sizebuf_t *original, sizebuf_t *ext)
{
	CVec3	v1, v2;
	const char *s;

	guard(SV_MulticastHook);

	original->BeginReading();

	// check for interesting messages
	if (MSG_ReadByte(original) != svc_temp_entity)
		return original;

	ext->Clear();
	byte cmd = MSG_ReadByte(original);
	switch (cmd)
	{
	case TE_RAILTRAIL:
		{
			MSG_ReadPos(original, v1);		// start
			MSG_ReadPos(original, v2);		// end

			client_t *cl = FindClient(v1, 18*18);
			int rType = 1;
			int rColor = 0;
			if (cl)
			{
				if (s = Info_ValueForKey(cl->Userinfo, "railcolor"))
				{
					rColor = atoi(s);
					rColor = bound(rColor, 0, 7);
				}
				if (s = Info_ValueForKey(cl->Userinfo, "railtype"))
				{
					rType = atoi(s);
					rType = bound(rType, 0, 3);
				}
			}
			if (!rType) return original;	// type==0 -> original rail trail

			MSG_WriteByte(ext, svc_temp_entity);
			MSG_WriteByte(ext, TE_RAILTRAIL_EXT);
			MSG_WritePos(ext, v1);
			MSG_WritePos(ext, v2);
			MSG_WriteByte(ext, rColor);
			MSG_WriteByte(ext, rType);
		}
		return ext;

	case TE_GUNSHOT:
	case TE_SPARKS:
	case TE_BULLET_SPARKS:
		{
			if (shotLevel != 2) return original;

			MSG_ReadPos(original, v1);
			MSG_ReadDir(original, v2);

			// compute reflection vector
//			appPrintf("sp: (%g %g %g) -> (%g %g %g)\n",VECTOR_ARG(v1),VECTOR_ARG(v2));//!!
			CVec3 d;
			VectorSubtract(shotStart, shotEnd, d);
			d.NormalizeFast();
			float back = dot(d, v2);
			for (int i = 0; i < 3; i++)
				v2[i] = d[i] - 2 * (d[i] - back * v2[i]);

			MSG_WriteByte(ext, svc_temp_entity);
			MSG_WriteByte(ext, cmd);
			MSG_WritePos(ext, v1);
			MSG_WriteDir(ext, v2);
		}
		return ext;

	case TE_SPLASH:
		{
			int count = MSG_ReadByte(original);
			MSG_ReadPos(original, v1);			// pos
			int tmp = MSG_ReadByte(original);	// dir
			int type = MSG_ReadByte(original);
			if (type != SPLASH_BLUE_WATER && type != SPLASH_BROWN_WATER)
				return original;			// not bullet effect

			// find splash origin in static map splashes to avoid bullethit sounds for waterfalls etc.
			for (const originInfo_t *spl = bspfile.splashes; spl; spl = spl->next)
			{
				if (fabs(spl->origin[0] - v1[0]) < 1 &&
					fabs(spl->origin[1] - v1[1]) < 1 &&
					fabs(spl->origin[2] - v1[2]) < 1)		// dir is quantized, so - make inprecisious compare
					return original;
			}

			//?? can add ripple effect on water, water smoke
			MSG_WriteByte(ext, svc_temp_entity);
			MSG_WriteByte(ext, TE_SPLASH);
			MSG_WriteByte(ext, count);
			MSG_WritePos(ext, v1);
			MSG_WriteByte(ext, tmp);
			MSG_WriteByte(ext, type - SPLASH_BLUE_WATER + SPLASH_BULLET_BLUE_WATER);
		}
		return ext;
	}

	return original;
	unguard;
}
Beispiel #25
0
void UVertMesh::BuildNormals()
{
	// UE1 meshes have no stored normals, should build them
	// This function is similar to BuildNormals() from SkelMeshInstance.cpp
	int numVerts = Verts.Num();
	int i;
	Normals.Empty(numVerts);
	Normals.AddZeroed(numVerts);
	TArray<CVec3> tmpVerts, tmpNormals;
	tmpVerts.AddZeroed(numVerts);
	tmpNormals.AddZeroed(numVerts);
	// convert verts
	for (i = 0; i < numVerts; i++)
	{
		const FMeshVert &SV = Verts[i];
		CVec3           &DV = tmpVerts[i];
		DV[0] = SV.X * MeshScale.X;
		DV[1] = SV.Y * MeshScale.Y;
		DV[2] = SV.Z * MeshScale.Z;
	}
	// iterate faces
	for (i = 0; i < Faces.Num(); i++)
	{
		const FMeshFace &F = Faces[i];
		// get vertex indices
		int i1 = Wedges[F.iWedge[0]].iVertex;
		int i2 = Wedges[F.iWedge[2]].iVertex;		// note: reverse order in comparison with SkeletalMesh
		int i3 = Wedges[F.iWedge[1]].iVertex;
		// iterate all frames
		for (int j = 0; j < FrameCount; j++)
		{
			int base = VertexCount * j;
			// compute edges
			const CVec3 &V1 = tmpVerts[base + i1];
			const CVec3 &V2 = tmpVerts[base + i2];
			const CVec3 &V3 = tmpVerts[base + i3];
			CVec3 D1, D2, D3;
			VectorSubtract(V2, V1, D1);
			VectorSubtract(V3, V2, D2);
			VectorSubtract(V1, V3, D3);
			// compute normal
			CVec3 norm;
			cross(D2, D1, norm);
			norm.Normalize();
			// compute angles
			D1.Normalize();
			D2.Normalize();
			D3.Normalize();
			float angle1 = acos(-dot(D1, D3));
			float angle2 = acos(-dot(D1, D2));
			float angle3 = acos(-dot(D2, D3));
			// add normals for triangle verts
			VectorMA(tmpNormals[base + i1], angle1, norm);
			VectorMA(tmpNormals[base + i2], angle2, norm);
			VectorMA(tmpNormals[base + i3], angle3, norm);
		}
	}
	// normalize and convert computed normals
	for (i = 0; i < numVerts; i++)
	{
		CVec3 &SN     = tmpNormals[i];
		FMeshNorm &DN = Normals[i];
		SN.Normalize();
		DN.X = appRound(SN[0] * 511 + 512);
		DN.Y = appRound(SN[1] * 511 + 512);
		DN.Z = appRound(SN[2] * 511 + 512);
	}
}
Beispiel #26
0
///////////////////////////////////////////////////////////////////////////////
//		_Direct_lighting
// Calculate the radiance directly come from light sources
///////////////////////////////////////////////////////////////////////////////
CCol4 CMcBspTR::_Direct_lighting (const CVec3 &vPos, const CVec3 &vNml,
								  const CVec3 &vView, const CVec3 &vTex,
								  int PID)
{
	int l;
	TBspRay ray;
	TBspCross crs;
	CBspMaterial *pm;
	CCol4 cRsl = COLOR_BLACK;

	pm = &m_pMaterials[m_pTriangles[PID].GetMaterialID()];

	if (m_bDirectionalLight)
	{
		ray.vOrg = vPos;
		ray.vDir = m_vLightDir;
		ray.vEnd = ray.vOrg + ray.vDir * m_fSceneSize;

		if (ray.vDir.Dot(vNml) < 0.f)	return cRsl;

		int PID2 = PID;
		if (_RayTreeIntersect (ray, crs, PID2))
		{
			if (!_Is_light_patch(PID2)) return cRsl;
			cRsl =  G_theSahder.PhongShading (m_cLightColor, ray.vDir,
				vView*(-1), vPos, vNml, vTex.at(0), vTex.at(1), pm);
		}
		return cRsl;
	}

	for (l = 0; l < m_nLigPatches; l ++)
	{
		//---------------------------------------------------------------------
		// construct a ray point to a random point on the selected light patch
		//---------------------------------------------------------------------
		CBspTriangle *tri = &m_pTriangles[m_tpLigPatches[l].nTID];
		ray.vEnd = tri->Random_point_on_the_triangle();
		ray.vOrg = vPos;
		ray.vDir = ray.vEnd - vPos;
		ray.vDir.Normalize();
		ray.vEnd += ray.vDir; // extend the ray a little bit

		//---------------------------------------------------------------------
		// If the patch does not facing the light patch or the light patch dose
		// not facing the patch, return black;
		//---------------------------------------------------------------------
		if (ray.vDir.Dot(vNml) < 0.f) continue;
		float fDirectionalScale = m_tpLigPatches[l].vNml.Dot(ray.vDir);
		if (fDirectionalScale > 0.f) continue;
		else fDirectionalScale = -fDirectionalScale;

		//---------------------------------------------------------------------
		// Check if the light is occluded by another patch
		// Calculate the shading by phong model if is lit
		//---------------------------------------------------------------------
		int PID2 = PID;
		if (_RayTreeIntersect (ray, crs, PID2))
		{
			if (PID2 == m_tpLigPatches[l].nTID)
			{
				// Calculate the lighting intensity according to the the
				// distance and the intensity of the light patch.
				CVec3 vTemp = crs.vPos - vPos;
				//float fTemp = vTemp.Dot(vTemp) * 4 * PI;
				float fTemp = vTemp.Dot(vTemp) * PI;
				CCol4 cIntensity =
					m_tpLigPatches[l].cEnergy * fDirectionalScale / fTemp;

				// Shading computation by Phong model.
				cRsl += G_theSahder.PhongShading (cIntensity, ray.vDir,
					vView*(-1), vPos, vNml, vTex[0], vTex[1], pm);
			}
		}
	}

	return cRsl;
}
Beispiel #27
0
void	Pilot_Steer_Vehicle()
{
	if (!NPC->enemy || !NPC->enemy->client)
	{
		return;
	}






// SETUP
//=======
	// Setup Actor Data
	//------------------
	CVec3		ActorPos(NPC->currentOrigin);
	CVec3		ActorAngles(NPC->currentAngles);
				ActorAngles[2]	= 0;
	Vehicle_t*	ActorVeh		= NPCInfo->greetEnt->m_pVehicle;
	bool		ActorInTurbo	= (ActorVeh->m_iTurboTime>level.time);
	float		ActorSpeed		= (ActorVeh)?(VectorLength(ActorVeh->m_pParentEntity->client->ps.velocity)):(NPC->client->ps.speed);


	// If my vehicle is spinning out of control, just hold on, we're going to die!!!!!
	//---------------------------------------------------------------------------------
	if (ActorVeh && (ActorVeh->m_ulFlags & VEH_OUTOFCONTROL))
	{
		if (NPC->client->ps.weapon!=WP_NONE)
		{
			NPC_ChangeWeapon(WP_NONE);
		}
		ucmd.buttons	&=~BUTTON_ATTACK;
		ucmd.buttons	&=~BUTTON_ALT_ATTACK;
		return;
	}

	CVec3		ActorDirection;
				AngleVectors(ActorAngles.v, ActorDirection.v, 0, 0);

	CVec3		ActorFuturePos(ActorPos);
				ActorFuturePos.ScaleAdd(ActorDirection, FUTURE_PRED_DIST);

	bool		ActorDoTurbo	= false;
	bool		ActorAccelerate	= false;
	bool		ActorAimAtTarget= true;
	float		ActorYawOffset	= 0.0f;


	// Setup Enemy Data
	//------------------
	CVec3		EnemyPos(NPC->enemy->currentOrigin);
	CVec3		EnemyAngles(NPC->enemy->currentAngles);
				EnemyAngles[2]	= 0;
	Vehicle_t*	EnemyVeh		= (NPC->enemy->s.m_iVehicleNum)?(g_entities[NPC->enemy->s.m_iVehicleNum].m_pVehicle):(0);
	bool		EnemyInTurbo	= (EnemyVeh && EnemyVeh->m_iTurboTime>level.time);
	float		EnemySpeed		= (EnemyVeh)?(EnemyVeh->m_pParentEntity->client->ps.speed):(NPC->enemy->resultspeed);
	bool		EnemySlideBreak	= (EnemyVeh && (EnemyVeh->m_ulFlags&VEH_SLIDEBREAKING || EnemyVeh->m_ulFlags&VEH_STRAFERAM));
	bool		EnemyDead		= (NPC->enemy->health<=0);

	bool		ActorFlank		= (NPCInfo->lastAvoidSteerSideDebouncer>level.time && EnemyVeh && EnemySpeed>10.0f);

	CVec3		EnemyDirection;
	CVec3		EnemyRight;
				AngleVectors(EnemyAngles.v, EnemyDirection.v, EnemyRight.v, 0);

	CVec3		EnemyFuturePos(EnemyPos);
				EnemyFuturePos.ScaleAdd(EnemyDirection, FUTURE_PRED_DIST);

	ESide		EnemySide		= ActorPos.LRTest(EnemyPos, EnemyFuturePos);
	CVec3		EnemyFlankPos(EnemyFuturePos);
				EnemyFlankPos.ScaleAdd(EnemyRight, (EnemySide==Side_Right)?(FUTURE_SIDE_DIST):(-FUTURE_SIDE_DIST));

	// Debug Draw Enemy Data
	//-----------------------
	if (false)
	{
		CG_DrawEdge(EnemyPos.v,			EnemyFuturePos.v, EDGE_IMPACT_SAFE);
		CG_DrawEdge(EnemyFuturePos.v,	EnemyFlankPos.v, EDGE_IMPACT_SAFE);
	}


	// Setup Move And Aim Directions
	//-------------------------------
	CVec3		MoveDirection((ActorFlank)?(EnemyFlankPos):(EnemyFuturePos));
				MoveDirection	-= ActorPos;
	float		MoveDistance	= MoveDirection.SafeNorm();
	float		MoveAccuracy	= MoveDirection.Dot(ActorDirection);

	CVec3		AimDirection(EnemyPos);
				AimDirection	-= ActorPos;
	float		AimDistance		= AimDirection.SafeNorm();
	float		AimAccuracy		= AimDirection.Dot(ActorDirection);



	if (!ActorFlank && TIMER_Done(NPC, "FlankAttackCheck"))
	{
		TIMER_Set(NPC, "FlankAttackCheck", Q_irand(1000, 3000));
		if (MoveDistance<4000 && Q_irand(0, 1)==0)
		{
			NPCInfo->lastAvoidSteerSideDebouncer	= level.time + Q_irand(8000, 14000);
		}
	}



	// Fly By Sounds
	//---------------
	if ((ActorVeh->m_pVehicleInfo->soundFlyBy || ActorVeh->m_pVehicleInfo->soundFlyBy2) &&
		EnemyVeh &&
		MoveDistance<800 &&
		ActorSpeed>500.0f &&
		TIMER_Done(NPC, "FlybySoundDebouncer")
		)
	{
		if (EnemySpeed<100.0f || (ActorDirection.Dot(EnemyDirection)*(MoveDistance/800.0f))<-0.5f)
		{
			TIMER_Set(NPC, "FlybySoundDebouncer", 2000);
			int soundFlyBy = ActorVeh->m_pVehicleInfo->soundFlyBy;
			if (ActorVeh->m_pVehicleInfo->soundFlyBy2 && (!soundFlyBy || !Q_irand(0,1)))
			{
				soundFlyBy = ActorVeh->m_pVehicleInfo->soundFlyBy2;
			}
			G_Sound(ActorVeh->m_pParentEntity, soundFlyBy);		
		}
	}



// FLY PAST BEHAVIOR
//===================
 	if (EnemySlideBreak || !TIMER_Done(NPC, "MinHoldDirectionTime"))
	{
		if (TIMER_Done(NPC, "MinHoldDirectionTime"))
		{ 
			TIMER_Set(NPC, "MinHoldDirectionTime", 500);	// Hold For At Least 500 ms
		}
		ActorAccelerate		= true;							// Go
		ActorAimAtTarget	= false;						// Don't Alter Our Aim Direction
		ucmd.buttons		&=~BUTTON_VEH_SPEED;			// Let Normal Vehicle Controls Go
	}


// FLANKING BEHAVIOR
//===================
	else if (ActorFlank)
	{
  		ActorAccelerate	= true;
		ActorDoTurbo	= (MoveDistance>2500 || EnemyInTurbo);
		ucmd.buttons	|= BUTTON_VEH_SPEED;			// Tells PMove to use the ps.speed we calculate here, not the one from g_vehicles.c


		// For Flanking, We Calculate The Speed By Hand, Rather Than Using Pure Accelerate / No Accelerate Functionality
		//---------------------------------------------------------------------------------------------------------------
		NPC->client->ps.speed = ActorVeh->m_pVehicleInfo->speedMax * ((ActorInTurbo)?(1.35f):(1.15f));


		// If In Slowing Distance, Scale Down The Speed As We Approach Our Move Target
		//-----------------------------------------------------------------------------
		if (MoveDistance<ATTACK_FLANK_SLOWING)
		{
			NPC->client->ps.speed *= (MoveDistance/ATTACK_FLANK_SLOWING);
			NPC->client->ps.speed += EnemySpeed;

			// Match Enemy Speed
			//-------------------
			if (NPC->client->ps.speed<5.0f && EnemySpeed<5.0f)
			{
				NPC->client->ps.speed = EnemySpeed;
			}

			// Extra Slow Down When Out In Front
			//-----------------------------------
 			if  (MoveAccuracy<0.0f)
			{
				NPC->client->ps.speed *= (MoveAccuracy + 1.0f);
			}

	
			MoveDirection	*=        (MoveDistance/ATTACK_FLANK_SLOWING);
			EnemyDirection	*= 1.0f - (MoveDistance/ATTACK_FLANK_SLOWING);
			MoveDirection	+= EnemyDirection;

			if (TIMER_Done(NPC, "RamCheck"))
			{
				TIMER_Set(NPC, "RamCheck", Q_irand(1000, 3000));
				if (MoveDistance<RAM_DIST && Q_irand(0, 2)==0)
				{
					VEH_StartStrafeRam(ActorVeh, (EnemySide==Side_Left));
				}
			}
		}
	}


// NORMAL CHASE BEHAVIOR
//=======================
	else
	{
		if (!EnemyVeh && AimAccuracy>0.99f && MoveDistance<500 && !EnemyDead)
		{
			ActorAccelerate = true;
			ActorDoTurbo	= false;
		}
		else
		{
			ActorAccelerate = ((MoveDistance>500 && EnemySpeed>20.0f) || MoveDistance>1000);
			ActorDoTurbo	= (MoveDistance>3000 && EnemySpeed>20.0f);
		}
		ucmd.buttons	&=~BUTTON_VEH_SPEED;
	}




// APPLY RESULTS
//=======================
	// Decide Turbo
	//--------------
	if (ActorDoTurbo || ActorInTurbo)
	{
		ucmd.buttons |= BUTTON_ALT_ATTACK;
	}
	else
	{
		ucmd.buttons &=~BUTTON_ALT_ATTACK;
	}

	// Decide Acceleration
	//---------------------
	ucmd.forwardmove = (ActorAccelerate)?(127):(0);



	// Decide To Shoot
	//-----------------
	ucmd.buttons	&=~BUTTON_ATTACK;
	ucmd.rightmove	= 0;
 	if (AimDistance<2000 && !EnemyDead)
	{
		// If Doing A Ram Attack
		//-----------------------
		if (ActorYawOffset!=0)
		{
			if (NPC->client->ps.weapon!=WP_NONE)
			{
				NPC_ChangeWeapon(WP_NONE);
			}
			ucmd.buttons	&=~BUTTON_ATTACK;
		}
 		else if (AimAccuracy>ATTACK_FWD)
		{
			if (NPC->client->ps.weapon!=WP_NONE)
			{
				NPC_ChangeWeapon(WP_NONE);
			}
			ucmd.buttons	|= BUTTON_ATTACK;
		}
		else if (AimAccuracy<AIM_SIDE && AimAccuracy>-AIM_SIDE)
		{
			if (NPC->client->ps.weapon!=WP_BLASTER)
			{
				NPC_ChangeWeapon(WP_BLASTER);
			}

			if (AimAccuracy<ATTACK_SIDE && AimAccuracy>-ATTACK_SIDE)
			{
				//if (!TIMER_Done(NPC, "RiderAltAttack"))
				//{
				//	ucmd.buttons |= BUTTON_ALT_ATTACK;
				//}
				//else
				//{
                    ucmd.buttons |= BUTTON_ATTACK;

			/*		if (TIMER_Done(NPC, "RiderAltAttackCheck"))
					{
						TIMER_Set(NPC, "RiderAltAttackCheck", Q_irand(1000, 3000));
						if (Q_irand(0, 2)==0)
						{
							TIMER_Set(NPC, "RiderAltAttack", 300);
						}
					}*/
				//}
				WeaponThink(true);
			}
			ucmd.rightmove = (EnemySide==Side_Left)?( 127):(-127);
		}
		else
		{
			if (NPC->client->ps.weapon!=WP_NONE)
			{
				NPC_ChangeWeapon(WP_NONE);
			}
		}
	}
	else
	{
		if (NPC->client->ps.weapon!=WP_NONE)
		{
			NPC_ChangeWeapon(WP_NONE);
		}
	}


	// Aim At Target
	//---------------
	if (ActorAimAtTarget)
	{
		MoveDirection.VecToAng();
		NPCInfo->desiredPitch	= AngleNormalize360(MoveDirection[PITCH]);
		NPCInfo->desiredYaw		= AngleNormalize360(MoveDirection[YAW] + ActorYawOffset);
	}
	NPC_UpdateAngles(qtrue, qtrue);
}
void CShapeRenderer::DrawAABB(const CAABBBox& aabb, CColor color)
{
    CVec3 tmin = aabb.m_minPos;
    CVec3 tmax = aabb.m_maxPos;
    CVec3 sub = tmax - tmin;
    CVertexPC startPos, endPos;
    startPos.color = color;
    endPos.color = color;
    startPos.position = tmin;
    endPos.position = CVec3(tmin.X(), tmin.Y(), tmin.Z() + sub.Z() * 0.2F);// Min
    CRenderManager::GetInstance()->RenderLine(startPos, endPos, 1.0f, true);
    endPos.position = CVec3(tmin.X(), tmin.Y() + sub.Y() * 0.2F, tmin.Z());
    CRenderManager::GetInstance()->RenderLine(startPos, endPos, 1.0f, true);
    endPos.position = CVec3(tmin.X() + sub.X() * 0.2F, tmin.Y(), tmin.Z());
    CRenderManager::GetInstance()->RenderLine(startPos, endPos, 1.0f, true);

    startPos.position = tmax;
    endPos.position = CVec3(tmax.X(), tmax.Y(), tmax.Z() - sub.Z() * 0.2F);// Max
    CRenderManager::GetInstance()->RenderLine(startPos, endPos, 1.0f, true);
    endPos.position = CVec3(tmax.X(), tmax.Y() - sub.Y() * 0.2F, tmax.Z());
    CRenderManager::GetInstance()->RenderLine(startPos, endPos, 1.0f, true);
    endPos.position = CVec3(tmax.X() - sub.X() * 0.2F, tmax.Y(), tmax.Z());
    CRenderManager::GetInstance()->RenderLine(startPos, endPos, 1.0f, true);

    startPos.position = CVec3(tmin.X(), tmin.Y(), tmax.Z());// x min, y min, z max
    endPos.position = CVec3(tmin.X(), tmin.Y(), tmax.Z() - sub.Z() * 0.2F);
    CRenderManager::GetInstance()->RenderLine(startPos, endPos, 1.0f, true);
    endPos.position = CVec3(tmin.X(), tmin.Y() + sub.Y() * 0.2F, tmax.Z());
    CRenderManager::GetInstance()->RenderLine(startPos, endPos, 1.0f, true);
    endPos.position = CVec3(tmin.X() + sub.X() * 0.2F, tmin.Y(), tmax.Z());
    CRenderManager::GetInstance()->RenderLine(startPos, endPos, 1.0f, true);

    startPos.position = CVec3(tmin.X(), tmax.Y(), tmin.Z());// x min, y max, z min
    endPos.position = CVec3(tmin.X(), tmax.Y(), tmin.Z() + sub.Z() * 0.2F);
    CRenderManager::GetInstance()->RenderLine(startPos, endPos, 1.0f, true);
    endPos.position = CVec3(tmin.X(), tmax.Y() - sub.Y() * 0.2F, tmin.Z());
    CRenderManager::GetInstance()->RenderLine(startPos, endPos, 1.0f, true);
    endPos.position = CVec3(tmin.X() + sub.X() * 0.2F, tmax.Y(), tmin.Z());
    CRenderManager::GetInstance()->RenderLine(startPos, endPos, 1.0f, true);

    startPos.position = CVec3(tmax.X(), tmin.Y(), tmin.Z());// x max, y min, z min
    endPos.position = CVec3(tmax.X(), tmin.Y(), tmin.Z() + sub.Z() * 0.2F);
    CRenderManager::GetInstance()->RenderLine(startPos, endPos, 1.0f, true);
    endPos.position = CVec3(tmax.X(), tmin.Y() + sub.Y() * 0.2F, tmin.Z());
    CRenderManager::GetInstance()->RenderLine(startPos, endPos, 1.0f, true);
    endPos.position = CVec3(tmax.X() - sub.X() * 0.2F, tmin.Y(), tmin.Z());
    CRenderManager::GetInstance()->RenderLine(startPos, endPos, 1.0f, true);

    startPos.position = CVec3(tmin.X(), tmax.Y(), tmax.Z());// x min, y max, z max
    endPos.position = CVec3(tmin.X(), tmax.Y(), tmax.Z() - sub.Z() * 0.2F);
    CRenderManager::GetInstance()->RenderLine(startPos, endPos, 1.0f, true);
    endPos.position = CVec3(tmin.X(), tmax.Y() - sub.Y() * 0.2F, tmax.Z());
    CRenderManager::GetInstance()->RenderLine(startPos, endPos, 1.0f, true);
    endPos.position = CVec3(tmin.X() + sub.X() * 0.2F, tmax.Y(), tmax.Z());
    CRenderManager::GetInstance()->RenderLine(startPos, endPos, 1.0f, true);

    startPos.position = CVec3(tmax.X(), tmax.Y(), tmin.Z());// x min, y max, z max
    endPos.position = CVec3(tmax.X(), tmax.Y(), tmin.Z() + sub.Z() * 0.2F);
    CRenderManager::GetInstance()->RenderLine(startPos, endPos, 1.0f, true);
    endPos.position = CVec3(tmax.X(), tmax.Y() - sub.Y() * 0.2F, tmin.Z());
    CRenderManager::GetInstance()->RenderLine(startPos, endPos, 1.0f, true);
    endPos.position = CVec3(tmax.X() - sub.X() * 0.2F, tmax.Y(), tmin.Z());
    CRenderManager::GetInstance()->RenderLine(startPos, endPos, 1.0f, true);

    startPos.position = CVec3(tmax.X(), tmin.Y(), tmax.Z());// x max, y min, z max
    endPos.position = CVec3(tmax.X(), tmin.Y(), tmax.Z() - sub.Z() * 0.2F);
    CRenderManager::GetInstance()->RenderLine(startPos, endPos, 1.0f, true);
    endPos.position = CVec3(tmax.X(), tmin.Y() + sub.Y() * 0.2F, tmax.Z());
    CRenderManager::GetInstance()->RenderLine(startPos, endPos, 1.0f, true);
    endPos.position = CVec3(tmax.X() - sub.X() * 0.2F, tmin.Y(), tmax.Z());
    CRenderManager::GetInstance()->RenderLine(startPos, endPos, 1.0f, true);
}