SpinOff Game Code Snippets
This is, how I access the sky in SpinOff. m_xfmSky is the translation for
the sky. This code disables default rotation as defined in the editor.
m_angle.z is the z rotation of the camera, so that the sky keeps horizontally
correct when you rotate around yourself
geWorld *pWorld=GetWorld();
geQuaternion q;
geVec3d v;
geFloat a;
geXForm3d c;
geXForm3d_SetIdentity(&c);
geXForm3d_RotateY(&c, 4-m_angle.Z);
geXForm3d_Multiply(&m_xfmSky, &c, &c);
geQuaternion_FromMatrix(&c, &q);
geQuaternion_GetAxisAngle(&q, &v, &a);
pWorld->SkyBox.Axis=v;
pWorld->SkyBox.Angle=a;
Another interesting snippet may be the shadow-routine. As the genesis shadow code is quite
buggy this one fits perfect for a marble shadow (a round one). For an avatar shadow an additional
rotation is needed. Shadow gets bigger and fades out by distance.
Marble *pMarble= (Marble*)geEntity_GetUserData(pEntity);
// calculate shadow
geVec3d_Copy(&pMarble->origin, &destination);
destination.Y-=300; // draw shadow only at distances < 300
if(geWorld_Collision(m_pWorld, NULL, NULL, &pMarble->origin, &destination, GE_CONTENTS_SOLID_CLIP,
GE_COLLIDE_MODELS, 0, NULL, NULL, &col))
{ // do not draw shadow in the sky. This hack works only if your leveldesign has sky.bottom.y at around -150
if (col.Impact.Y>-160)
{
//crossproduct for Orientation of shadow
geVec3d_CrossProduct(&col.Impact, &col.Plane.Normal, &corner1);
// Shadow gets bigger with distance
float dis = (float)(fabs((col.Impact.Y- pMarble->origin.Y))+50)/4;
float length = (geVec3d_Length(&corner1) / 2.0f) / (dis*(pMarble->scale.X/12));
// Shadow gets more transparent with distance
float trans=400-dis*5;
if (trans>255)
trans=255;
if (trans<0)
trans=0;
// Setup vertex for Shadow 1,2,3,4
for(int i = 0; i < 4; i++)
{
// color
Vertex[i].r = 255.0f; //red
Vertex[i].g = 255.0f; //green
Vertex[i].b = 255.0f; //blue
Vertex[i].a = trans; //alpha
}
//divide with length
if (length<=0.03)
length=0.03f;
corner1.X /= length;
corner1.Y /= length;
corner1.Z /= length;
//take the crossproduct to get the other corner
geVec3d_CrossProduct(&corner1, &col.Plane.Normal, &corner2);
//move a bit away to avoid being filtered out by ZBuffer
geVec3d_MA(&col.Impact, 0.3f, &col.Plane.Normal, &col.Impact);
//calculate vertices from corners
Vertex[0].X = corner1.X + col.Impact.X;
Vertex[0].Y = corner1.Y + col.Impact.Y;
Vertex[0].Z = corner1.Z + col.Impact.Z;
Vertex[0].u = 0.0f;
Vertex[0].v = 0.0f;
Vertex[1].X = corner2.X + col.Impact.X;
Vertex[1].Y = corner2.Y + col.Impact.Y;
Vertex[1].Z = corner2.Z + col.Impact.Z;
Vertex[1].u = 0.0f;
Vertex[1].v = 1.0f;
Vertex[2].X = col.Impact.X - corner1.X;
Vertex[2].Y = col.Impact.Y - corner1.Y;
Vertex[2].Z = col.Impact.Z - corner1.Z;
Vertex[2].u = 1.0f;
Vertex[2].v = 1.0f;
Vertex[3].X = col.Impact.X - corner2.X;
Vertex[3].Y = col.Impact.Y - corner2.Y;
Vertex[3].Z = col.Impact.Z - corner2.Z;
Vertex[3].u = 1.0f;
Vertex[3].v = 0.0f;
geWorld_AddPolyOnce(m_pWorld,Vertex,4,m_pShadowBitmap,GE_TEXTURED_POLY,GE_RENDER_DO_NOT_OCCLUDE_OTHERS,1.0f);
}
}
This is SpinOffs very tricky camera movement logic.
It is a "third person (marble)" view. You can rotate around the marble, using the cursor keys.
The logic also makes shure, that the marble is allways in scope of the camera.
Even if the marble is disappearing behind or below a wall. In such a case, the
camera zooms slowly in before the marble is out of sight.
void CEngineWrapper::TransformCamera(DWORD dwTime)
{
int t= dwTime- m_dwLastRenderTime;
float speed=0;
// Cursor key pressed ?
// This rotates the Camera around the marble, depending on the cursor key pressed.
switch (m_CameraRotate)
{
case right: // rotate left
m_angle.Z += m_rotSpeed * t/100;
break;
case left: // rotate right
m_angle.Z -= m_rotSpeed * t/100;
break;
case down: // rotate down
if (m_CameraFeature<3)
{
m_OffsetAngle.Y += m_rotSpeed * t/100;
if (m_OffsetAngle.Y>-0.1f)
m_OffsetAngle.Y=-0.1f;
if (m_CameraFeature == 2)
{
if (m_OffsetAngle.Y>-0.3f)
m_OffsetAngle.Y=-0.3f;
}
}
break;
case up: // rotate up
if (m_CameraFeature<3)
{
m_OffsetAngle.Y -= m_rotSpeed * t/100;
if (m_OffsetAngle.Y<-1.57f)
m_OffsetAngle.Y=-1.57f;
}
break;
}
geEntity_EntitySet *pSet2;
geEntity *pEntity2;
if (!m_pWorld) throw "no world";
// look for the main marble (the one, that owns the camera)
pSet2 = geWorld_GetEntitySet(m_pWorld, "Marble");
if (!pSet2) return;
for (pEntity2= geEntity_EntitySetGetNextEntity(pSet2, NULL); pEntity2;
pEntity2= geEntity_EntitySetGetNextEntity(pSet2, pEntity2))
{
Marble *pMarble= (Marble*)geEntity_GetUserData(pEntity2);
if (pMarble->MainMarble) // This is the one !
{
geVec3d OldKameraPos, MarblePos;
geVec3d_Copy(&m_xfmCamera.Translation, &OldKameraPos);
geVec3d_Copy(&pMarble->origin, &MarblePos);
geVec3d NewPos, TestPos, DeltaPos2;
GE_Collision col;
// Position of Camera
NewPos.X=pMarble->origin.X+(float)(fabs(m_Distance)*sin(m_angle.Z)*cos(m_OffsetAngle.Y));
NewPos.Y=pMarble->origin.Y+0+(float)(fabs(m_Distance)*1.2*(sin(-m_OffsetAngle.Y)));
NewPos.Z=pMarble->origin.Z+(float)(fabs(m_Distance)*cos(m_angle.Z)*cos(m_OffsetAngle.Y));
MarblePos.Y += 14; // A bit up, so that we don't act on small walls
float y=NewPos.Y;
geVec3d side, TestPos2, DeltaPos;
// Is the marble disappearing behind something on the right side ?
// if so, then zoom camera near, until nothing is in its way.
geXForm3d_GetLeft(&m_xfmCamera, &side);
geVec3d_Scale(&side, -6.0f, &side);
geVec3d_Add(&MarblePos, &side, &TestPos2);
geVec3d_Subtract(&NewPos, &MarblePos, &DeltaPos);
while ((geWorld_Collision(m_pWorld, NULL, NULL, &TestPos2, &NewPos, GE_CONTENTS_SOLID_CLIP,
GE_COLLIDE_MODELS, 0xffffffff, NULL, NULL, &col)) && (geVec3d_Length(&DeltaPos)>1))
{
geVec3d_Scale(&DeltaPos, 0.95f, &DeltaPos);
geVec3d_Add(&MarblePos, &DeltaPos, &NewPos);
}
// Is the marble disappearing behind something on the left side ?
// if so, then zoom camera near, until nothing is in its way.
geXForm3d_GetLeft(&m_xfmCamera, &side);
geVec3d_Scale(&side, 6.0f, &side);
geVec3d_Add(&MarblePos, &side, &TestPos2);
geVec3d_Subtract(&NewPos, &MarblePos, &DeltaPos);
while ((geWorld_Collision(m_pWorld, NULL, NULL, &TestPos2, &NewPos, GE_CONTENTS_SOLID_CLIP,
GE_COLLIDE_MODELS, 0xffffffff, NULL, NULL, &col)) && (geVec3d_Length(&DeltaPos)>1))
{
geVec3d_Scale(&DeltaPos, 0.95f, &DeltaPos);
geVec3d_Add(&MarblePos, &DeltaPos, &NewPos);
}
// Is the marble disappearing behind a door ceiling ?
// if so, then zoom camera near, until nothing is in its way.
geXForm3d_GetUp(&m_xfmCamera, &side);
geVec3d_Scale(&side, 6.0f, &side);
geVec3d_Add(&MarblePos, &side, &TestPos2);
geVec3d_Subtract(&NewPos, &MarblePos, &DeltaPos);
while ((geWorld_Collision(m_pWorld, NULL, NULL, &TestPos2, &NewPos, GE_CONTENTS_SOLID_CLIP,
GE_COLLIDE_MODELS, 0xffffffff, NULL, NULL, &col)) && (geVec3d_Length(&DeltaPos)>1))
{
geVec3d_Scale(&DeltaPos, 0.95f, &DeltaPos);
geVec3d_Add(&MarblePos, &DeltaPos, &NewPos);
}
// To be shure, nothing is in the way now, we look in direction of marbles center
// Also the camera gets its own size to avoid rendering mistakes
geVec3d_Subtract( &MarblePos,&NewPos, &DeltaPos2 );
geVec3d_Normalize(&DeltaPos2);
// Scale, so that there are no rendering mistakes
geVec3d_Scale(&DeltaPos2, 5.0f, &DeltaPos2);
// The test position, where camera meets wall
geVec3d_Subtract(&NewPos, &DeltaPos2, &TestPos);
// lift camera a little bit up, this looks better
NewPos.Y=(6*NewPos.Y+y)/7;
// But only if there is nothing up there
geVec3d_Subtract( &MarblePos,&NewPos, &DeltaPos2 );
geVec3d_Normalize(&DeltaPos2);
// Scale with the size of a camera without rendering problems
geVec3d_Scale(&DeltaPos2, 15.5f, &DeltaPos2);
// The test position, where camera meets wall
geVec3d_Subtract(&NewPos, &DeltaPos2, &TestPos);
if (geWorld_Collision(m_pWorld, NULL, NULL, &MarblePos, &TestPos, GE_CONTENTS_SOLID_CLIP,
GE_COLLIDE_MODELS, 0xffffffff, NULL, NULL, &col))
{ // Still a tall wall between camera and marble. Push camera in front of collision point.
geVec3d_Add(&col.Impact, &DeltaPos2, &NewPos );
}
// Done: New cameraposition:
geXForm3d_SetTranslation(&m_xfmCamera, NewPos.X, NewPos.Y ,NewPos.Z);
}
}
}
// Before we rotate, we save translation
geVec3d trans= m_xfmCamera.Translation;
geXForm3d_SetXRotation(&m_xfmCamera, m_OffsetAngle.Y);
geXForm3d_RotateY(&m_xfmCamera, m_OffsetAngle.X);
m_xfmCamera.Translation= trans;
// Ready
geCamera_SetWorldSpaceXForm(m_pCamera, &m_xfmCamera);
}