Bitmap shadows rotation with actor

Post your Feature Requests here...
Post Reply
User avatar
federico
RF Dev Team
Posts: 443
Joined: Tue Jul 05, 2005 3:14 pm
Contact:

Bitmap shadows rotation with actor

Post by federico » Wed Jan 25, 2006 1:34 pm

I'm working on a demo using (and showing) the physics release. I've reduced the level and actors scale to have faster physics and also the stencil shadows works faster and better. However I'm thinking about using bitmap shadows but there are two problems:

1) the actor TILT and PITCH has a bad influence on shadows rotation and position.

2) the actor YAW hasn't any influence. it doesn't rotate with actor.

can we fix theese issues? i think that the better result for our games is to have stenciled shadows on meshes structures and bitmap shadows (you can set the alpha to make them quite similar to stenciled) for fast dynamic objects.
I think that this is the bitmap shadow code:

Code: Select all

if(RenderBitmapShadow)
	{
// end change gekido
		if(theEntry->ShadowSize > 0.0f && theEntry->ShadowBitmap) // changed QD 06/26/04
		{
			geVec3d	Pos1, Pos2;
			GE_Collision Collision;
			geVec3d right, up;
			GE_LVertex	vertex[4];
			geVec3d Axis[3];
			int major, i;
			geVec3d Impact, normal;
			#define fab(a) (a > 0 ? a : -a)
			
// changed Pickles Jul 04 -- PWX
			//Pos1 = thePosition.Translation;
			geXForm3d BoneXForm;
		
			geActor_GetBoneTransformByIndex(theEntry->Actor, 0, &BoneXForm);
			Pos1 = BoneXForm.Translation;
// end changed Pickles Jul 04 -- PWX
			Pos2 = Pos1;
			Pos2.Y -= 30000.0f;
		
			geWorld_Collision(CCD->World(), NULL, NULL,
				&Pos1, &Pos2, GE_CONTENTS_SOLID_CLIP | GE_CONTENTS_WINDOW, 
				GE_COLLIDE_MODELS, 0x0, NULL, NULL, &Collision);
			//CCD->Collision()->CheckForWCollision(NULL, NULL,
				//Pos1, Pos2, &Collision, theEntry->Actor);
		
			Impact = Collision.Impact;
			normal = Collision.Plane.Normal;
		
// changed QD 12/15/05
			// can't be negative (sqrt!)
			// float dist = (float)fabs(geVec3d_DistanceBetween(&Pos1, &Impact));
			// if(dist<0.0f)
			// 	dist = 0.0f;
			float dist = geVec3d_DistanceBetween(&Pos1, &Impact);
						
			if(dist<(theEntry->ShadowSize*2.0f))
			{
				for(i=0; i<4; i++)
				{
					// texture coordinates
					vertex[i].u = 0.0f;
					vertex[i].v = 0.0f;
					// color
					vertex[i].r = 24.0f;
					vertex[i].g = 24.0f;
					vertex[i].b = 24.0f;
// changed QD 06/26/04
// changed RF064
//					vertex[i].a = ShadowAlpha;
// end change RF064
					vertex[i].a = theEntry->ShadowAlpha;
// end change QD
				}
				
				vertex[3].u = 1.0f;
				vertex[2].u = 1.0f;
				vertex[2].v = 1.0f;
				vertex[1].v = 1.0f;
				
				for(i=0; i<3; i++)
				{
					Axis[i].X = 0.0f;
					Axis[i].Y = 0.0f;
					Axis[i].Z = 0.0f;
				}

				Axis[0].X = 1.0f;
				Axis[1].Y = 1.0f;
				Axis[2].Z = 1.0f;
				
				major = 0;
				
				if(fab(normal.Y) > fab(normal.X))
				{
					major = 1;
				
					if(fab(normal.Z) > fab(normal.Y))
						major = 2;
				}
				else
				{
					if(fab(normal.Z) > fab(normal.X))
						major = 2;
				}
				
				if(fab(normal.X) == 1.0f || fab(normal.Y) == 1.0f || fab(normal.Z) == 1.0f)
				{
					if((major == 0 && normal.X > 0) || major == 1)
					{
						right.X = 0.0f;
						right.Y = 0.0f;
						right.Z = -1.0f;
					}
					else if(major == 0)
					{
						right.X = 0.0f;
						right.Y = 0.0f;
						right.Z = 1.0f;
					}
					else 
					{
						right.X = normal.Z;
						right.Y = 0.0f;
						right.Z = 0.0f;
					}
				}
				else 
					geVec3d_CrossProduct(&Axis[major], &normal, &right);
				
// start Pickles Jul 04
				InVector(theEntry->Actor,&up);
				LeftVector(theEntry->Actor,&right);
				geVec3d_Inverse(&right);
// end Pickles Jul 04

				geVec3d_CrossProduct(&normal, &right, &up);
				geVec3d_Normalize(&up);
				geVec3d_Normalize(&right);
				geVec3d_Scale(&right, (theEntry->ShadowSize-(dist*0.5f))*0.5f, &right); 
				geVec3d_Scale(&up, (theEntry->ShadowSize-(dist*0.5f))*0.5f, &up);
				
				geVec3d_MA(&Impact, 0.4f, &normal, &Impact);
				
				//	calculate vertices from corners
				vertex[1].X = Impact.X + ((-right.X - up.X));
				vertex[1].Y = Impact.Y + ((-right.Y - up.Y));
				vertex[1].Z = Impact.Z + ((-right.Z - up.Z));
				
				vertex[2].X = Impact.X + ((right.X - up.X));
				vertex[2].Y = Impact.Y + ((right.Y - up.Y));
				vertex[2].Z = Impact.Z + ((right.Z - up.Z));
				
				vertex[3].X = Impact.X + ((right.X + up.X));
				vertex[3].Y = Impact.Y + ((right.Y + up.Y));
				vertex[3].Z = Impact.Z + ((right.Z + up.Z));
				
				vertex[0].X = Impact.X + ((-right.X + up.X));
				vertex[0].Y = Impact.Y + ((-right.Y + up.Y));
				vertex[0].Z = Impact.Z + ((-right.Z + up.Z));
				
				geWorld_AddPolyOnce(CCD->World(),
					vertex,
					4,
// changed QD 06/26/04
// changed RF064
//					ShadowBitmap,
// end change RF064
					theEntry->ShadowBitmap,
// end change QD
					GE_TEXTURED_POLY,
					GE_RENDER_DO_NOT_OCCLUDE_OTHERS | GE_RENDER_DEPTH_SORT_BF,
					1.0f);
			}
		}
// end change RF063
	}

User avatar
federico
RF Dev Team
Posts: 443
Joined: Tue Jul 05, 2005 3:14 pm
Contact:

Post by federico » Thu Feb 02, 2006 3:34 pm

I think I found a good resource to solve this problem.
This is the code for the walldecal that has the rotation parameter:
void CWallDecal::AddDecal(WallDecal *pSource)
{
geVec3d right, up;
GE_LVertex vertex[4];

//Setup vertex 1,2,3,4
for(int i = 0; i < 4; i++)
{
// texture coordinates
vertex.u = 0.0f;
vertex.v = 0.0f;
// color
vertex.r = pSource->Color.r; //red
vertex.g = pSource->Color.g; //green
vertex.b = pSource->Color.b; //blue
vertex.a = pSource->Alpha; //alpha
}

vertex[3].u = 1.0f;
vertex[2].u = 1.0f;
vertex[2].v = 1.0f;
vertex[1].v = 1.0f;

geVec3d Axis[3] =
{
{1.0f, 0.0f, 0.0f},
{0.0f, 1.0f, 0.0f},
{0.0f, 0.0f, 1.0f}
};

pSource->origin = pSource->OriginOffset;
SetOriginOffset(pSource->EntityName, pSource->BoneName, pSource->Model, &(pSource->origin));

geVec3d impact, normal, Pos, Direction;
geVec3d Front, Back;
GE_Collision Collision;

Direction.Z = 0.0174532925199433f*(pSource->Angle.Z);
Direction.X = 0.0174532925199433f*(pSource->Angle.X);
Direction.Y = 0.0174532925199433f*(pSource->Angle.Y-90.0f);

geXForm3d Xf;
geXForm3d_SetIdentity(&Xf);
geXForm3d_RotateZ(&Xf, Direction.Z);
geXForm3d_RotateX(&Xf, Direction.X);
geXForm3d_RotateY(&Xf, Direction.Y);
geXForm3d_Translate(&Xf, pSource->origin.X, pSource->origin.Y, pSource->origin.Z);

Pos = Xf.Translation;
geXForm3d_GetIn(&Xf, &Direction);

geVec3d_AddScaled (&Pos, &Direction, 4000.0f, &Back);
geVec3d_AddScaled (&Pos, &Direction, 0.0f, &Front);
geWorld_Collision(CCD->World(), NULL, NULL, &Front,
&Back, GE_CONTENTS_SOLID_CLIP, GE_COLLIDE_MODELS | GE_COLLIDE_MESHES , 0, NULL, NULL, &Collision);

impact = Collision.Impact;
normal = Collision.Plane.Normal;

right.X = (float) ( (normal.Z * cos(0.0174532925199433f*pSource->Angle.Z)) + ( normal.Y * cos(0.0174532925199433f*pSource->Angle.Z)));
right.Y = (float) ( (normal.Z * sin(0.0174532925199433f*pSource->Angle.Z)) + ( normal.X * sin(0.0174532925199433f*pSource->Angle.Z)));
right.Z = (float) ((-normal.X * cos(0.0174532925199433f*pSource->Angle.Z)) + (-normal.Y * sin(0.0174532925199433f*pSource->Angle.Z)));
//End Aug2003DCS

geVec3d_CrossProduct(&normal, &right, &up);
geVec3d_Normalize(&up);
geVec3d_Normalize(&right);

geVec3d_Scale(&right, pSource->Width/2.0f, &right);
geVec3d_Scale(&up, pSource->Height/2.0f, &up);
// changed RF064
geVec3d_MA(&impact, 0.5f, &normal, &impact);
// end change RF064
//calculate vertices from corners
vertex[1].X = impact.X + ((-right.X - up.X));
vertex[1].Y = impact.Y + ((-right.Y - up.Y));
vertex[1].Z = impact.Z + ((-right.Z - up.Z));

vertex[2].X = impact.X + ((right.X - up.X));
vertex[2].Y = impact.Y + ((right.Y - up.Y));
vertex[2].Z = impact.Z + ((right.Z - up.Z));

vertex[3].X = impact.X + ((right.X + up.X));
vertex[3].Y = impact.Y + ((right.Y + up.Y));
vertex[3].Z = impact.Z + ((right.Z + up.Z));

vertex[0].X = impact.X + ((-right.X + up.X));
vertex[0].Y = impact.Y + ((-right.Y + up.Y));
vertex[0].Z = impact.Z + ((-right.Z + up.Z));
// changed RF064
if(!pSource->Animated)
{
pSource->Poly = geWorld_AddPoly(CCD->World(),
vertex,
4,
pSource->Bitmap,
GE_TEXTURED_POLY,
GE_RENDER_DO_NOT_OCCLUDE_OTHERS | GE_RENDER_DEPTH_SORT_BF ,
1.0f);
}
else
{
memcpy(pSource->vertex, vertex, sizeof(GE_LVertex)*4);
//Start Aug2003DCS
pSource->Poly = geWorld_AddPoly(CCD->World(),
//End Aug2003DCS
(GE_LVertex *)pSource->vertex,
4,
pSource->FBitmap[pSource->CurTex],
GE_TEXTURED_POLY,
GE_RENDER_DO_NOT_OCCLUDE_OTHERS | GE_RENDER_DEPTH_SORT_BF ,
1.0f);
}
// end change RF064
}

I will try to use this way to make the shadow rotate togheter with the actor on Y axis and to make an inverse rotation on the Z X axis to keep alignement on the floor (now, tilt and pitch has a bad influence on shadow rotation, because the shadow follow the ZX, and not Y, rotation of the actor). If anyone would help my poor programming eperience, I would be thankful! :D

User avatar
federico
RF Dev Team
Posts: 443
Joined: Tue Jul 05, 2005 3:14 pm
Contact:

Post by federico » Thu Feb 02, 2006 8:37 pm

oh men, I got it! :D
I've got the aspected result! so now the bitmap shadow rotates Y according to the actor and place itself on Y Z shadows to place itself correctly on the floor.
This is the code modded by me. It is in CActorManager.cpp

Code: Select all

if(RenderBitmapShadow)
	{
// end change gekido
		if(theEntry->ShadowSize > 0.0f && theEntry->ShadowBitmap) // changed QD 06/26/04
		{
			geVec3d	Pos1, Pos2, Direction, Angle_Sh;
			GE_Collision Collision;
			geVec3d right, up;
			GE_LVertex	vertex[4];
			geVec3d Axis[3];
			int major, i;
			geVec3d Impact, normal;
			#define fab(a) (a > 0 ? a : -a)
			
// changed Pickles Jul 04 -- PWX
			//Pos1 = thePosition.Translation;
			geXForm3d BoneXForm;
		
			geActor_GetBoneTransformByIndex(theEntry->Actor, 0, &BoneXForm);
			Pos1 = BoneXForm.Translation;
// end changed Pickles Jul 04 -- PWX

			//fede shadow tries - begin

			GetRotate(theEntry->Actor, &Angle_Sh);
			Direction.Z = 0.0174532925199433f*(Angle_Sh.Z);
	        Direction.X = 0.0174532925199433f*(Angle_Sh.X);
          	Direction.Y = 0.0174532925199433f*(Angle_Sh.Y);
          	geXForm3d_SetIdentity(&BoneXForm);
			geXForm3d_RotateZ(&BoneXForm, -Direction.Z);
         	geXForm3d_RotateX(&BoneXForm, -Direction.X);
	        geXForm3d_RotateY(&BoneXForm, Direction.Y);

			//fede shadow tries - end
			
			geXForm3d_GetIn(&BoneXForm, &Direction);

			Pos2 = Pos1;
			Pos2.Y -= 30000.0f;
		
			geWorld_Collision(CCD->World(), NULL, NULL,
				&Pos1, &Pos2, GE_COLLIDE_ALL /*GE_VISIBLE_CONTENTS GE_CONTENTS_SOLID_CLIP | GE_CONTENTS_WINDOW*/, 
			GE_COLLIDE_MODELS, 0x0, NULL, NULL, &Collision);
			//CCD->Collision()->CheckForWCollision(NULL, NULL,
				//Pos1, Pos2, &Collision, theEntry->Actor);
		
			Impact = Collision.Impact;
			normal = Collision.Plane.Normal;
		
// changed QD 12/15/05
			// can't be negative (sqrt!)
			// float dist = (float)fabs(geVec3d_DistanceBetween(&Pos1, &Impact));
			// if(dist<0.0f)
			// 	dist = 0.0f;
			float dist = geVec3d_DistanceBetween(&Pos1, &Impact);
						
			if(dist<(theEntry->ShadowSize*2.0f))
			{
				for(i=0; i<4; i++)
				{
					// texture coordinates
					vertex[i].u = 0.0f;
					vertex[i].v = 0.0f;
					// color
					vertex[i].r = 24.0f;
					vertex[i].g = 24.0f;
					vertex[i].b = 24.0f;
// changed QD 06/26/04
// changed RF064
//					vertex[i].a = ShadowAlpha;
// end change RF064
					vertex[i].a = theEntry->ShadowAlpha;
// end change QD
				}
				
				vertex[3].u = 1.0f;
				vertex[2].u = 1.0f;
				vertex[2].v = 1.0f;
				vertex[1].v = 1.0f;
				
				for(i=0; i<3; i++)
				{
					Axis[i].X = 0.0f;
					Axis[i].Y = 0.0f;
					Axis[i].Z = 0.0f;
				}

				Axis[0].X = 1.0f;
				Axis[1].Y = 1.0f;
				Axis[2].Z = 1.0f;
				
				major = 0;
				
				if(fab(normal.Y) > fab(normal.X))
				{
					major = 1;
				
					if(fab(normal.Z) > fab(normal.Y))
						major = 2;
				}
				else
				{
					if(fab(normal.Z) > fab(normal.X))
						major = 2;
				}
				
				if(fab(normal.X) == 1.0f || fab(normal.Y) == 1.0f || fab(normal.Z) == 1.0f)
				{
					if((major == 0 && normal.X > 0) || major == 1)
					{
						right.X = 0.0f;
						right.Y = 0.0f;
						right.Z = -1.0f;
					}
					else if(major == 0)
					{
						right.X = 0.0f;
						right.Y = 0.0f;
						right.Z = 1.0f;
					}
					else 
					{
						right.X = normal.Z;
						right.Y = 0.0f;
						right.Z = 0.0f;
					}
				}
				else 
					geVec3d_CrossProduct(&Axis[major], &normal, &right);
				
// start Pickles Jul 04
				InVector(theEntry->Actor,&up);
				LeftVector(theEntry->Actor,&right);
				geVec3d_Inverse(&right);
// end Pickles Jul 04

				geVec3d_CrossProduct(&normal, &right, &up);
				geVec3d_Normalize(&up);
				geVec3d_Normalize(&right);
				geVec3d_Scale(&right, (theEntry->ShadowSize-(dist*0.5f))*0.5f, &right); 
				geVec3d_Scale(&up, (theEntry->ShadowSize-(dist*0.5f))*0.5f, &up);
				
				geVec3d_MA(&Impact, 0.4f, &normal, &Impact);
				
				//	calculate vertices from corners // add 5.0f to help actor occlusions - fede
				vertex[1].X = Impact.X + ((-right.X - up.X));
				vertex[1].Y = Impact.Y + ((-right.Y - up.Y)) + 5.0f;
				vertex[1].Z = Impact.Z + ((-right.Z - up.Z));
				
				vertex[2].X = Impact.X + ((right.X - up.X));
				vertex[2].Y = Impact.Y + ((right.Y - up.Y)) + 5.0f;
				vertex[2].Z = Impact.Z + ((right.Z - up.Z));
				
				vertex[3].X = Impact.X + ((right.X + up.X));
				vertex[3].Y = Impact.Y + ((right.Y + up.Y)) + 5.0f;
				vertex[3].Z = Impact.Z + ((right.Z + up.Z));
				
				vertex[0].X = Impact.X + ((-right.X + up.X));
				vertex[0].Y = Impact.Y + ((-right.Y + up.Y)) + 5.0f;
				vertex[0].Z = Impact.Z + ((-right.Z + up.Z));
				
				geWorld_AddPolyOnce(CCD->World(),
					vertex,
					4,
// changed QD 06/26/04
// changed RF064
//					ShadowBitmap,
// end change RF064
					theEntry->ShadowBitmap,
// end change QD
					GE_TEXTURED_POLY,
					GE_RENDER_DO_NOT_OCCLUDE_OTHERS | GE_RENDER_DEPTH_SORT_BF,
					1.0f);
			}
		}
// end change RF063
	}
see the result in the screens: the shdow rotates with the actor and change his ZX rotation on the ramp.

P.s. the shadow is a bottle only for test... :wink:
Attachments
screen001.jpg
screen001.jpg (6.86 KiB) Viewed 981 times
screen004.jpg
screen004.jpg (11.83 KiB) Viewed 982 times
screen008.jpg
screen008.jpg (22.28 KiB) Viewed 982 times

hike1
RF FAQ-Keeper
Posts: 607
Joined: Tue Jul 05, 2005 4:19 am
Contact:

Post by hike1 » Fri Feb 03, 2006 12:02 am

That's good, will tree shadows on terrain actually lie on the
tilted terrain? Because now they only lie flat on a flat surface.

User avatar
federico
RF Dev Team
Posts: 443
Joined: Tue Jul 05, 2005 3:14 pm
Contact:

Post by federico » Fri Feb 03, 2006 11:50 am

no. the shadow alignement is dependent by actor rotation and not by mesh face collision. I think that something can be done for this but I currently can't find a solution. This method is good for objects that change X or Z orientation accordingly to the floor. this is why it good for a physics object. however I have to make some more test because I saw that the shadow remains aligned with the terrain also when the car jumps changing XZ rotation. I'll let you know asap. Any idea QoD? :D

User avatar
federico
RF Dev Team
Posts: 443
Joined: Tue Jul 05, 2005 3:14 pm
Contact:

Post by federico » Wed Feb 08, 2006 5:11 pm

HIKE1!!!
yes the shadow align itself using the orientation of the brush face intersected by a vector that starts from the actor.So, if the terrain isn't complex you can have you bitmap shadow perfectly aligned with the bsp brush. look at this screens (the shadow is a rectangle, only for test):
Image
Image
Image

Veleran
Posts: 891
Joined: Mon Aug 22, 2005 10:22 am
Location: Greece

Post by Veleran » Thu Feb 09, 2006 5:13 am

To be sure,can you please post a screen with the shadow falling on a 8-8 gensurf like terrain with small bumps?
And take the screen not from atop,to see if the shadow floats(i know it wont float).

I ask this because as you place trees with decal shadows,they are often placed over 8 different polygons-or more because tree shadows are big.

User avatar
federico
RF Dev Team
Posts: 443
Joined: Tue Jul 05, 2005 3:14 pm
Contact:

Post by federico » Thu Feb 09, 2006 11:31 am

using this method the shadow align itself using the inclination of the brush itersected down the actor. if the other polygons of the terrain are higher or lower than this point they could occlude the bitmap. You need a regular brush that can receive enterely the bitmap.
I will try another method making the shadow occlude the bitmap trying to force the actor to occlude the shadow. I don't if it can work.

Post Reply