void VVirtualThumbStick::SetValidArea(const VRectanglef& validArea)
{
  VRectanglef finalValidArea = validArea;
  if (!finalValidArea.IsValid())
  {
    // default area: square (width is 50% of screen height)
    const float fScreenWidth = static_cast<float>(Vision::Video.GetXRes());
    const float fScreenHeight = static_cast<float>(Vision::Video.GetYRes());
    const float fSquareWidth = hkvMath::Min(fScreenHeight, fScreenWidth) * 0.5f;

    finalValidArea.Set(
      hkvVec2(0.0f, fScreenHeight - fSquareWidth), 
      hkvVec2(fSquareWidth, fScreenHeight));
  }

  // create /resize touch area
  if (m_spTouchArea == NULL)
  {
    IVMultiTouchInput& inputDevice = static_cast<IVMultiTouchInput&>(
      VInputManager::GetInputDevice(INPUT_DEVICE_TOUCHSCREEN));
    VASSERT(&inputDevice != &VInputManager::s_NoInputDevice);

    m_spTouchArea = new VTouchArea(inputDevice, finalValidArea, -1500.0f);
  }
  else
  {
    m_spTouchArea->SetArea(finalValidArea);
  }

  m_validArea = finalValidArea;

  Reset();
}
void VVirtualThumbStick::Reset()
{
  m_fXValue = 0.0f;
  m_fYValue = 0.0f;
  m_iLastTouchPointIndex = -1;

  // (Re)position thumb stick
  const VRectanglef finalValidArea = m_spTouchArea->GetArea();

  // Outer ring mask
  const float fInitialX = finalValidArea.m_vMin.x + finalValidArea.GetSizeX() * m_fRelativeInitialX;
  const float fInitialY = finalValidArea.m_vMin.y + finalValidArea.GetSizeY() * m_fRelativeInitialY;

  m_spRingMask->GetTextureSize(m_iRingWidth, m_iRingHeight);
  m_spRingMask->SetPos(
    fInitialX - static_cast<float>(m_iRingWidth / 2), 
    fInitialY - static_cast<float>(m_iRingHeight / 2));  

  // Inner circle mask
  m_iCircleCenterX = static_cast<int>(fInitialX);
  m_iCircleCenterY = static_cast<int>(fInitialY);

  m_spCircleMask->GetTextureSize(m_iCircleWidth, m_iCircleHeight);
  m_spCircleMask->SetPos(
    fInitialX - static_cast<float>(m_iCircleWidth / 2), 
    fInitialY - static_cast<float>(m_iCircleHeight / 2));
}
int VRTLSupport_cl::DistanceToRightAlignment(VisFont_cl * pFont, const char * pText) {

  VASSERT(pFont);

  VRectanglef requiredSpace;
  pFont->GetTextDimension(pText, requiredSpace);

  return (int)-requiredSpace.GetSizeX();
}
void VInfoDialog::DoLayout()
{
  const float fResX = (float)Vision::Video.GetXRes();
  const float fResY = (float)Vision::Video.GetYRes();
  const float fOuterBorderWidth = VUISharedData::GetOuterBorderWidth();
  const hkvVec2 buttonSize = hkvVec2(fResX * 0.25f - fOuterBorderWidth * 1.5f, VUISharedData::GetIconSize());

  {
    VRectanglef textDimensions;
    m_pTextLabel->Text().GetFont()->GetTextDimension(m_pTextLabel->GetText(), textDimensions);

    float fTextArea = textDimensions.GetArea() * m_pTextLabel->Text().GetScaling() * m_pTextLabel->Text().GetScaling();

    // Estimate suitable text width based on text area
    float fTextWidth = hkvMath::clamp(1.4f * hkvMath::sqrt(fTextArea), fResX * 0.5f, fResX * 0.8f);

    // Set initial dialog size estimate
    SetSize(fTextWidth, fTextWidth);

    // Paint once without graphics to compute actual text size
    VTextState &state(m_pTextLabel->Text().m_States[m_pTextLabel->GetCurrentState()]);
    state.Paint(NULL, this, V_RGBA_WHITE);
    
    float fTextHeight = state.GetHeight() * m_pTextLabel->Text().GetScaling();

    // Reinvalidate computed line wrapping etc.
    state.UpdateAlignment();

    // Set final size
    float fDialogWidth = fTextWidth + fOuterBorderWidth * 2;
    float fDialogHeight = hkvMath::Min(fTextHeight + buttonSize.y + fOuterBorderWidth * 3, fResY * 0.8f);
    SetSize(fDialogWidth, fDialogHeight);
  }

  const hkvVec2 dialogSize = GetSize();

  SetPosition((fResX - dialogSize.x) * 0.5f, (fResY - dialogSize.y) * 0.5f);

  m_pTextLabel->SetSize(dialogSize.x - fOuterBorderWidth * 2, dialogSize.y - buttonSize.y - fOuterBorderWidth * 3);
  m_pTextLabel->SetPosition(fOuterBorderWidth, fOuterBorderWidth);

  const float fOkButtonWidth = m_pCancelButton->IsVisible() ? buttonSize.x : dialogSize.x - fOuterBorderWidth * 2;
  m_pOKButton->SetSize(fOkButtonWidth, buttonSize.y);
  m_pOKButton->SetPosition(fOuterBorderWidth, dialogSize.y - buttonSize.y - fOuterBorderWidth);

  m_pCancelButton->SetSize(buttonSize.x, buttonSize.y);
  m_pCancelButton->SetPosition(fOuterBorderWidth * 2 + buttonSize.x, dialogSize.y - buttonSize.y - fOuterBorderWidth);
}
Exemple #5
0
void VUINodeImage::OnVariableValueChanged(VisVariable_cl *pVar, const char * value)
{
	VASSERT (pVar);
	VASSERT (value);

	if (pVar->name && strcmp(pVar->name, "Texture") == 0)
	{
		VTextureObject* spTexture = Vision::TextureManager.Load2DTexture( value );
		Image().SetTexture( spTexture );
	}	
	else if (pVar->name && strcmp(pVar->name, "m_eTransp") == 0)
	{		
		sscanf(value,"%d",&m_eTransp);
		Image().SetTransparency(m_eTransp);
	}
	else if (pVar->name && strcmp(pVar->name, "m_eStretcheMode") == 0)
	{
		sscanf(value,"%d",&m_eStretcheMode);	
		Image().SetStretchMode( m_eStretcheMode );
	}
	else if (pVar->name && strcmp(pVar->name, "ShaderEffect") == 0)
	{
	

	//	Image().SetTechnique( spTexture );
	}
	else if (pVar->name && strcmp(pVar->name, "TextureRange") == 0)
	{
		float fComponents[4];
		int iCount = sscanf(value,"%f/%f/%f/%f",&fComponents[0],&fComponents[1],&fComponents[2],&fComponents[3]);
	
		VRectanglef coord;
		coord.Add( hkvVec2(fComponents[0],fComponents[1]));
		coord.Add( hkvVec2(fComponents[2],fComponents[3]));

		Image().SetTextureRange( coord );
	}
	else if (pVar->name && strcmp(pVar->name, "Color") == 0)
	{
		int iComponents[4];
		int iCount = sscanf(value,"%i/%i/%i/%i",&iComponents[0],&iComponents[1],&iComponents[2],&iComponents[3]);

		VColorRef color(iComponents[0],iComponents[1],iComponents[2],iComponents[3]);	
		SetColor( color );
	}	
}
void VLogoOverlay::RefreshLayout()
{
  if (m_spLogoOverlay == NULL)
    return;

  float fWidth = 0.0f, fHeight = 0.0f;
  m_spLogoOverlay->GetTargetSize(fWidth, fHeight);

  const VRectanglef screenExtents = GetScreenExtents();
  const float fVerticalBorder = 2.0f;
  const float fHorizontalBorder = 18.0f;

  hkvVec2 vPos;

  // X-Coordinate
  if (m_eAlignment == ALIGN_TOPLEFT || m_eAlignment == ALIGN_BOTTOMLEFT)
  {
    vPos.x = screenExtents.m_vMin.x + fHorizontalBorder;
  }
  else if (m_eAlignment == ALIGN_TOPRIGHT || m_eAlignment == ALIGN_BOTTOMRIGHT)
  {
    vPos.x = screenExtents.m_vMax.x - fWidth - fHorizontalBorder;
  }
  else
  {
    vPos.x = screenExtents.m_vMin.x + (screenExtents.GetSizeX() - fWidth) * 0.5f;
  }

  // Y-Coordinate
  if (m_eAlignment == ALIGN_TOPLEFT || m_eAlignment == ALIGN_TOPRIGHT || m_eAlignment == ALIGN_TOP)
  {
    vPos.y = screenExtents.m_vMin.y + fVerticalBorder;
  }
  else
  {
    vPos.y = screenExtents.m_vMax.y - fHeight - fVerticalBorder;
  }
  
  m_spLogoOverlay->SetPos(vPos.x, vPos.y);
}
wchar_t* VArabicSupport_cl::UniformToContextualLine(VisFont_cl * pArabicFont, const wchar_t * pUniformArabic, bool duplicateSpaces) {

  if(pUniformArabic==NULL) return NULL;

  //length of the current line
	int iLength = VArrayHelper_cl<wchar_t>::Length(pUniformArabic);

  //count spaces
  int iNumSpaces = 0;
  if(duplicateSpaces) 
  {
    for(int i=0;i<iLength;i++) 
    {
      if(pUniformArabic[i]==L' ') iNumSpaces++;
    }
  }

  //allocate the target buffer (original length + number of duplicates space + terminator)
  wchar_t *pContextualArabic = (wchar_t *) vMemAlloc(sizeof(wchar_t)*(iLength+iNumSpaces+1));

  //pre-terminate buffer
  pContextualArabic[iLength+iNumSpaces] = 0;

  wchar_t wcCurLetter = pUniformArabic[0];	//start with the first letter
  wchar_t wcPrevLetter = 0;					//init wcPrevLetter, because the assignment is done at the end of the for loop

  //is the current (first) letter an Arabic letter?
  bool bCurIsArabic = isUniformArabic(pUniformArabic[0]);

  bool bPrevIsArabic = false;	//at the beginning the previous letter is never Arabic
  bool bNextIsArabic;			//calculated inside the loop

  //transform and terminate the target buffer
  for(int i=0;i<iLength;i++) 
  {

    //start at the right side with the first character at index 0 (because Arabic is RTL)
    wcCurLetter = pUniformArabic[i];
    bNextIsArabic = i<(iLength-1) ? isUniformArabic(pUniformArabic[i+1]) : false;

    if(bCurIsArabic) 
    {
      //first uniform letter is 'alif madda: 0x0622 (maps to contextual representation 0xFE81)

      int iIndex = wcCurLetter-0x0622;	//number of iterations
      wchar_t wcContextual = 0xFE81;		//contextual representation of 'alif madda

      //calculate contextual representation of current uniform representation
      //(count up transformation steps) 
      for(int j=0;j<iIndex;j++) {
        if(s_pArabicLetterMap[j]==0) wcContextual +=2;
        else if(s_pArabicLetterMap[j]==1) wcContextual +=4;
        //else skip (-1)
      }

      //now adjust character according to the in-word position

      wchar_t wcCandidate = wcContextual; //we experiment with a candidate character since it's not sure that the candidate exists!

      //end character
      if( bPrevIsArabic && isInnerLetter(wcPrevLetter) && (!bNextIsArabic || isEndLetter(wcCurLetter)) ) {
        wcCandidate+=1;

        //start character
      } else if( bNextIsArabic && (!bPrevIsArabic || isEndLetter(wcPrevLetter)) ) {
        if(s_pArabicLetterMap[iIndex]!=0)
          wcCandidate+=2;

        //inner character
      } else if( i>0 && bPrevIsArabic && bNextIsArabic && isInnerLetter(wcPrevLetter) && isInnerLetter(wcCurLetter) ) {
        if(s_pArabicLetterMap[iIndex]!=0)
          wcCandidate+=3;
      }
      //else isolated (default)

      //candidate check (do we have this character in the font..?)
      VRectanglef requiredSpace = GetCharacterDimension(wcCandidate, pArabicFont);
      if(requiredSpace.GetSizeX()<=0)
      {
        wcCandidate = wcContextual; //restore from backup (because the character is missing)

        //just in case: fall-back to non-contextual
        VRectanglef requiredSpace = GetCharacterDimension(wcCandidate, pArabicFont);
        if(requiredSpace.GetSizeX()<=0)	wcCandidate = wcCurLetter;
      }

      #ifdef HK_DEBUG_SLOW
        requiredSpace = GetCharacterDimension(wcCandidate, pArabicFont);
        VASSERT_MSG(requiredSpace.GetSizeX()>0, "Your text contains a character which is not present in your font!")
      #endif

      //candidate approved:
      pContextualArabic[iLength+iNumSpaces-i-1] = wcCandidate;

    } else {
      //invert directional symbols
      switch(wcCurLetter) {
        case L'(': wcCurLetter = L')'; break;
        case L')': wcCurLetter = L'('; break;
        case L'<': wcCurLetter = L'>'; break;
        case L'>': wcCurLetter = L'<'; break;
        case L'{': wcCurLetter = L'}'; break;
        case L'}': wcCurLetter = L'{'; break;
        case L'[': wcCurLetter = L']'; break;
        case L']': wcCurLetter = L'['; break;
        case L'/': wcCurLetter = L'\\'; break;
        case L'\\': wcCurLetter = L'/'; break;
        case L' ':
          if(duplicateSpaces) {
            pContextualArabic[iLength+iNumSpaces-i-1] = L' ';
            iNumSpaces--;
          }
          break;
        default: break;
      }
      pContextualArabic[iLength+iNumSpaces-i-1] = wcCurLetter;
    }

    //prepare for next loop iteration
    wcPrevLetter = wcCurLetter;

    bPrevIsArabic = bCurIsArabic; 
    bCurIsArabic = bNextIsArabic;

  }

  return pContextualArabic;
}
void VTextState::Paint(VGraphicsInfo *pGraphics, VWindowBase *pParentWnd, VColorRef iColor)
{
  m_iNumTextLines = 0;
  const char *szText = GetText();
  if (!m_spFont || !szText || !szText[0])
    return;

  VSimpleRenderState_t state = VGUIManager::DefaultGUIRenderState();
  if (m_fFontScaling!=1.0f)
    state.SetFlag(RENDERSTATEFLAG_FILTERING);

  VRectanglef v = pParentWnd->GetBoundingBox(); // clipping box of parent control

  if (m_bTextWrap)
  {
    float fMaxWidth = v.GetSizeX()/m_fFontScaling;
    float fLineHeight = m_spFont->GetFontHeight() * m_fRelativeFontHeight * m_fFontScaling;
    hkvVec2 vPos = v.m_vMin;

    const char *szStart = szText;
    VMemoryTempBuffer<512> tmpBuffer;

    while (szStart[0])
    {
      // byte offsets
      int iOffset;
      int iNextOffset;

      // search for next newline
      const char *pCR = strchr(szStart, '\n');

      // compute automatic wrap character index
      int iCharCount = m_spFont->GetCharacterIndexAtPos(szStart, fMaxWidth, -1, false);
      int iWrapOffset = VString::GetUTF8CharacterOffset(szStart, iCharCount);

      if (pCR != NULL && (pCR - szStart) < iCharCount)
      {
        // newline occurs before automatic text wrap
        iOffset = static_cast<int>(pCR - szStart);
        iNextOffset = iOffset + 1;
      }
      else
      {
        // automatic text wrap
        iOffset = iWrapOffset;
        // try to find white space
        if (iOffset > 0)
        {
          while (iOffset > 0 && szStart[iOffset] != 0 && szStart[iOffset] != ' ')
            iOffset--;
          if (iOffset == 0) // no whitespace found? then wrap inside word
            iOffset = iWrapOffset;
        }
        else 
          iOffset = 1;
        iNextOffset = iOffset;
      }

      // put together line
      const int iLineBufferSize = (iOffset+1) * sizeof(char);
      tmpBuffer.EnsureCapacity(iLineBufferSize);
      char *szLineBuffer = static_cast<char*>(tmpBuffer.GetBuffer());

      memcpy(szLineBuffer, szStart, iLineBufferSize);
      szLineBuffer[iOffset] = 0;
      m_iNumTextLines++;

      // draw line
      if (pGraphics)
      {
        hkvVec2 vAlignedPos = m_spFont->GetTextPositionOfs(szLineBuffer, v.GetSize(), 
          m_hAlign, m_vAlign) * m_fFontScaling;
        vAlignedPos.set(vAlignedPos.x + vPos.x, vPos.y); // only consider the x-ofs
        m_spFont->PrintText(&pGraphics->Renderer, vAlignedPos, szLineBuffer, iColor, state, 
          m_fFontScaling, m_pCustomBBox ? m_pCustomBBox : &v);
      }
      vPos.y += fLineHeight;

      szStart = &szStart[iNextOffset];
      while (szStart[0] == ' ')
        szStart++;
    }
    return;
  }

  // recompute the alignment offset
  if (!m_bAlignmentValid)
  {
    m_vCurrentOfs = m_vOfs + m_spFont->GetTextPositionOfs(szText, v.GetSize(),
      m_hAlign,m_vAlign) * m_fFontScaling;
    m_bAlignmentValid = true;
  }

  m_iNumTextLines = 1;
  if (pGraphics)
  {
    m_spFont->PrintText(&pGraphics->Renderer, v.m_vMin+m_vCurrentOfs, szText, 
      iColor, state, m_fFontScaling, m_pCustomBBox ? m_pCustomBBox : &v);
  }
}
// TODO: This doesn't handle opaque fullbright surfaces correctly yet, and translucent fullbright surfaces are simply ignored.
void MirrorRenderLoop_cl::OnDoRenderLoop(void *pUserData)
{
  INSERT_PERF_MARKER_SCOPE("MirrorRenderLoop_cl::OnDoRenderLoop");

#if defined (WIN32) || defined (_VISION_XENON) || defined (_VISION_PS3) || defined(_VISION_PSP2) || defined(_VISION_WIIU)
  if (Vision::Editor.GetIgnoreAdvancedEffects())
  {
    // force a black reflection because it won't work with orthographic views
    Vision::RenderLoopHelper.ClearScreen(VisRenderLoopHelper_cl::VCTF_All, V_RGBA_BLACK);
    return;
  }
#endif

  VisRenderContext_cl *pContext = Vision::Contexts.GetCurrentContext();

  const int iRenderFlags = pContext->GetRenderFlags();

  const float fFarClipDist = m_pMirror->GetActualFarClipDistance();

  const VFogParameters &fog = Vision::World.GetFogParameters();
  VColorRef clearColor = (fog.depthMode != VFogParameters::Off) ? fog.iDepthColor : Vision::Renderer.GetDefaultClearColor();
  Vision::RenderLoopHelper.ClearScreen(VisRenderLoopHelper_cl::VCTF_All, clearColor);

  // set the oblique clipping plane...
  pContext->SetCustomProjectionMatrix (m_pMirror->GetObliqueClippingProjection().getPointer ());

  const VisStaticGeometryInstanceCollection_cl *pVisibleGeoInstancesPrimaryOpaquePass;
  const VisStaticGeometryInstanceCollection_cl *pVisibleGeoInstancesSecondaryOpaquePass;
  const VisStaticGeometryInstanceCollection_cl *pVisibleGeoInstancesTransparentPass;
  const VisEntityCollection_cl *pVisEntities;


  // === Visibility Determination ===

  IVisVisibilityCollector_cl *pVisColl = VisRenderContext_cl::GetCurrentContext()->GetVisibilityCollector();
  if (pVisColl == NULL)
    return;
  const VisVisibilityObjectCollection_cl *pVisObjectCollection = pVisColl->GetVisibleVisObjects();

  hkvAlignedBBox box;
  int iVoCount = m_pMirror->GetVisibilityObjectCount();
  int iFrustumCount = 0;
  bool bUseCommonFrustum = false;

  // === Determine Scissor Rect ===
  hkvVec2 vMinScreenSpace, vMaxScreenSpace;
  const hkvAlignedBBox &worldSpaceBox = m_pMirror->GetBoundingBox();

  hkvVec3 vCorners[8];
  worldSpaceBox.getCorners (vCorners);

  VRectanglef scissorRect;
  bool bUseScissorRect = true;
  for (int i=0; i<8; i++)
  {
    float x2d, y2d;
    BOOL bInFrontOfCamera = pContext->Project2D(vCorners[i], x2d, y2d);
    if (bInFrontOfCamera)
    {
      scissorRect.Add(hkvVec2(x2d, y2d));
    }
    else
    {
      bUseScissorRect = false;
      break;
    }
  }

  if (bUseScissorRect)
    Vision::RenderLoopHelper.SetScissorRect(&scissorRect);

  for (int iVo = 0; iVo < iVoCount; iVo++)
  {
    VisVisibilityObject_cl *pVisObj = m_pMirror->GetVisibilityObject(iVo);
    if (pVisObj != NULL && pVisObj->WasVisibleInAnyLastFrame())
    {
      if (iFrustumCount <= MAX_SEPARATE_FRUSTA)
      {
        const hkvAlignedBBox &voBox = pVisObj->GetWorldSpaceBoundingBox();
        box.expandToInclude(voBox);
        if (m_Frustum[iFrustumCount].Set(pContext->GetCamera()->GetPosition(), voBox, true, fFarClipDist))
        {
          iFrustumCount++;
        }
        else
        {
          bUseCommonFrustum = true;
        }
      }
      else
      {
        const hkvAlignedBBox &voBox = pVisObj->GetWorldSpaceBoundingBox();
        box.expandToInclude(voBox);
        bUseCommonFrustum = true;
      }
    }
  }

  if (bUseCommonFrustum)
  {
    iFrustumCount = 1;
    if (!m_Frustum[0].Set(pContext->GetCamera()->GetPosition(), box, true, fFarClipDist))
      iFrustumCount = 0;
  }

  if (iFrustumCount>0)
  {
    for (int i=0; i<iFrustumCount; i++)
    {
      m_visiblePrimaryOpaquePassGeoInstances.Clear();
      m_visibleSecondaryOpaquePassGeoInstances.Clear();
      m_visibleTransparentOpaquePassGeoInstances.Clear();
      m_visEntities.Clear();
      pVisColl->GetVisibleStaticGeometryInstancesForPass(VPT_PrimaryOpaquePass)->DetermineEntriesTouchingFrustum(m_Frustum[i], m_visiblePrimaryOpaquePassGeoInstances);
      pVisColl->GetVisibleStaticGeometryInstancesForPass(VPT_SecondaryOpaquePass)->DetermineEntriesTouchingFrustum(m_Frustum[i], m_visibleSecondaryOpaquePassGeoInstances);
      pVisColl->GetVisibleStaticGeometryInstancesForPass(VPT_TransparentPass)->DetermineEntriesTouchingFrustum(m_Frustum[i], m_visibleTransparentOpaquePassGeoInstances);
      pVisColl->GetVisibleEntities()->DetermineEntriesTouchingFrustum(m_Frustum[i], m_visEntities);
      if (iFrustumCount == 1)
        break;
      m_visiblePrimaryOpaquePassGeoInstances.TagEntries();
      m_visibleSecondaryOpaquePassGeoInstances.TagEntries();
      m_visibleTransparentOpaquePassGeoInstances.TagEntries();
      m_visEntities.TagEntries();
    }
    if (iFrustumCount > 1)
    {
      m_visiblePrimaryOpaquePassGeoInstances.Clear();
      m_visibleSecondaryOpaquePassGeoInstances.Clear();
      m_visibleTransparentOpaquePassGeoInstances.Clear();
      m_visEntities.Clear();
      pVisColl->GetVisibleStaticGeometryInstancesForPass(VPT_PrimaryOpaquePass)->GetTaggedEntries(m_visiblePrimaryOpaquePassGeoInstances);
      pVisColl->GetVisibleStaticGeometryInstancesForPass(VPT_SecondaryOpaquePass)->GetTaggedEntries(m_visibleSecondaryOpaquePassGeoInstances);
      pVisColl->GetVisibleStaticGeometryInstancesForPass(VPT_TransparentPass)->GetTaggedEntries(m_visibleTransparentOpaquePassGeoInstances);
      pVisColl->GetVisibleEntities()->GetTaggedEntries(m_visEntities);
    }
    pVisibleGeoInstancesPrimaryOpaquePass = &m_visiblePrimaryOpaquePassGeoInstances;
    pVisibleGeoInstancesSecondaryOpaquePass = &m_visibleSecondaryOpaquePassGeoInstances;
    pVisibleGeoInstancesTransparentPass = &m_visibleTransparentOpaquePassGeoInstances;
    pVisEntities = &m_visEntities;
  }
  else
  {
    pVisibleGeoInstancesPrimaryOpaquePass = pVisColl->GetVisibleStaticGeometryInstancesForPass(VPT_PrimaryOpaquePass);
    pVisibleGeoInstancesSecondaryOpaquePass = pVisColl->GetVisibleStaticGeometryInstancesForPass(VPT_SecondaryOpaquePass);
    pVisibleGeoInstancesTransparentPass = pVisColl->GetVisibleStaticGeometryInstancesForPass(VPT_TransparentPass);
    pVisEntities = pVisColl->GetVisibleEntities();
  }

  // === End Visibility Determination ===

  if (m_pMirror->GetExecuteRenderHooks())
  {
    VisRenderHookDataObject_cl data(&Vision::Callbacks.OnRenderHook,VRH_PRE_PRIMARY_OPAQUE_PASS_GEOMETRY);
    Vision::Callbacks.OnRenderHook.TriggerCallbacks(&data);
  }

  // Render opaque static geometry
  VASSERT(m_spDefaultLightMapping->m_Shaders.Count()==1);

  TRIGGER_MIRROR_HOOK(VRH_PRE_PRIMARY_OPAQUE_PASS_GEOMETRY)
  VisMirror_cl::VReflectionShaderSets_e shaderMode = m_pMirror->m_eReflectionShaderMode;
  DrawStaticGeometry(*pVisibleGeoInstancesPrimaryOpaquePass, VPT_PrimaryOpaquePass);
  DrawStaticGeometry(*pVisibleGeoInstancesSecondaryOpaquePass, VPT_SecondaryOpaquePass);

  // Render entities
  const VisEntityCollection_cl *pEntities = pVisEntities;
  int iCount = pEntities->GetNumEntries();
  VASSERT(m_spDefaultLightGrid->m_Shaders.Count()==1);
  //VCompiledShaderPass *pLightgridShader = m_spDefaultLightGrid->m_Shaders.GetAt(0);
  int i;
  //bool bUseSimpleShader = shaderMode==VisMirror_cl::AlwaysSimple;

  Vision::RenderLoopHelper.BeginEntityRendering();

  for (i=0;i<iCount;i++)
  { 
    VisBaseEntity_cl *pEnt = pEntities->GetEntry(i);

//    Vision::RenderLoopHelper.TrackLightGridInfo(pEnt);  // important: need to be done in RenderEntityWithSurfaceShaderList

    //if (bUseSimpleShader)
    //{
    //  Vision::RenderLoopHelper.RenderEntityWithShaders(pEnt,1,&pLightgridShader);
    //}
    //else
    {
      VisDrawCallInfo_t surfaceShaderList[RLP_MAX_ENTITY_SURFACES];
      VDynamicMesh *pMesh = pEnt->GetMesh();
      VisSurface_cl **ppSurfaces = pEnt->GetSurfaceArray();
      int iNumSubmeshes = pMesh->GetSubmeshCount();
      for (int j=0; j<iNumSubmeshes; j++)
      {
        VisDrawCallInfo_t &info(surfaceShaderList[j]);
        VBaseSubmesh* pSubmesh = pMesh->GetSubmesh(j);
        VisSurface_cl* pSurface = ppSurfaces[pSubmesh->m_iMaterialIndex];
        info.Set(pSubmesh, pSurface, GetMirrorShader (pSurface, shaderMode));
      }

      Vision::RenderLoopHelper.RenderEntityWithSurfaceShaderList(pEnt, iNumSubmeshes, surfaceShaderList);
    }
  }

  Vision::RenderLoopHelper.EndEntityRendering();

  // Render Sky
  if (VSky::IsVisible())
  {
    // The sky has to be rendered without oblique clipping
    pContext->SetCustomProjectionMatrix(NULL);
    Vision::RenderLoopHelper.RenderSky();
    // set the oblique clipping plane after sky...
    pContext->SetCustomProjectionMatrix (m_pMirror->GetObliqueClippingProjection().getPointer ());
  }

  if (m_pMirror->GetExecuteRenderHooks())
  {
    VisRenderHookDataObject_cl data(&Vision::Callbacks.OnRenderHook,VRH_PRE_OCCLUSION_TESTS);
    Vision::Callbacks.OnRenderHook.TriggerCallbacks(&data);
  }

  // Render Coronas / Lens Flares
  VisRenderHookDataObject_cl data(&Vision::Callbacks.OnRenderHook,VRH_CORONAS_AND_FLARES);
  Vision::Callbacks.OnRenderHook.TriggerCallbacks(&data);
  TRIGGER_MIRROR_HOOK(VRH_PRE_OCCLUSION_TESTS)  

  if (iRenderFlags&VIS_RENDERCONTEXT_FLAG_USE_OCCLUSIONQUERY)
    Vision::RenderLoopHelper.PerformHardwareOcclusionQuery();

  if (iRenderFlags&VIS_RENDERCONTEXT_FLAG_USE_PIXELCOUNTER)
    Vision::RenderLoopHelper.PerformHardwarePixelCounterQuery();

  DrawDynamicLight();

  TRIGGER_MIRROR_HOOK(VRH_DECALS)
  TRIGGER_MIRROR_HOOK(VRH_CORONAS_AND_FLARES)

  TRIGGER_MIRROR_HOOK(VRH_PRE_TRANSPARENT_PASS_GEOMETRY)
  DrawStaticGeometry(*pVisibleGeoInstancesTransparentPass, VPT_TransparentPass);
  TRIGGER_MIRROR_HOOK(VRH_POST_TRANSPARENT_PASS_GEOMETRY)

  if (bUseScissorRect)
    Vision::RenderLoopHelper.SetScissorRect(NULL);
}
void VTextState::Paint(VGraphicsInfo *pGraphics, VWindowBase *pParentWnd, VColorRef iColor)
{
  const char *szText = GetText();
  if (!m_spFont || !szText || !szText[0])
    return;

  VRectanglef vParentRect = pParentWnd->GetClientRect(); // clipping box of parent control

  if(!m_bCachedLinesValid)
  {
    m_lines.Reset();
    m_lineOffsets.Reset();

    float fLineHeight = m_spFont->GetFontHeight() * m_fRelativeFontHeight * m_fFontScaling;

    if(!m_bTextWrap)
    {
      const char* szCurrentLine = szText;
      for (const char* szPtr = szCurrentLine; *szPtr != '\0'; ++szPtr)
      {
        if (*szPtr == '\n')
        {
          VStaticString<512> line;
          line.Set(szCurrentLine, static_cast<int>(szPtr - szCurrentLine));

          m_lines.Add(line.AsChar());
          szCurrentLine = szPtr + 1;
        }
      }

      // Add the last line
      if(*szCurrentLine)
      {
        m_lines.Add(szCurrentLine);
      }
    }
    else
    {
      float fMaxLineWidth = vParentRect.GetSizeX() / m_fFontScaling;

      // Wrap text into individual lines
      {
        const char *szCurrentLine = szText;
        while (*szCurrentLine)
        {
          // byte offsets
          int iByteOffsetAtWrapPosition;
          int iByteOffsetAfterWrapPosition;

          // search for next newline
          const char *pNextNewLine = strchr(szCurrentLine, '\n');

          // compute automatic wrap character index
          int iCharCount = m_spFont->GetCharacterIndexAtPos(szCurrentLine, fMaxLineWidth, -1, false);
          int iWrapOffset = VString::GetUTF8CharacterOffset(szCurrentLine, iCharCount);

          if (pNextNewLine != NULL && (pNextNewLine - szCurrentLine) <= iWrapOffset)
          {
            // newline occurs before automatic text wrap
            iByteOffsetAtWrapPosition = static_cast<int>(pNextNewLine - szCurrentLine);
            iByteOffsetAfterWrapPosition = iByteOffsetAtWrapPosition + 1;
          }
          else if(strlen(szCurrentLine) <= iWrapOffset)
          {
            // End of text occurs before automatic text wrap
            iByteOffsetAtWrapPosition = static_cast<int>(strlen(szCurrentLine));
            iByteOffsetAfterWrapPosition = iByteOffsetAtWrapPosition;
          }
          else
          {
            // automatic text wrap
            iByteOffsetAtWrapPosition = iWrapOffset;
            if (iByteOffsetAtWrapPosition > 0)
            {
              // Go backwards and try to find white space
              while (iByteOffsetAtWrapPosition > 0 && !IsWhiteSpace(szCurrentLine[iByteOffsetAtWrapPosition]))
              {
                iByteOffsetAtWrapPosition--;
              }

              // no whitespace found? then wrap inside word
              if (iByteOffsetAtWrapPosition == 0)
              {
                iByteOffsetAtWrapPosition = iWrapOffset;
              }
              else
              {
                // Find end of next word
                int iEndOfWord = iByteOffsetAtWrapPosition + 1;
                while(szCurrentLine[iEndOfWord] && !IsWhiteSpace(szCurrentLine[iEndOfWord]))
                {
                  iEndOfWord++;
                }

                // If the word does not fit into a line by itself, it will be wrapped anyway, so wrap it early to avoid ragged looking line endings
                VRectanglef nextWordSize;
                m_spFont->GetTextDimension(szCurrentLine + iByteOffsetAtWrapPosition, nextWordSize, iEndOfWord - iByteOffsetAtWrapPosition);
                if(nextWordSize.GetSizeX() > fMaxLineWidth)
                {
                  iByteOffsetAtWrapPosition = iWrapOffset;
                }
              }
            }
            else
            {
              // First character is already wider than the line
              iByteOffsetAtWrapPosition = VString::GetUTF8CharacterOffset(szCurrentLine, 1);
            }
            iByteOffsetAfterWrapPosition = iByteOffsetAtWrapPosition;
          }

          // put together line
          VStaticString<512> line;
          line.Set(szCurrentLine, iByteOffsetAtWrapPosition);

          m_lines.Add(line.AsChar());

          szCurrentLine = &szCurrentLine[iByteOffsetAfterWrapPosition];
          while(*szCurrentLine == ' ')
            szCurrentLine++;
        }
      }
    }

    // Compute offset for each line
    for(int iLineIdx = 0; iLineIdx < m_lines.GetLength(); iLineIdx++)
    {
      hkvVec2 offset = m_vOffset;

      offset.x += m_spFont->GetTextPositionOfs(m_lines[iLineIdx], vParentRect.GetSize(), m_hAlign, m_vAlign, m_fFontScaling).x;
      offset.y += iLineIdx * fLineHeight;

      if (m_vAlign == VisFont_cl::ALIGN_CENTER)
      {
        offset.y += (vParentRect.GetSizeY() - fLineHeight * m_lines.GetSize()) * 0.5f;
      }
      else if (m_vAlign == VisFont_cl::ALIGN_BOTTOM)
      {
        offset.y += (vParentRect.GetSizeY() - fLineHeight * m_lines.GetSize());
      }

      m_lineOffsets.Add(offset);
    }

    m_bCachedLinesValid = true;
  }

  // Render lines
  if (pGraphics)
  {
    VSimpleRenderState_t state = VGUIManager::DefaultGUIRenderState();
    if (m_fFontScaling!=1.0f)
      state.SetFlag(RENDERSTATEFLAG_FILTERING);

    for(int iLineIdx = 0; iLineIdx < m_lines.GetLength(); iLineIdx++)
    {
      m_spFont->PrintText(&pGraphics->Renderer, vParentRect.m_vMin + m_lineOffsets[iLineIdx], m_lines[iLineIdx], iColor, state, 
        m_fFontScaling, m_pCustomBBox ? m_pCustomBBox : &vParentRect);
    }
  }
}
 /// \brief  Query if the touch is in the given area.
 ///
 /// \param  area The area to test.
 ///
 /// \return true if in area, false if not. 
 ///
 inline bool IsInArea(const VRectanglef& area) const
 {
   return area.IsInside(fXAbsolute, fYAbsolute);
 }