Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

659 lines
21 KiB

//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "uigeometry.h"
#include "materialsystem/imaterialsystem.h"
#include "mathlib/vector.h"
#include "gamegraphic.h"
#include "graphicgroup.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CGeometry::CGeometry()
{
m_Center.x = 0;
m_Center.y = 0;
m_Scale.x = 1;
m_Scale.y = 1;
m_Rotation = 0;
m_Color.r = 255;
m_Color.g = 255;
m_Color.b = 255;
m_Color.a = 255;
m_TopColor.r = 255;
m_TopColor.g = 255;
m_TopColor.b = 255;
m_TopColor.a = 255;
m_BottomColor.r = 255;
m_BottomColor.g = 255;
m_BottomColor.b = 255;
m_BottomColor.a = 255;
m_bHorizontalGradient = false;
m_SheetSequenceNumber = 0;
m_AnimationRate = 1;
m_Sublayer = -1;
m_bMaintainAspectRatio = true;
m_bVisible = true;
m_AnimStartTime = DMETIME_ZERO;
m_bAnimate = false;
m_bDirtyExtents = false;
}
//-----------------------------------------------------------------------------
// Rendering helper
// Calculate a matrix that will transform the points of the geometry, which are
// currently relative to the center, into screen coords.
//-----------------------------------------------------------------------------
void CGeometry::UpdateRenderTransforms( const StageRenderInfo_t &stageRenderInfo, const CGraphicGroup *pGroup )
{
Assert( pGroup );
if ( !pGroup->IsStageGroup() )
{
if ( pGroup->GetVisible() == false )
{
Assert( m_bVisible == pGroup->GetVisible() );
}
}
if ( !m_bVisible )
return;
// Update positions relative to the center, texture coords, and vertex colors
// If the group maintains aspect ratio it will already have handled this in its transform update.
Vector2D center;
bool bUseMaintainedMatrix = m_bMaintainAspectRatio && !pGroup->MaintainAspectRatio();
if ( bUseMaintainedMatrix )
{
// If this is the case we transform the center to screen coords first.
// Then take into account any size scaling in the scalemat
// Note this is not accounting for groups or rotations.
matrix3x4_t screenScalemat;
SetScaleMatrix( stageRenderInfo.parentScale.x, stageRenderInfo.parentScale.y, 1, screenScalemat );
Vector centerVec( m_Center.x, m_Center.y, 0 );
Vector centerInScreen;
VectorTransform( centerVec, screenScalemat, centerInScreen );
// Remove the uniform scale component from the center because it will come back in GetRenderTransform()
float scaleToUse;
if ( stageRenderInfo.parentScale.x > stageRenderInfo.parentScale.y )
{
scaleToUse = stageRenderInfo.parentScale.y;
}
else
{
scaleToUse = stageRenderInfo.parentScale.x;
}
SetScaleMatrix( 1/scaleToUse, 1/scaleToUse, 1, screenScalemat );
Vector tempCenter;
VectorTransform( centerInScreen, screenScalemat, tempCenter );
center.x = tempCenter.x;
center.y = tempCenter.y;
/* old version.
// If this is the case we transform the center to screen coords first.
// Then take into account any size scaling in the scalemat
matrix3x4_t screenScalemat;
SetScaleMatrix( stageRenderInfo.parentScale.x, stageRenderInfo.parentScale.y, 1, screenScalemat );
Vector centerVec( m_Center.x, m_Center.y, 0 );
Vector centerInScreen;
VectorTransform( centerVec, screenScalemat, centerInScreen );
center.x = centerInScreen.x;
center.y = centerInScreen.y;
*/
}
else
{
center = m_Center;
}
matrix3x4_t transmat;
Vector position( center.x, center.y, 0 );
SetIdentityMatrix( transmat );
PositionMatrix( position, transmat );
matrix3x4_t scalemat;
SetScaleMatrix( m_Scale.x, m_Scale.y, 1, scalemat );
matrix3x4_t rotmat;
Vector axis( 0, 0, 1 );
MatrixBuildRotationAboutAxis( axis, m_Rotation, rotmat );
matrix3x4_t temp;
MatrixMultiply( rotmat, scalemat, temp );
matrix3x4_t rawToLocal;
MatrixMultiply( transmat, temp, rawToLocal );
matrix3x4_t groupToScreen;
// Use the matrix that doesn't contain any scale changes if we should
pGroup->GetRenderTransform( groupToScreen, bUseMaintainedMatrix );
MatrixMultiply( groupToScreen, rawToLocal, m_RenderToScreen );
if ( m_bDirtyExtents )
{
CalculateExtentsMatrix( stageRenderInfo, pGroup );
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CGeometry::UpdateRenderData( CUtlVector< RenderGeometryList_t > &renderGeometryLists, int firstListIndex )
{
if ( !m_bVisible )
return;
int i = renderGeometryLists[firstListIndex].AddToTail();
CRenderGeometry &renderGeometry = renderGeometryLists[firstListIndex][i];
// Now transform our array of positions into local graphic coord system.
int nCount = m_RelativePositions.Count();
for ( int i = 0; i < nCount; ++i )
{
// Position
Vector relativePosition( m_RelativePositions[i].x, m_RelativePositions[i].y, 0 );
Vector screenpos;
VectorTransform( relativePosition, m_RenderToScreen, screenpos );
renderGeometry.m_Positions.AddToTail( Vector2D( screenpos.x, screenpos.y ) );;
// TexCoord
renderGeometry.m_TextureCoords.AddToTail( m_TextureCoords[i] );
// Vertex Color
renderGeometry.m_VertexColors.AddToTail( m_VertexColors[i] );
}
// Triangles
nCount = m_Triangles.Count();
for ( int i = 0; i < nCount; ++i )
{
renderGeometry.m_Triangles.AddToTail( m_Triangles[i] );
}
// Anim Info
renderGeometry.m_SheetSequenceNumber = m_SheetSequenceNumber;
renderGeometry.m_AnimationRate = m_AnimationRate;
renderGeometry.m_bAnimate = m_bAnimate;
renderGeometry.m_AnimStartTime = m_AnimStartTime;
renderGeometry.m_pImageAlias = NULL;
CalculateExtents();
}
//-----------------------------------------------------------------------------
// Calculate the rectangular extents of this graphic.
//-----------------------------------------------------------------------------
void CGeometry::CalculateExtentsMatrix( const StageRenderInfo_t &stageRenderInfo, const CGraphicGroup *pGroup )
{
Assert( pGroup );
// Update positions relative to the center, texture coords, and vertex colors
// If the group maintains aspect ratio it will already have handled this in its transform update.
Vector2D center;
bool bUseMaintainedMatrix = m_bMaintainAspectRatio && !pGroup->MaintainAspectRatio();
if ( bUseMaintainedMatrix )
{
// If this is the case we transform the center to screen coords first.
// Then take into account any size scaling in the scalemat
matrix3x4_t screenScalemat;
SetScaleMatrix( stageRenderInfo.parentScale.x, stageRenderInfo.parentScale.y, 1, screenScalemat );
Vector centerVec( m_Center.x, m_Center.y, 0 );
Vector centerInScreen;
VectorTransform( centerVec, screenScalemat, centerInScreen );
center.x = centerInScreen.x;
center.y = centerInScreen.y;
}
else
{
center = m_Center;
}
matrix3x4_t transmat;
Vector position( center.x, center.y, 0 );
SetIdentityMatrix( transmat );
PositionMatrix( position, transmat );
// TODO: account for scaling anims?
matrix3x4_t scalemat;
SetScaleMatrix( m_Scale.x, m_Scale.y, 1, scalemat );
// Rotation is ignored.
matrix3x4_t rotmat;
Vector axis( 0, 0, 1 );
MatrixBuildRotationAboutAxis( axis, 0, rotmat );
matrix3x4_t temp;
MatrixMultiply( rotmat, scalemat, temp );
matrix3x4_t rawToLocal;
MatrixMultiply( transmat, temp, rawToLocal );
matrix3x4_t groupToScreen;
// Use the matrix that doesn't contain any scale changes if we should
pGroup->GetRenderTransform( groupToScreen, bUseMaintainedMatrix );
MatrixMultiply( groupToScreen, rawToLocal, m_ExtentsMatrix );
}
//-----------------------------------------------------------------------------
// Calculate the rectangular extents of this graphic.
//-----------------------------------------------------------------------------
void CGeometry::CalculateExtents()
{
if ( !m_bDirtyExtents )
return;
// Now transform our array of positions into local graphic coord system.
int nCount = m_RelativePositions.Count();
CUtlVector< Vector2D > screenPositions;
for ( int i = 0; i < nCount; ++i )
{
// Position
Vector relativePosition( m_RelativePositions[i].x, m_RelativePositions[i].y, 0 );
Vector screenpos;
VectorTransform( relativePosition, m_ExtentsMatrix, screenpos );
screenPositions.AddToTail( Vector2D( screenpos.x, screenpos.y ) );;
}
// left most position is x.
m_Extents.m_TopLeft.x = INT_MAX;
for ( int i = 0; i < screenPositions.Count(); ++i )
{
if ( m_Extents.m_TopLeft.x > screenPositions[i].x )
m_Extents.m_TopLeft.x = screenPositions[i].x;
}
// top most position is y.
m_Extents.m_TopLeft.y = INT_MAX;
for ( int i = 0; i < screenPositions.Count(); ++i )
{
if ( m_Extents.m_TopLeft.y > screenPositions[i].y )
m_Extents.m_TopLeft.y = screenPositions[i].y;
}
// right most position is x
m_Extents.m_BottomRight.x = INT_MIN;
for ( int i = 0; i < screenPositions.Count(); ++i )
{
if ( m_Extents.m_BottomRight.x < screenPositions[i].x )
m_Extents.m_BottomRight.x = screenPositions[i].x;
}
// bottom most position is y
m_Extents.m_BottomRight.y = INT_MIN;
for ( int i = 0; i < screenPositions.Count(); ++i )
{
if ( m_Extents.m_BottomRight.y < screenPositions[i].y )
m_Extents.m_BottomRight.y = screenPositions[i].y;
}
m_bDirtyExtents = false;
}
//-----------------------------------------------------------------------------
// Return the rectangular bounds of this object
//-----------------------------------------------------------------------------
void CGeometry::GetBounds( Rect_t &bounds )
{
bounds.x = m_Extents.m_TopLeft.x;
bounds.y = m_Extents.m_TopLeft.x;
bounds.width = m_Extents.m_BottomRight.x - m_Extents.m_TopLeft.x;
bounds.height = m_Extents.m_BottomRight.y - m_Extents.m_TopLeft.y;
}
//-----------------------------------------------------------------------------
// Purpose: Set the vertex colors of the graphic using the base color and the gradient colors.
//-----------------------------------------------------------------------------
void CGeometry::SetResultantColor( color32 parentColor )
{
SetResultantColor( true, parentColor );
SetResultantColor( false, parentColor );
}
//-----------------------------------------------------------------------------
// Purpose: Set the vertex colors of the graphic using the base color and the gradient colors.
//-----------------------------------------------------------------------------
void CGeometry::SetResultantColor( bool bTop, color32 parentColor )
{
if ( bTop )
{
color32 localColor;
localColor.r = (int)( (float)m_TopColor.r * (float)(m_Color.r/255.0) );
localColor.g = (int)( (float)m_TopColor.g * (float)(m_Color.g/255.0) );
localColor.b = (int)( (float)m_TopColor.b * (float)(m_Color.b/255.0) );
localColor.a = (int)( (float)m_TopColor.a * (float)(m_Color.a/255.0) );
color32 resultantColor;
resultantColor.r = (int)( (float)localColor.r * (float)(parentColor.r/255.0) );
resultantColor.g = (int)( (float)localColor.g * (float)(parentColor.g/255.0) );
resultantColor.b = (int)( (float)localColor.b * (float)(parentColor.b/255.0) );
resultantColor.a = (int)( (float)localColor.a * (float)(parentColor.a/255.0) );
SetTopVerticesColor( resultantColor );
}
else
{
color32 localColor;
localColor.r = (int)( (float)m_BottomColor.r * (float)(m_Color.r/255.0) );
localColor.g = (int)( (float)m_BottomColor.g * (float)(m_Color.g/255.0) );
localColor.b = (int)( (float)m_BottomColor.b * (float)(m_Color.b/255.0) );
localColor.a = (int)( (float)m_BottomColor.a * (float)(m_Color.a/255.0) );
color32 resultantColor;
resultantColor.r = (int)( (float)localColor.r * (float)(parentColor.r/255.0) );
resultantColor.g = (int)( (float)localColor.g * (float)(parentColor.g/255.0) );
resultantColor.b = (int)( (float)localColor.b * (float)(parentColor.b/255.0) );
resultantColor.a = (int)( (float)localColor.a * (float)(parentColor.a/255.0) );
SetBottomVerticesColor( resultantColor );
}
}
//-----------------------------------------------------------------------------
// Note corner colors will be stomped if the base graphic's color changes.
//-----------------------------------------------------------------------------
void CGeometry::SetTopVerticesColor( color32 c )
{
if ( m_bHorizontalGradient )
{
for ( int i = 0; i < m_VertexColors.Count(); i += 4 )
{
m_VertexColors[i] = c;
m_VertexColors[i+3] = c;
}
}
else
{
for ( int i = 0; i < m_VertexColors.Count(); i += 4 )
{
m_VertexColors[i] = c;
m_VertexColors[i+1] = c;
}
}
}
//-----------------------------------------------------------------------------
// Note corner colors will be stomped if the base graphic's color changes.
//-----------------------------------------------------------------------------
void CGeometry::SetBottomVerticesColor( color32 c )
{
if ( m_bHorizontalGradient )
{
for ( int i = 0; i < m_VertexColors.Count(); i += 4 )
{
m_VertexColors[i+1] = c;
m_VertexColors[i+2] = c;
}
}
else
{
for ( int i = 0; i < m_VertexColors.Count(); i += 4 )
{
m_VertexColors[i + 2] = c;
m_VertexColors[i + 3] = c;
}
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CGeometry::DrawExtents( CUtlVector< RenderGeometryList_t > &renderGeometryLists, int firstListIndex, color32 extentLineColor )
{
if ( !m_bVisible )
return;
float lineWidth = 2.0;
// Time to invent some render data to draw this thing.
{
int i = renderGeometryLists[firstListIndex].AddToTail();
CRenderGeometry &renderGeometry = renderGeometryLists[firstListIndex][i];
renderGeometry.m_Positions.AddToTail( Vector2D( m_Extents.m_TopLeft.x, m_Extents.m_TopLeft.y ) );
renderGeometry.m_TextureCoords.AddToTail( Vector2D( 0 , 0) );
renderGeometry.m_VertexColors.AddToTail( extentLineColor );
renderGeometry.m_Positions.AddToTail( Vector2D( m_Extents.m_BottomRight.x, m_Extents.m_TopLeft.y ) );
renderGeometry.m_TextureCoords.AddToTail( Vector2D( 1 , 0) );
renderGeometry.m_VertexColors.AddToTail( extentLineColor );
renderGeometry.m_Positions.AddToTail( Vector2D( m_Extents.m_BottomRight.x, m_Extents.m_TopLeft.y + lineWidth ) );
renderGeometry.m_TextureCoords.AddToTail( Vector2D( 1 , 1) );
renderGeometry.m_VertexColors.AddToTail( extentLineColor );
renderGeometry.m_Positions.AddToTail( Vector2D( m_Extents.m_TopLeft.x, m_Extents.m_TopLeft.y + lineWidth ) );
renderGeometry.m_TextureCoords.AddToTail( Vector2D( 0 , 1) );
renderGeometry.m_VertexColors.AddToTail( extentLineColor );
// Triangles
CTriangle tri;
tri.m_PointIndex[0] = 0;
tri.m_PointIndex[1] = 1;
tri.m_PointIndex[2] = 2;
renderGeometry.m_Triangles.AddToTail( tri );
tri.m_PointIndex[0] = 0;
tri.m_PointIndex[1] = 2;
tri.m_PointIndex[2] = 3;
renderGeometry.m_Triangles.AddToTail( tri );
// Anim Info
renderGeometry.m_SheetSequenceNumber = 0;
renderGeometry.m_AnimationRate = m_AnimationRate;
renderGeometry.m_bAnimate = false;
renderGeometry.m_AnimStartTime = m_AnimStartTime;
renderGeometry.m_pImageAlias = NULL;
}
{
// Time to invent some render data to draw this thing.
int i = renderGeometryLists[firstListIndex].AddToTail();
CRenderGeometry &renderGeometry = renderGeometryLists[firstListIndex][i];
renderGeometry.m_Positions.AddToTail( Vector2D( m_Extents.m_BottomRight.x - lineWidth, m_Extents.m_TopLeft.y ) );
renderGeometry.m_TextureCoords.AddToTail( Vector2D( 0 , 0) );
renderGeometry.m_VertexColors.AddToTail( extentLineColor );
renderGeometry.m_Positions.AddToTail( Vector2D( m_Extents.m_BottomRight.x, m_Extents.m_TopLeft.y ) );
renderGeometry.m_TextureCoords.AddToTail( Vector2D( 1 , 0) );
renderGeometry.m_VertexColors.AddToTail( extentLineColor );
renderGeometry.m_Positions.AddToTail( Vector2D( m_Extents.m_BottomRight.x, m_Extents.m_BottomRight.y ) );
renderGeometry.m_TextureCoords.AddToTail( Vector2D( 1 , 1) );
renderGeometry.m_VertexColors.AddToTail( extentLineColor );
renderGeometry.m_Positions.AddToTail( Vector2D( m_Extents.m_BottomRight.x - lineWidth, m_Extents.m_BottomRight.y ) );
renderGeometry.m_TextureCoords.AddToTail( Vector2D( 0 , 1) );
renderGeometry.m_VertexColors.AddToTail( extentLineColor );
// Triangles
CTriangle tri;
tri.m_PointIndex[0] = 0;
tri.m_PointIndex[1] = 1;
tri.m_PointIndex[2] = 2;
renderGeometry.m_Triangles.AddToTail( tri );
tri.m_PointIndex[0] = 0;
tri.m_PointIndex[1] = 2;
tri.m_PointIndex[2] = 3;
renderGeometry.m_Triangles.AddToTail( tri );
// Anim Info
renderGeometry.m_SheetSequenceNumber = 0;
renderGeometry.m_AnimationRate = m_AnimationRate;
renderGeometry.m_bAnimate = false;
renderGeometry.m_AnimStartTime = m_AnimStartTime;
renderGeometry.m_pImageAlias = NULL;
}
{
int i = renderGeometryLists[firstListIndex].AddToTail();
CRenderGeometry &renderGeometry = renderGeometryLists[firstListIndex][i];
renderGeometry.m_Positions.AddToTail( Vector2D( m_Extents.m_TopLeft.x, m_Extents.m_BottomRight.y - lineWidth ) );
renderGeometry.m_TextureCoords.AddToTail( Vector2D( 0 , 0) );
renderGeometry.m_VertexColors.AddToTail( extentLineColor );
renderGeometry.m_Positions.AddToTail( Vector2D( m_Extents.m_BottomRight.x, m_Extents.m_BottomRight.y - lineWidth ) );
renderGeometry.m_TextureCoords.AddToTail( Vector2D( 1 , 0) );
renderGeometry.m_VertexColors.AddToTail( extentLineColor );
renderGeometry.m_Positions.AddToTail( Vector2D( m_Extents.m_BottomRight.x, m_Extents.m_BottomRight.y ) );
renderGeometry.m_TextureCoords.AddToTail( Vector2D( 1 , 1) );
renderGeometry.m_VertexColors.AddToTail( extentLineColor );
renderGeometry.m_Positions.AddToTail( Vector2D( m_Extents.m_TopLeft.x, m_Extents.m_BottomRight.y ) );
renderGeometry.m_TextureCoords.AddToTail( Vector2D( 0 , 1) );
renderGeometry.m_VertexColors.AddToTail( extentLineColor );
// Triangles
CTriangle tri;
tri.m_PointIndex[0] = 0;
tri.m_PointIndex[1] = 1;
tri.m_PointIndex[2] = 2;
renderGeometry.m_Triangles.AddToTail( tri );
tri.m_PointIndex[0] = 0;
tri.m_PointIndex[1] = 2;
tri.m_PointIndex[2] = 3;
renderGeometry.m_Triangles.AddToTail( tri );
// Anim Info
renderGeometry.m_SheetSequenceNumber = 0;
renderGeometry.m_AnimationRate = m_AnimationRate;
renderGeometry.m_bAnimate = false;
renderGeometry.m_AnimStartTime = m_AnimStartTime;
renderGeometry.m_pImageAlias = NULL;
}
{
// Time to invent some render data to draw this thing.
int i = renderGeometryLists[firstListIndex].AddToTail();
CRenderGeometry &renderGeometry = renderGeometryLists[firstListIndex][i];
renderGeometry.m_Positions.AddToTail( Vector2D( m_Extents.m_TopLeft.x, m_Extents.m_TopLeft.y ) );
renderGeometry.m_TextureCoords.AddToTail( Vector2D( 0 , 0) );
renderGeometry.m_VertexColors.AddToTail( extentLineColor );
renderGeometry.m_Positions.AddToTail( Vector2D( m_Extents.m_TopLeft.x + lineWidth, m_Extents.m_TopLeft.y ) );
renderGeometry.m_TextureCoords.AddToTail( Vector2D( 1 , 0) );
renderGeometry.m_VertexColors.AddToTail( extentLineColor );
renderGeometry.m_Positions.AddToTail( Vector2D( m_Extents.m_TopLeft.x + lineWidth, m_Extents.m_BottomRight.y ) );
renderGeometry.m_TextureCoords.AddToTail( Vector2D( 1 , 1) );
renderGeometry.m_VertexColors.AddToTail( extentLineColor );
renderGeometry.m_Positions.AddToTail( Vector2D( m_Extents.m_TopLeft.x, m_Extents.m_BottomRight.y ) );
renderGeometry.m_TextureCoords.AddToTail( Vector2D( 0 , 1) );
renderGeometry.m_VertexColors.AddToTail( extentLineColor );
// Triangles
CTriangle tri;
tri.m_PointIndex[0] = 0;
tri.m_PointIndex[1] = 1;
tri.m_PointIndex[2] = 2;
renderGeometry.m_Triangles.AddToTail( tri );
tri.m_PointIndex[0] = 0;
tri.m_PointIndex[1] = 2;
tri.m_PointIndex[2] = 3;
renderGeometry.m_Triangles.AddToTail( tri );
// Anim Info
renderGeometry.m_SheetSequenceNumber = 0;
renderGeometry.m_AnimationRate = m_AnimationRate;
renderGeometry.m_bAnimate = false;
renderGeometry.m_AnimStartTime = m_AnimStartTime;
renderGeometry.m_pImageAlias = NULL;
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int CRenderGeometry::GetTriangleCount()
{
return m_Triangles.Count();
}
int CRenderGeometry::GetVertexCount()
{
return m_Positions.Count();
}
DmeTime_t CRenderGeometry::GetAnimStartTime()
{
return m_AnimStartTime;
}
//-----------------------------------------------------------------------------
// Return true if the point x, y lies inside the triangle.
// Triangle points should be supplied in clockwise dir.
//-----------------------------------------------------------------------------
bool PointTriangleHitTest( Vector2D tringleVert0, Vector2D tringleVert1, Vector2D tringleVert2, Vector2D point )
{
// Compute vectors
Vector2D v0 = tringleVert2 - tringleVert0;
Vector2D v1 = tringleVert1 - tringleVert0;
Vector2D v2 = point - tringleVert0;
// Compute dot products
float dot00 = v0.Dot( v0 );
float dot01 = v0.Dot( v1 );
float dot02 = v0.Dot( v2 );
float dot11 = v1.Dot( v1 );
float dot12 = v1.Dot( v2 );
// Compute barycentric coordinates
float invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
// Check if point is in triangle
return ( u > 0 ) && ( v > 0 ) && ( u + v < 1 );
}