Vector3 FeedingTube::GetForwardsClippingDir( double _predictionTime, FeedingTube *_sender )
{
	if (_sender == NULL) {
		return GetDishFront( _predictionTime );
	}

	Vector3 senderDishFront = _sender->GetDishFront( _predictionTime );
	
	Vector3 dishFront = GetDishFront( _predictionTime );

	// Make the two dishFronts point at each other.

	Vector3 SR = GetDishPos(_predictionTime) - _sender->GetDishPos(_predictionTime) ;
	SR.Normalise();

	if (SR * senderDishFront < 0)
		senderDishFront *= -1;
	
	if (SR * dishFront > 0)
		dishFront *= -1;	

	Vector3 combinedDirection = 
		-senderDishFront +
		dishFront;

	if (combinedDirection.MagSquared() < 0.5)
		return -dishFront;

	return combinedDirection.Normalise();
}
void Vector3::RotateAround(Vector3 const &_axis)
{
	float angle = _axis.MagSquared();
	if (angle < 1e-8) return;
	angle = sqrtf(angle);
	Vector3 norm(_axis / angle);
	FastRotateAround(norm, angle);
}
void Nuke::RenderHistory( double _predictionTime )
{
    if( m_history.Size() > 0 && m_id.GetTeamId() != 255 )
    {
        glBindTexture( GL_TEXTURE_2D, g_app->m_resource->GetTexture( "textures/laser.bmp" ) );

        Vector3 predictedPos = m_pos + m_vel * SERVER_ADVANCE_PERIOD;
        Vector3 lastPos = predictedPos;

        RGBAColour colour = g_app->m_location->m_teams[m_id.GetTeamId()]->m_colour;
        if( m_id.GetTeamId() == g_app->m_location->GetMonsterTeamId() )
        {
            glBlendFunc     ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR );
            colour.Set( 255, 255, 255, 0 );
        }

        for( int i = 0; i < m_history.Size(); ++i )
        {
            Vector3 historyPos, thisPos;
			thisPos = historyPos = *m_history[i];

            Vector3 diff = thisPos - lastPos;
            lastPos += diff * 0.1f;
            if( m_id.GetTeamId() != g_app->m_location->GetMonsterTeamId() )
            {
                colour.a = 255 - 255 * (float) i / (float) m_history.Size();
            }

            glColor4ubv( colour.GetData() );

            Vector3 lengthVector = (thisPos - lastPos).Normalise();
	        lengthVector.SetLength((thisPos - lastPos).Mag());
            Vector3 fromPos = lastPos;
            Vector3 toPos = thisPos;//lastPos - lengthVector;

            Vector3 midPoint        = fromPos + (toPos - fromPos)/2.0f;
            Vector3 camToMidPoint   = g_app->m_camera->GetPos() - midPoint;
            float   camDistSqd      = camToMidPoint.MagSquared();
            Vector3 rightAngle      = (camToMidPoint ^ ( midPoint - toPos )).Normalise();
            
            //rightAngle *= 0.8f;
            rightAngle.SetLength(5.0f);

            glBegin( GL_QUADS );
                glTexCoord2i(0,0);      glVertex3dv( (fromPos - rightAngle).GetData() );
                glTexCoord2i(0,1);      glVertex3dv( (fromPos + rightAngle).GetData() );
                glTexCoord2i(1,1);      glVertex3dv( (toPos + rightAngle).GetData() );                
                glTexCoord2i(1,0);      glVertex3dv( (toPos - rightAngle).GetData() );                     

                glTexCoord2i(0,0);      glVertex3dv( (fromPos - rightAngle).GetData() );
                glTexCoord2i(0,1);      glVertex3dv( (fromPos + rightAngle).GetData() );
                glTexCoord2i(1,1);      glVertex3dv( (toPos + rightAngle).GetData() );                
                glTexCoord2i(1,0);      glVertex3dv( (toPos - rightAngle).GetData() );                     

                glTexCoord2i(0,0);      glVertex3dv( (fromPos - rightAngle).GetData() );
                glTexCoord2i(0,1);      glVertex3dv( (fromPos + rightAngle).GetData() );
                glTexCoord2i(1,1);      glVertex3dv( (toPos + rightAngle).GetData() );                
                glTexCoord2i(1,0);      glVertex3dv( (toPos - rightAngle).GetData() );                     
            glEnd();

            lastPos = historyPos;
        }
    }
}
void Nuke::Render( double _predictionTime )
{
    if( m_renderMarker )
    {
        RenderGroundMarker();
    }
    RenderSub( _predictionTime );
    if( m_exploded )
    {
        //RenderDeaths();
    }
    else
    {
        if( !m_launched ) return;

        Vector3 front = m_front;
        front.RotateAroundY( M_PI / 2.0f );
        Vector3 predictedPos = m_pos + m_vel * _predictionTime;;
        Vector3 entityUp = m_front;
        Vector3 entityRight (front ^ entityUp);

        Vector3 lengthVector = m_vel;
        lengthVector.SetLength( 10.0f );
        Vector3 fromPos = predictedPos;
        Vector3 toPos = predictedPos - lengthVector;

        Vector3 midPoint        = fromPos + (toPos - fromPos)/2.0f;
        Vector3 camToMidPoint   = g_app->m_camera->GetPos() - midPoint;
        float   camDistSqd      = camToMidPoint.MagSquared();
        Vector3 rightAngle      = (camToMidPoint ^ ( midPoint - toPos )).Normalise();

        entityRight = rightAngle;

        float size = 10.0f;  
        size *= (1.0f + 0.03f * (( m_id.GetIndex() * m_id.GetUniqueId() ) % 10));
        entityRight *= size;
        entityUp *= size * 2.0f;

        glDepthMask     ( false );
        glEnable        ( GL_BLEND );
        glBlendFunc     ( GL_SRC_ALPHA, GL_ONE );
        glEnable        ( GL_TEXTURE_2D );
        glBindTexture( GL_TEXTURE_2D, g_app->m_resource->GetTexture( "sprites/nuke.bmp" ) );

        RGBAColour colour = g_app->m_location->m_teams[m_id.GetTeamId()]->m_colour;
        if( m_id.GetTeamId() == g_app->m_location->GetMonsterTeamId() )
        {
            glBlendFunc     ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR );
            colour.Set( 255, 255, 255, 0 );
        }
        glColor4ubv(colour.GetData());

        glBegin(GL_QUADS);
            glTexCoord2i(0, 1);     glVertex3dv( (predictedPos - entityRight + entityUp).GetData() );
            glTexCoord2i(1, 1);     glVertex3dv( (predictedPos + entityRight + entityUp).GetData() );
            glTexCoord2i(1, 0);     glVertex3dv( (predictedPos + entityRight).GetData() );
            glTexCoord2i(0, 0);     glVertex3dv( (predictedPos - entityRight).GetData() );
        glEnd();

        RenderHistory( _predictionTime );

        glShadeModel    ( GL_FLAT );
        glDisable       ( GL_TEXTURE_2D );
        glDepthMask     ( true );
    }
}