Exemple #1
0
//========================================================================================
//	geCamera_ScreenPointToWorld
//========================================================================================
GENESISAPI void GENESISCC geCamera_ScreenPointToWorld (	const geCamera	*Camera,
														int32			 ScreenX,
														int32			 ScreenY,
														geVec3d			*Vector)
// Takes a screen X and Y pair, and a camera and generates a vector pointing
// in the direction from the camera position to the screen point.
{
	geVec3d In,Left,Up;
	geVec3d ScaledIn,ScaledLeft,ScaledUp ;
	geFloat	XCenter ;
	geFloat	YCenter ;
	geFloat	Scale ;
	const geXForm3d *pM;

	pM = &(Camera->TransposeXForm);
	XCenter = Camera->XCenter ;
	YCenter = Camera->YCenter ;
	Scale   = Camera->Scale ;

	geXForm3d_GetIn( pM, &In ) ;
	geXForm3d_GetLeft( pM, &Left ) ;
	geXForm3d_GetUp( pM, &Up ) ;
	
	geVec3d_Scale(&In,   Scale, &ScaledIn);
	geVec3d_Scale(&Left, XCenter - ((geFloat)ScreenX), &ScaledLeft );
	geVec3d_Scale(&Up,   YCenter - ((geFloat)ScreenY), &ScaledUp   );

	geVec3d_Copy(&ScaledIn, Vector);
	geVec3d_Add(Vector,		&ScaledLeft,	Vector );
	geVec3d_Add(Vector,		&ScaledUp,		Vector );
	geVec3d_Normalize(Vector);
}
/* ------------------------------------------------------------------------------------ */
CDSpotLight::CDSpotLight()
{
	geEntity_EntitySet *pSet;
	geEntity *pEntity;

	pSet = geWorld_GetEntitySet(CCD->World(), "DSpotLight");

	if(!pSet)
		return;						// None there.

	// Ok, we have dynamic lights somewhere.  Dig through 'em all.
	for(pEntity=geEntity_EntitySetGetNextEntity(pSet, NULL); pEntity;
	    pEntity=geEntity_EntitySetGetNextEntity(pSet, pEntity))
	{
		DSpotLight *pLight = (DSpotLight*)geEntity_GetUserData(pEntity);
		pLight->RadiusSpeed *= 1000.0f;			// From SECONDS to MILLISECONDS

		if(EffectC_IsStringNull(pLight->szEntityName))
		{
			char szName[128];
			geEntity_GetName(pEntity, szName, 128);
			pLight->szEntityName = szName;
		}

		// Ok, put this entity into the Global Entity Registry
		CCD->EntityRegistry()->AddEntity(pLight->szEntityName, "DSpotLight");

		// "negative light" shadow
		if(pLight->Shadow)
		{
			pLight->Color.r = -pLight->Color.r;
			pLight->Color.g = -pLight->Color.g;
			pLight->Color.b = -pLight->Color.b;
		}

		pLight->OriginOffset = pLight->origin;
		geVec3d_Scale(&(pLight->angles), GE_PIOVER180, &(pLight->RealAngle));

        if(pLight->Model)
		{
            geVec3d ModelOrigin;
	    	geWorld_GetModelRotationalCenter(CCD->World(), pLight->Model, &ModelOrigin);
            geVec3d_Subtract(&(pLight->origin), &ModelOrigin, &(pLight->OriginOffset));
  		}

		pLight->active = GE_FALSE;
		pLight->NumFunctionValues = strlen(pLight->RadiusFunction);

		if(pLight->NumFunctionValues == 0)
		{
			CCD->ReportError("[WARNING] DSpotLight has no RadiusFunction string\n", false);
			continue;
		}

		pLight->IntervalWidth = pLight->RadiusSpeed / (geFloat)pLight->NumFunctionValues;
	}
}
Exemple #3
0
//=====================================================================================
//	LeafCenter
//=====================================================================================
void LeafCenter(GBSP_Node *Node, geVec3d *PortalMid)
{
	int32		NumPortals, Side;
	geVec3d		Mid;
	GBSP_Portal	*Portal;

	NumPortals = 0;
	geVec3d_Clear(PortalMid);

	for (Portal = Node->Portals; Portal; Portal = Portal->Next[Side])
	{
		Side = Portal->Nodes[1] == Node;

		PolyCenter(Portal->Poly, &Mid);
		geVec3d_Add(PortalMid, &Mid, PortalMid);
		NumPortals++;
	}

	geVec3d_Scale(PortalMid, 1.0f/(geFloat)NumPortals, PortalMid);
}
void CMainFrame::OnUpdateWorldPos(CCmdUI *pCmdUI)
{
	CPoint		CursorPos;
	char		stuff[100];
	CFusionView * pView ;
	CWnd		* pWnd ; 
	geVec3d		wp ;

	pCmdUI->Enable() ;
	GetCursorPos(&CursorPos);
	pWnd = WindowFromPoint( CursorPos );

	if( pWnd != NULL )
	{
		if( pWnd->IsKindOf( RUNTIME_CLASS (CFusionView)) )
		{
			pView = (CFusionView *)pWnd ;
			if( pView->mViewType == ID_VIEW_TOPVIEW ||
				pView->mViewType == ID_VIEW_FRONTVIEW ||
				pView->mViewType == ID_VIEW_SIDEVIEW )
			{
				pView->ScreenToClient( &CursorPos ) ; 
				Render_ViewToWorld( pView->VCam, CursorPos.x, CursorPos.y, &wp ) ;

				CFusionDoc	*pDoc	=pView->GetDocument();
				if(pDoc)
				{
					if (Level_GetGridType (pDoc->pLevel) == GridMetric)
					{
						geVec3d_Scale (&wp, Units_EngineToCentimeters (1.0f), &wp);
					}
				}
				sprintf( stuff, "% 4d,% 4d,% 4d", (int)wp.X, (int)wp.Y, (int)wp.Z );
				pCmdUI->SetText( stuff ) ;
				return ;
			}
		}
	}

	m_wndStatusBar.SetPaneText( ID_WORLDPOS_PANE, "" );
}
Exemple #5
0
GENESISAPI gePhysicsJoint * GENESISCC gePhysicsJoint_Create(gePhysicsJoint_Kind Kind, const geVec3d *Location, 
	geFloat assemblyRate, gePhysicsObject *PS1, gePhysicsObject *PS2, geFloat physicsScale)
{
	gePhysicsJoint*	pPhysjnt;
	geVec3d		POLocation;
	geVec3d		physicsSpaceLocation;

	pPhysjnt = NULL;
	pPhysjnt = GE_RAM_ALLOCATE_STRUCT(gePhysicsJoint);
	if (pPhysjnt == NULL)
	{
		return NULL;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// make sure the joint makes sense

	pPhysjnt->Type = Kind;
	pPhysjnt->assemblyRate = assemblyRate * JOINT_ASSEMBLY_RATE_MULTIPLIER;
	pPhysjnt->Object1 = PS1;
	pPhysjnt->Object2 = PS2;

	geVec3d_Scale(Location, physicsScale, &physicsSpaceLocation);

	switch (Kind)
	{
		case JT_WORLD:
			if (PS1 == NULL)
			{
				/*
				GenVSI_Error(VSI, 
					GE_FALSE, 
					"Joint_Spawn: World joint needs non-NULL gePhysicsObject1 field.\n");
				*/
				return NULL;
			}
			#if 0
			if (pJoint->Next == pJoint)
			{
				/*
				GenVSI_Error(VSI, 
					GE_FALSE, 
					"Joint_Spawn: Next field points to parent.\n");
				*/
				return NULL;
			}
			#endif
			
			gePhysicsObject_GetLocation(PS1, &POLocation, 0);

			geVec3d_Subtract(&physicsSpaceLocation,
				&POLocation,
				&pPhysjnt->locationA);
			geVec3d_Copy(&physicsSpaceLocation, &pPhysjnt->locationB);
			break;

		case JT_SPHERICAL:
			if (PS1 == NULL || PS2 == NULL)
			{
				/*
				GenVSI_Error(VSI, 
					GE_FALSE, 
					"Joint_Spawn: Spherical joint needs 2 non-NULL gePhysicsObjects.\n");
				*/
				return NULL;
			}
			#if 0
			if (pJoint->Next == pJoint)
			{
				/*
				GenVSI_Error(VSI, 
					GE_FALSE, 
					"Joint_Spawn: Next field points to parent.\n");
				*/
			}
			#endif
			if (PS1 == PS2)
			{
				/*
				GenVSI_Error(VSI, 
					GE_FALSE, 
					"Joint_Spawn: Spherical joint: need 2 distinct gePhysicsObjects.\n");
				*/
				return NULL;
			}

			gePhysicsObject_GetLocation(PS1, &POLocation, 0);
			geVec3d_Subtract(&physicsSpaceLocation,
				&POLocation,
				&pPhysjnt->locationA);

			gePhysicsObject_GetLocation(PS2, &POLocation, 0);
			geVec3d_Subtract(&physicsSpaceLocation,
				&POLocation,
				&pPhysjnt->locationB);
			break;

		default:
			/*
			GenVSI_Error(VSI, 
				GE_FALSE, 
				"Joint_Spawn: unsupported joint type %d.\n", ij->jointType);
			*/
			return NULL;
	}

	return pPhysjnt;
}
Exemple #6
0
//=====================================================================================
//	RenderTexturedPoint
//=====================================================================================
static geBoolean RenderTexturedPoint(DRV_Driver *RDriver, gePoly *Poly, Frustum_Info *FInfo, geCamera *Camera)
{
	assert(geWorld_PolyIsValid(Poly));

	if (MirrorRecursion > 0)
	{
		GE_LVertex		*pVerts, Save;
		geVec3d			Up, Left, Start;
		geFloat			Scale, XScale, YScale;
		const geXForm3d	*MXForm;

		pVerts = Poly->Verts;

		Poly->NumVerts = 4;

		Start.X = pVerts[0].X;
		Start.Y = pVerts[0].Y;
		Start.Z = pVerts[0].Z;

		Save = pVerts[1] = pVerts[2] = pVerts[3] = pVerts[0];

		MXForm = geCamera_GetWorldSpaceXForm(Camera);

		geXForm3d_GetLeft(MXForm, &Left);
		geXForm3d_GetUp(MXForm, &Up);

		Scale = Poly->Scale * 0.5f;

		XScale = (geFloat)geBitmap_Width(Poly->Bitmap) * Scale;
		YScale = (geFloat)geBitmap_Height(Poly->Bitmap) * Scale;

		geVec3d_Scale(&Left, XScale, &Left);
		geVec3d_Scale(&Up, YScale, &Up);

		pVerts->X = Start.X - Left.X + Up.X;
		pVerts->Y = Start.Y - Left.Y + Up.Y;
		pVerts->Z = Start.Z - Left.Z + Up.Z;
		pVerts->u = 0.0f;
		pVerts->v = 0.0f;

		pVerts++;
	
		pVerts->X = Start.X + Left.X + Up.X;
		pVerts->Y = Start.Y + Left.Y + Up.Y;
		pVerts->Z = Start.Z + Left.Z + Up.Z;
		pVerts->u = 1.0f;
		pVerts->v = 0.0f;
	
		pVerts++;
	
		pVerts->X = Start.X + Left.X - Up.X;
		pVerts->Y = Start.Y + Left.Y - Up.Y;
		pVerts->Z = Start.Z + Left.Z - Up.Z;
		pVerts->u = 1.0f;
		pVerts->v = 1.0f;

		pVerts++;
	
		pVerts->X = Start.X - Left.X - Up.X;
		pVerts->Y = Start.Y - Left.Y - Up.Y;
		pVerts->Z = Start.Z - Left.Z - Up.Z;
		pVerts->u = 1.0f;
		pVerts->v = 0.0f;

		RenderTexturedPoly(RDriver, Poly, FInfo, Camera);

		Poly->NumVerts = 1;		// Restore the poly
		Poly->Verts[0] = Save;
	}
	else
	{
		//GFX_Plane		*Planes;
		geVec3d			Src, Dest;
		GE_LVertex		*pVerts;
		DRV_TLVertex	ScreenPnts[4];
		geBitmap		*Bitmap;
		geFloat			Sx, Sy, z, UVAdd, Width, Height;
		geFloat			Left, Right, Top, Bottom;
		geFloat			Scale;
		uint32			RenderFlags;
		int32			i;
		
		assert(Poly != NULL);
		assert(Camera != NULL);

		pVerts = &Poly->Verts[0];
	
		// Xform the point 
		Src.X = pVerts->X;
		Src.Y = pVerts->Y;
		Src.Z = pVerts->Z;

		geCamera_Transform(Camera,&Src,&Dest);

		geCamera_Project(Camera, &Dest, &Src);

		for (i=0; i<4; i++)
		{
			ScreenPnts[i].x = Src.X;
			ScreenPnts[i].y = Src.Y;
			ScreenPnts[i].z = Src.Z;
			ScreenPnts[i].r = pVerts->r;
			ScreenPnts[i].g = pVerts->g;
			ScreenPnts[i].b = pVerts->b;
			ScreenPnts[i].a = pVerts->a;
		}
	
		z = -Dest.Z;

		if (z < 1)
			return GE_TRUE;

		{
			geRect Rect;
			geCamera_GetClippingRect(Camera,&Rect);

			Left   = (geFloat)Rect.Left;
			Right  = (geFloat)Rect.Right+1.0f;
			Top    = (geFloat)Rect.Top;
			Bottom = (geFloat)Rect.Bottom+1.0f;
		}

		Scale = ((geCamera_GetScale(Camera) / z) * Poly->Scale);

		Bitmap = Poly->Bitmap;
		Width = (geFloat)geBitmap_Width(Bitmap) * Scale;
		Height = (geFloat)geBitmap_Height(Bitmap) * Scale;

		Sx = Width * 0.5f;
		Sy = Height * 0.5f;

		// Build the screen poly from the point
		ScreenPnts[0].x -= Sx;
		ScreenPnts[0].y -= Sy;

		ScreenPnts[1].x += Sx;
		ScreenPnts[1].y -= Sy;

		ScreenPnts[2].x += Sx;
		ScreenPnts[2].y += Sy;

		ScreenPnts[3].x -= Sx;
		ScreenPnts[3].y += Sy;

		ScreenPnts[0].u = 0.0f + pVerts->u;
		ScreenPnts[0].v = 0.0f + pVerts->v;
		ScreenPnts[1].u = 1.0f + pVerts->u;
		ScreenPnts[1].v = 0.0f + pVerts->v;
		ScreenPnts[2].u = 1.0f + pVerts->u;
		ScreenPnts[2].v = 1.0f + pVerts->v;
		ScreenPnts[3].u = 0.0f + pVerts->u;
		ScreenPnts[3].v = 1.0f + pVerts->v;

		// Now, clip it against the 2d camera viewport
		if (ScreenPnts[0].x < Left)
		{
			if (ScreenPnts[1].x <= Left)
				return GE_TRUE;

			UVAdd = (Left-ScreenPnts[0].x) / Width;
			Width -= Left-ScreenPnts[0].x;
		
			ScreenPnts[0].u += UVAdd;
			ScreenPnts[3].u += UVAdd;

			ScreenPnts[0].x = Left;
			ScreenPnts[3].x = Left;
		}
		if (ScreenPnts[0].y < Top)
		{
			if (ScreenPnts[2].y <= Top)
				return GE_TRUE;

			UVAdd = (Top-ScreenPnts[0].y) / Height;
			Height -= (Top-ScreenPnts[0].y);
		
			ScreenPnts[0].v += UVAdd;
			ScreenPnts[1].v += UVAdd;

			ScreenPnts[0].y = Top;
			ScreenPnts[1].y = Top;
		}
		if (ScreenPnts[1].x >= Right)
		{
			if (ScreenPnts[0].x >= Right)
				return GE_TRUE;
	
			UVAdd = (ScreenPnts[1].x-Right) / Width;
			Width -= (ScreenPnts[1].x-Right);
		
			ScreenPnts[1].u -= UVAdd;
			ScreenPnts[2].u -= UVAdd;
		
			ScreenPnts[1].x	= Right-1;
			ScreenPnts[2].x	= Right-1;
		}
		if (ScreenPnts[2].y >= Bottom)
		{
			if (ScreenPnts[0].y >= Bottom)
				return GE_TRUE;

			UVAdd = (ScreenPnts[2].y-Bottom) / Height;
			Height -= (ScreenPnts[2].x-Bottom);
		
			ScreenPnts[2].v -= UVAdd;
			ScreenPnts[3].v -= UVAdd;

			ScreenPnts[2].y	= Bottom-1;
			ScreenPnts[3].y	= Bottom-1;
		}

		// Lastly, render it...
		ScreenPnts[0].a = pVerts->a;
		// Fixed bug where i fogot to set RGB's...
		ScreenPnts[0].r = pVerts->r;
		ScreenPnts[0].g = pVerts->g;
		ScreenPnts[0].b = pVerts->b;

		if (Poly->RenderFlags & GE_RENDER_DO_NOT_OCCLUDE_OTHERS)
			RenderFlags = DRV_RENDER_NO_ZWRITE;
		else
			RenderFlags = 0;

		if (Poly->RenderFlags & GE_RENDER_DO_NOT_OCCLUDE_SELF)
			RenderFlags |= DRV_RENDER_NO_ZMASK;

		if (pVerts->a != 255.0f)
			RenderFlags |= DRV_RENDER_ALPHA;

/* 01/30/2003 Wendell Buckner 
     Driver render flush is probably causing a slow down! */
		if (Poly->RenderFlags & GE_RENDER_DEPTH_SORT_BF)    
			RenderFlags |= DRV_RENDER_FLUSH;                 

		if (Poly->RenderFlags & GE_RENDER_CLAMP_UV)
			RenderFlags |= DRV_RENDER_CLAMP_UV;

		if (Poly->RenderFlags & GE_RENDER_NO_FOG) // skybox fog
			RenderFlags |= DRV_RENDER_POLY_NO_FOG;


		assert(geWorld_HasBitmap(gWorld, Bitmap));
		assert(geBitmap_GetTHandle(Bitmap));

/* 08/08/2004 Wendell Buckner
     BUG FIX: Allways call geBitmap_SetRenderFlags() before calling a textured rendering function(for embm, dot3, & etc...). */
		geBitmap_SetRenderFlags(Bitmap, &RenderFlags);

		RDriver->RenderMiscTexturePoly((DRV_TLVertex*)ScreenPnts, 4, geBitmap_GetTHandle(Bitmap), RenderFlags);
	}

	return GE_TRUE;
}
/* ------------------------------------------------------------------------------------ */
geBoolean CDSpotLight::Tick(geFloat dwTicks)
{
	geEntity_EntitySet *pSet;
	geEntity *pEntity;

	if(CCD->World() == NULL)
		return GE_TRUE;

	pSet = geWorld_GetEntitySet(CCD->World(), "DSpotLight");

	if(!pSet)
		return GE_TRUE;

	for(pEntity=geEntity_EntitySetGetNextEntity(pSet, NULL); pEntity;
		pEntity=geEntity_EntitySetGetNextEntity(pSet, pEntity))
	{
		DSpotLight *Light;
		geFloat		Radius;
		geFloat		Percentage;
		int			Index;
		geVec3d		Pos;
		int32		Leaf;

		Light = (DSpotLight*)geEntity_GetUserData(pEntity);

		if(!EffectC_IsStringNull(Light->TriggerName))
		{
			if(GetTriggerState(Light->TriggerName))
			{
				if(Light->active == GE_FALSE)
				{
					Light->DynLight = geWorld_AddLight(CCD->World());
					Light->active = GE_TRUE;
				}
			}
			else
			{
				if(Light->active == GE_TRUE)
					geWorld_RemoveLight(CCD->World(), Light->DynLight);

				Light->active = GE_FALSE;
			}
		}
		else
		{
			if(Light->active == GE_FALSE)
			{
				Light->DynLight = geWorld_AddLight(CCD->World());
				Light->active = GE_TRUE;
			//	CCD->ReportError("DSpotLight light added", false);
			}
		}

		if(Light->active == GE_TRUE)
		{
			// pass the OriginOffset to SetOrigin
            // so that the light will stay in the same position relative to the model.
            Light->origin = Light->OriginOffset;
			SetOriginOffset(Light->EntityName, Light->BoneName, Light->Model, &(Light->origin));
			geWorld_GetLeaf(CCD->World(), &(Light->origin), &Leaf);
			Pos = Light->origin;

			if(Light->Rotate)
			{
				if(Light->Model)
				{
					geXForm3d Xf;
					geVec3d  Tmp;
					geWorld_GetModelXForm(CCD->World(), Light->Model, &Xf);
					geXForm3d_GetEulerAngles(&Xf, &Tmp);
					geVec3d_Add(&(Light->RealAngle), &Tmp, &(Light->angles));

					geVec3d_Scale(&(Light->angles), GE_180OVERPI, &(Light->angles));
				}
				else if(!EffectC_IsStringNull(Light->EntityName))
				{
					// changed QD 07/15/06
					/*
					geXForm3d BoneXForm;
					geActor	*theActor;

					theActor = GetEntityActor(Light->EntityName);

					if(!EffectC_IsStringNull(Light->BoneName))
					{
						if(geActor_GetBoneTransform(theActor, Light->BoneName, &BoneXForm) != GE_TRUE)
							continue;					// No such bone
					}
					else
					{
						if(geActor_GetBoneTransform(theActor, RootBoneName(theActor), &BoneXForm) != GE_TRUE)
							continue;					// No such bone
					}

					geXForm3d_RotateY(&BoneXForm, Light->RealAngle.Y);
					geXForm3d_GetEulerAngles(&BoneXForm, &(Light->angles));
					Light->angles.Z += Light->RealAngle.Z;

					*/
					SetAngle(Light->EntityName, Light->BoneName, &(Light->angles));

					if(Light->RealAngle.X || Light->RealAngle.Y || Light->RealAngle.Z)
					{
						geXForm3d XForm, BoneXForm;

						geXForm3d_SetZRotation(&XForm, Light->RealAngle.Z);
						geXForm3d_RotateX(&XForm, Light->RealAngle.X);
						geXForm3d_RotateY(&XForm, Light->RealAngle.Y);

						geXForm3d_SetZRotation(&BoneXForm, Light->angles.Z);
						geXForm3d_RotateX(&BoneXForm, Light->angles.X);
						geXForm3d_RotateY(&BoneXForm, Light->angles.Y);

						geXForm3d_Multiply(&BoneXForm, &XForm, &XForm);
						geXForm3d_GetEulerAngles(&XForm, &(Light->angles));
					}
					// end change QD 07/15/06

					// convert to degrees
					geVec3d_Scale(&(Light->angles), GE_180OVERPI, &(Light->angles));

				}
			}


			Percentage = Light->LastTime / Light->RadiusSpeed;

			Index = (int)(Percentage * Light->NumFunctionValues);

			if(Light->InterpolateValues && Index < Light->NumFunctionValues - 1)
			{
				geFloat	Remainder;
				geFloat	InterpolationPercentage;
				int		DeltaValue;
				geFloat	Value;

				Remainder = (geFloat)fmod(Light->LastTime, Light->IntervalWidth);
				InterpolationPercentage = Remainder / Light->IntervalWidth;
				DeltaValue = Light->RadiusFunction[Index + 1] - Light->RadiusFunction[Index];
				Value = Light->RadiusFunction[Index] + DeltaValue * InterpolationPercentage;
				Percentage = ((geFloat)(Value - 'a')) / ((geFloat)('z' - 'a'));
			}
			else
				Percentage = ((geFloat)(Light->RadiusFunction[Index] - 'a')) / ((geFloat)('z' - 'a'));

			Radius = Percentage * (Light->MaxRadius - Light->MinRadius) + Light->MinRadius;

			// angles in degrees
			geWorld_SetSpotLightAttributes(CCD->World(),
										   Light->DynLight,
										   &Pos,
										   &(Light->Color),
										   Radius,
										   Light->arc,
										   &(Light->angles),
										   Light->style,
										   Light->CastShadows);

			Light->LastTime = (geFloat)fmod(Light->LastTime + dwTicks, Light->RadiusSpeed);

			if(EffectC_IsPointVisible(CCD->World(),
					CCD->CameraManager()->Camera(),
					&Pos,
					Leaf,
					EFFECTC_CLIP_LEAF ) == GE_FALSE)
			{
				geWorld_RemoveLight(CCD->World(), Light->DynLight);
				Light->active = GE_FALSE;
			}
		}
	}

	return GE_TRUE;
}
/* ------------------------------------------------------------------------------------ */
int CPathFollower::GetNextPosition(const char *szEntityName, geVec3d *NextPosition,
								   bool YLocked)
{
	geEntity_EntitySet *pSet;
	geEntity *pEntity;
	int nResult;
	geVec3d thePos, temp, DirectionVector;

	// Ok, check to see if there are path followers in this world
	pSet = geWorld_GetEntitySet(CCD->World(), "PathFollower");

	if(!pSet)
		return RGF_FAILURE;									// No path followers

	// Ok, dig through 'em all.
	for(pEntity=geEntity_EntitySetGetNextEntity(pSet, NULL); pEntity;
		pEntity=geEntity_EntitySetGetNextEntity(pSet, pEntity))
	{
		PathFollower *pFollower = (PathFollower*)geEntity_GetUserData(pEntity);

		if(strcmp(pFollower->EntityName, szEntityName) != 0)
			continue;										// Keep looking

		// Ok, it's this entity.  Let's set the return position to the
		// ..current position in case we aren't moving
		*NextPosition = pFollower->CurrentPosition;

		// Compute the # of milliseconds since we last moved, then use that
		// ..to compute the distance we've travelled in that period
		geFloat nMotionTime = (CCD->FreeRunningCounter_F() - pFollower->LastMotionTick);

		if(nMotionTime > 100.0f)
			nMotionTime = 100.0f;							// In case of stalls elsewhere

		geFloat fDistance = nMotionTime * (pFollower->Speed * 0.001f); // / 1000.0f);
		pFollower->TimeInMotion += nMotionTime;
		pFollower->LastMotionTick = CCD->FreeRunningCounter_F();

		if(pFollower->bMoving == GE_FALSE)
			return RGF_SUCCESS;								// Current position is good

		if(pFollower->bReady == GE_FALSE)
			return RGF_SUCCESS;								// Disabled, use current position

		switch(pFollower->CurrentNodeType)
		{
		// For both start and waypoints, we're going to compute the new
		// position the same way (since they're both target-based motion).
		case RGF_POINT_TYPE_START:
		case RGF_POINT_TYPE_WAY:
			// Ok, we're moving.  What we need to do is perform a new position
			// ..computation based on the entities speed on the path and the
			// ..vector towards the current target, if not a ranged point.  First,
			// ..compute the direction vector towards the current target
			geVec3d_Subtract(&pFollower->CurrentTarget, &pFollower->CurrentPosition, &DirectionVector);
			geVec3d_Normalize(&DirectionVector);
			geVec3d_Scale(&DirectionVector, fDistance, &DirectionVector);
			// Fine, DirectionVector now contains a displacement from the current
			// ..position of the bound entity towards the target node.
			if(YLocked)
				DirectionVector.Y = 0.0f;			// Stomp on Y vector value
			temp.X = pFollower->CurrentPosition.X + DirectionVector.X;
			temp.Y = pFollower->CurrentPosition.Y + DirectionVector.Y;
			temp.Z = pFollower->CurrentPosition.Z + DirectionVector.Z;
			break;
		case RGF_POINT_TYPE_RANGED:
			// Ranged points are somewhat different.  We're basically constraining
			// ..random motion to a sphere (if not YLocked) of space in which the
			// ..bound entity randomly moves about.  What we'll do is randomly
			// ..pick a target point in the range sphere every <n> seconds and
			// ..have the entity move towards that point.
			if(pFollower->TimeInMotion > pFollower->RandomRollTime)
			{
				// Compute a new random target in the range sphere.
				srand(CCD->FreeRunningCounter());
				geVec3d RandomPosition;
				RandomPosition.X = (geFloat)(rand() % (int)pFollower->PointRange);
				if(rand()%2 == 1)
					RandomPosition.X = -(RandomPosition.X);

				RandomPosition.Y = (geFloat)(rand() % (int)pFollower->PointRange);
				if(rand()%2 == 1)
					RandomPosition.Y = -(RandomPosition.Y);

				RandomPosition.Z = (geFloat)(rand() % (int)pFollower->PointRange);
				if(rand()%2 == 1)
					RandomPosition.Z = -(RandomPosition.Z);

				RandomPosition.X += pFollower->PathOrigin.X;
				RandomPosition.Y += pFollower->PathOrigin.Y;
				RandomPosition.Z += pFollower->PathOrigin.Z;

				pFollower->CurrentTarget = RandomPosition;
				pFollower->TimeInMotion = 0.0f;				// Clear re-seek time
			}
			// From here on in, the code is identical to the target-seeking
			// ..computations performed for way an start points.
			geVec3d_Subtract(&pFollower->CurrentTarget, &pFollower->CurrentPosition, &DirectionVector);
			geVec3d_Normalize(&DirectionVector);
			geVec3d_Scale(&DirectionVector, fDistance, &DirectionVector);
			// Fine, DirectionVector now contains a displacement from the current
			// ..position of the bound entity towards the target node.
			if(YLocked)
				DirectionVector.Y = 0.0f;			// Stomp on Y vector value
			temp.X = pFollower->CurrentPosition.X + DirectionVector.X;
			temp.Y = pFollower->CurrentPosition.Y + DirectionVector.Y;
			temp.Z = pFollower->CurrentPosition.Z + DirectionVector.Z;
			break;
		}

		// Ok, we have a new point to move to!  Let's do a basic ray
		// ..collision test so we can first-level cull any motion
		if(pFollower->CollisionTest == GE_TRUE)
		{
			nResult = CCD->Collision()->CheckForCollision(NULL, NULL,
									pFollower->CurrentPosition,	temp);

			if(nResult)
				return RGF_SUCCESS;						// Don't move, we collided.
		}

		// Fine, no collisions.  Let's check to see if (a) we're not on a
		// ..ranged point and (b) if we're within 1 world unit of the target
		// ..point.  If we pass those tests, we will reset the target to the
		// ..next target point.
		*NextPosition = temp;						// Copy out for use
		pFollower->CurrentPosition = *NextPosition;

		if(pFollower->CurrentNodeType == RGF_POINT_TYPE_RANGED)
			return RGF_SUCCESS;						// Done all we need to do, bail this.

		if(geVec3d_DistanceBetween(&pFollower->CurrentPosition, &pFollower->CurrentTarget) <= pFollower->Speed)
		{
			// Move to the NEXT path point, based on our direction
			if(pFollower->bForward == GE_TRUE)
			{
				// Get next pathpoint
				nResult = CCD->PathDatabase()->NextPoint(pFollower->PathHandle, &thePos);

				if(nResult == RGF_NO_NEXT)
				{
					// No next point is END OF PATH.  Fine, if looping motion, let's
					// ..switch directions - otherwise shut down the motion.
					if(pFollower->MotionLoops)
					{
						if(pFollower->Reverse)
							pFollower->bForward = GE_FALSE;			// Switch direction
						else
						{
							CCD->PathDatabase()->Rewind(pFollower->PathHandle, &thePos);
							pFollower->CurrentTarget = thePos;
						}
					}
					else
					{
						pFollower->bReady = GE_FALSE;				// End of motion
						pFollower->bMoving = GE_FALSE;				// Don't move any more!
					}
				}
				else
				{
					// All be cool, set our target tot he new point
					pFollower->CurrentTarget = thePos;
				}
			}
			else
			{
				// Get previous pathpoint
				nResult = CCD->PathDatabase()->PreviousPoint(pFollower->PathHandle, &thePos);

				if(nResult == RGF_NO_PREVIOUS)
				{
					// No previous point is START OF PATH.  Fine, if looping motion, let's
					// ..switch directions - otherwise shut down the motion.
					if(pFollower->MotionLoops)
						pFollower->bForward = GE_TRUE;				// Switch direction
					else
					{
						pFollower->bReady = GE_FALSE;				// End of motion
						pFollower->bMoving = GE_FALSE;				// Don't move any more!
					}
				}
				else
				{
					// All be cool, set our target tot he new point
					pFollower->CurrentTarget = thePos;
				}
			}
		}

		// All done, new point back to caller
		return RGF_SUCCESS;
	}

	// Entity not bound, return failure.
	return RGF_FAILURE;
}
/* ------------------------------------------------------------------------------------ */
void ActorParticle_SystemFrame(ActorParticle_System *ps, geFloat DeltaTime)
{
	// the quick fix to the particle no-draw problem
	ps->psQuantumSeconds = DeltaTime;

	{
		ActorParticle *ptcl;

		ptcl = ps->psParticles;

		while(ptcl)
		{
			ptcl->ptclTime -= ps->psQuantumSeconds;

			if(ptcl->ptclTime <= 0.0f)
			{
				ActorParticle *temp;

				temp = ptcl->ptclNext;
				ActorUnlinkParticle(ps, ptcl);
				ActorDestroyParticle(ps, ptcl);
				ptcl = temp;
				continue;
			}
			else
			{
				// locals
				geVec3d	DeltaPos = {0.0f, 0.0f, 0.0f};

				// apply velocity
				if(ptcl->ptclFlags & PARTICLE_HASVELOCITY)
				{
					geVec3d_Scale(&(ptcl->ptclVelocity), ps->psQuantumSeconds, &DeltaPos);
				}

				// apply DeltaPos to particle position
				if(ptcl->ptclFlags & PARTICLE_HASVELOCITY)
				{
					geVec3d temppos, temppos1;
					GE_Collision Collision;

					CCD->ActorManager()->GetPosition(ptcl->Actor, &temppos);
					geVec3d_Add(&temppos, &DeltaPos, &temppos1);

					if(ptcl->Bounce)
					{
						float totalTravelled = 1.0f;	// keeps track of fraction of path travelled (1.0=complete)
						float margin = 0.001f;			// margin to be satisfied with (1.0 - margin == 1.0)
						int loopCounter = 0;			// safety valve for endless collisions in tight corners
						const int maxLoops = 10;

						while(CCD->Collision()->CheckForWCollision(&ptcl->theBox.Min, &ptcl->theBox.Max,
							temppos, temppos1, &Collision, ptcl->Actor))
						{
							float ratio;
							float elasticity = 1.3f;
							float friction = 0.9f;		// loses (1 minus friction) of speed

							CollisionCalcRatio(Collision, temppos, temppos1, &ratio);
							CollisionCalcImpactPoint(Collision, temppos, temppos1, 1.0f, ratio, &temppos1);
							CollisionCalcVelocityImpact(Collision, ptcl->ptclVelocity, elasticity,
														friction, &(ptcl->ptclVelocity));

							if(ratio >= 0)
								totalTravelled += (1.0f - totalTravelled) * ratio;

							if(totalTravelled >= 1.0f - margin)
								break;

							if(++loopCounter >= maxLoops) // safety check
								break;
						}
					}
					else
					{
						if(CCD->Collision()->CheckForWCollision(&ptcl->theBox.Min, &ptcl->theBox.Max,
							temppos, temppos1, &Collision, ptcl->Actor))
						{
							ActorParticle *temp;
							temp = ptcl->ptclNext;
							ActorUnlinkParticle(ps, ptcl);
							ActorDestroyParticle(ps, ptcl);
							ptcl = temp;
							continue;
						}
					}

					CCD->ActorManager()->Position(ptcl->Actor, temppos1);
				}
			}

			geVec3d	Rotation, Amount;

			Amount.X = ptcl->RotationSpeed.X*ps->psQuantumSeconds;
			Amount.Y = ptcl->RotationSpeed.Y*ps->psQuantumSeconds;
			Amount.Z = ptcl->RotationSpeed.Z*ps->psQuantumSeconds;

			CCD->ActorManager()->GetRotate(ptcl->Actor, &Rotation);

			Rotation.X += Amount.X;
			Rotation.Y += Amount.Y;
			Rotation.Z += Amount.Z;

			CCD->ActorManager()->Rotate(ptcl->Actor, Rotation);

			// set actor alpha
			ptcl->Alpha -= (ptcl->AlphaRate*ps->psQuantumSeconds);

			if(ptcl->Alpha < 0.0f)
				ptcl->Alpha = 0.0f;
			else if(ptcl->Alpha > 255.0f)
				ptcl->Alpha = 255.0f;

			CCD->ActorManager()->SetAlpha(ptcl->Actor, ptcl->Alpha);

			ptcl = ptcl->ptclNext;
		}

		DeltaTime -= QUANTUMSIZE;
	}

	ps->psLastTime += DeltaTime;
}
/* ------------------------------------------------------------------------------------ */
void Particle_SystemFrame(Particle_System *ps, geFloat DeltaTime)
{
	geVec3d	AnchorDelta;

	// the quick fix to the particle no-draw problem
	ps->psQuantumSeconds = DeltaTime;

	{
		Particle *ptcl;

		ptcl = ps->psParticles;

		while(ptcl)
		{
			ptcl->ptclTime -= ps->psQuantumSeconds;

			if(ptcl->ptclTime <= 0.0f)
			{
				Particle *temp;

				temp = ptcl->ptclNext;
				UnlinkParticle(ps, ptcl);
				DestroyParticle(ps, ptcl);
				ptcl = temp;
				continue;
			}
			else
			{
				// locals
				geVec3d	DeltaPos = {0.0f, 0.0f, 0.0f};

				// apply velocity
				if(ptcl->ptclFlags & PARTICLE_HASVELOCITY)
				{
					geVec3d_Scale(&(ptcl->ptclVelocity), ps->psQuantumSeconds, &DeltaPos);
				}

				// apply gravity
				if(ptcl->ptclFlags & PARTICLE_HASGRAVITY)
				{
					// locals
					geVec3d	Gravity;

					// make gravity vector
					geVec3d_Scale(&(ptcl->Gravity), ps->psQuantumSeconds, &Gravity);

					// apply gravity to built in velocity and DeltaPos
					geVec3d_Add(&(ptcl->ptclVelocity), &Gravity, &(ptcl->ptclVelocity));
					geVec3d_Add(&DeltaPos, &Gravity, &DeltaPos);
				}

				//apply wind
				if(ptcl->ptclFlags & PARTICLE_HASWIND)
				{
					geVec3d Wind = CCD->Player()->GetWind();

					//make wind vector
					geVec3d_Scale(&Wind, ps->psQuantumSeconds, &Wind);

					//add wind to delta pos
					geVec3d_Add(&DeltaPos, &Wind, &DeltaPos);
				}

				// apply DeltaPos to particle position
				if((ptcl->ptclFlags & PARTICLE_HASVELOCITY) || (ptcl->ptclFlags & PARTICLE_HASGRAVITY) || (ptcl->ptclFlags & PARTICLE_HASWIND))
				{
					geVec3d temppos, temppos1;
					GE_Collision Collision;
					geVec3d_Copy((geVec3d*)&(ptcl->ptclVertex.X), &temppos);
					geVec3d_Add(&temppos, &DeltaPos, &temppos1);

					if(ptcl->Bounce)
					{
						float totalTravelled = 1.0f;// keeps track of fraction of path travelled (1.0=complete)
						float margin = 0.001f;		// margin to be satisfied with (1.0 - margin == 1.0)
						int loopCounter = 0;		// safety valve for endless collisions in tight corners
						const int maxLoops = 10;

						while(geWorld_Collision(CCD->World(), NULL, NULL, &temppos,
							&temppos1, GE_VISIBLE_CONTENTS, GE_COLLIDE_ALL, 0, NULL, NULL, &Collision))
						{
							float ratio ;
							float elasticity = 1.3f ;
							float friction = 0.9f ; // loses (1 minus friction) of speed

							CollisionCalcRatio(Collision, temppos, temppos1, &ratio);
							CollisionCalcImpactPoint(Collision, temppos, temppos1, 1.0f, ratio, &temppos1);
							CollisionCalcVelocityImpact(Collision, ptcl->ptclVelocity, elasticity,
														friction, &(ptcl->ptclVelocity));

							if(ratio >= 0)
								totalTravelled += (1.0f - totalTravelled) * ratio ;

							if(totalTravelled >= 1.0f - margin)
								break ;

							if(++loopCounter >= maxLoops) // safety check
								break ;
						}
					}
					else
					{
						if(geWorld_Collision(CCD->World(), NULL, NULL, &temppos,
							&temppos1, GE_VISIBLE_CONTENTS, GE_COLLIDE_ALL, 0, NULL, NULL, &Collision))
						{
							Particle *temp;
							temp = ptcl->ptclNext;
							UnlinkParticle(ps, ptcl);
							DestroyParticle(ps, ptcl);
							ptcl = temp;
							continue;
						}
					}

					geVec3d_Copy(&temppos1, (geVec3d *)&(ptcl->ptclVertex.X));
				}

				// make the particle follow its anchor point if it has one
				if(ptcl->AnchorPoint != (const geVec3d*)NULL)
				{
					geVec3d_Subtract(ptcl->AnchorPoint, &(ptcl->CurrentAnchorPoint), &AnchorDelta);
					geVec3d_Add((geVec3d*)&(ptcl->ptclVertex.X), &AnchorDelta, (geVec3d*)&(ptcl->ptclVertex.X));
					geVec3d_Copy(ptcl->AnchorPoint, &(ptcl->CurrentAnchorPoint));
				}
			}

#ifndef	POLYQ
			// set particle alpha
			ptcl->ptclVertex.a = ptcl->Alpha * (ptcl->ptclTime / ptcl->ptclTotalTime);

			geWorld_AddPolyOnce(ps->psWorld,
								&(ptcl->ptclVertex),
								1,
								ptcl->ptclTexture,
								GE_TEXTURED_POINT,
								PARTICLE_RENDERSTYLE,
								ptcl->Scale);
#else
			// set particle alpha
			ptcl->ptclVertex.a = ptcl->Alpha * (ptcl->ptclTime / ptcl->ptclTotalTime);

			if(ptcl->ptclPoly)
				gePoly_SetLVertex(ptcl->ptclPoly, 0, &(ptcl->ptclVertex));
#endif

			ptcl = ptcl->ptclNext;
		}

		DeltaTime -= QUANTUMSIZE;
	}

	ps->psLastTime += DeltaTime;
}