예제 #1
0
// begin/end model rendering to screen
void BeginModelRenderingView( CAnyProjection3D &prProjection, CDrawPort *pdp)
{
  ASSERT( _iRenderingType==0 && _pdp==NULL);

  // set 3D projection
  _iRenderingType = 1;
  _pdp = pdp;
  prProjection->ObjectPlacementL() = CPlacement3D(FLOAT3D(0,0,0), ANGLE3D(0,0,0));
  prProjection->Prepare();
  // in case of mirror projection, move mirror clip plane a bit father from the mirrored models,
  // so we have less clipping (for instance, player feet)
  if( prProjection->pr_bMirror) prProjection->pr_plMirrorView.pl_distance -= 0.06f; // -0.06 is because entire projection is offseted by +0.05
  _aprProjection = prProjection;
  _pdp->SetProjection( _aprProjection);
  // make FPU precision low
  _fpuOldPrecision = GetFPUPrecision(); 
  SetFPUPrecision(FPT_24BIT);

  // prepare common arrays for simple shadows rendering
  _avtxCommon.PopAll();
  _atexCommon.PopAll();
  _acolCommon.PopAll();

  // eventually setup truform
  extern INDEX gap_bForceTruform;
  extern INDEX ogl_bTruformLinearNormals;
  if( ogl_bTruformLinearNormals) ogl_bTruformLinearNormals = 1;
  if( gap_bForceTruform) {
    gap_bForceTruform = 1;
    gfxSetTruform( _pGfx->gl_iTessellationLevel, ogl_bTruformLinearNormals);
  }
}
예제 #2
0
// Prepare scene for terrain rendering
void PrepareScene(CAnyProjection3D &apr, CDrawPort *pdp, CTerrain *ptrTerrain)
{
  ASSERT(ptrTerrain!=NULL);
  ASSERT(ptrTerrain->tr_penEntity!=NULL);

  // Set current terrain
  _ptrTerrain = ptrTerrain;

  // Set drawport
  _pdp = pdp;

  // Prepare and set the projection
  apr->ObjectPlacementL() = CPlacement3D(FLOAT3D(0,0,0), ANGLE3D(0,0,0));
  apr->Prepare();
  _aprProjection = apr;
  _pdp->SetProjection( _aprProjection);

  CEntity *pen = ptrTerrain->tr_penEntity;

  // calculate projection of viewer in absolute space
  const FLOATmatrix3D &mViewer = _aprProjection->pr_ViewerRotationMatrix;
  _vViewer(1) = -mViewer(3,1);
  _vViewer(2) = -mViewer(3,2);
  _vViewer(3) = -mViewer(3,3);
  // calculate projection of viewer in object space
  _vViewerObj = _vViewer * !pen->en_mRotation;


  const CPlacement3D &plTerrain = pen->GetLerpedPlacement();

  _mObjectToView  = mViewer * pen->en_mRotation;
  _vObjectToView  = (plTerrain.pl_PositionVector - _aprProjection->pr_vViewerPosition) * mViewer;

  // make transform matrix 
  const FLOATmatrix3D &m = _mObjectToView;
  const FLOAT3D       &v = _vObjectToView;
  FLOAT glm[16];
  glm[0] = m(1,1);  glm[4] = m(1,2);  glm[ 8] = m(1,3);  glm[12] = v(1);
  glm[1] = m(2,1);  glm[5] = m(2,2);  glm[ 9] = m(2,3);  glm[13] = v(2);
  glm[2] = m(3,1);  glm[6] = m(3,2);  glm[10] = m(3,3);  glm[14] = v(3);
  glm[3] = 0;       glm[7] = 0;       glm[11] = 0;       glm[15] = 1;
  gfxSetViewMatrix(glm);

  // Get viewer in absolute space
  _vViewerAbs = (_aprProjection->ViewerPlacementR().pl_PositionVector - 
                 pen->en_plPlacement.pl_PositionVector) * !pen->en_mRotation;

  gfxDisableBlend();
  gfxDisableTexture();
  gfxDisableAlphaTest();
  gfxEnableDepthTest();
  gfxEnableDepthWrite();
  gfxCullFace(GFX_BACK);
}
예제 #3
0
static void RenderHazeLayer(INDEX itt)
{
  FLOAT3D vObjPosition = _ptrTerrain->tr_penEntity->en_plPlacement.pl_PositionVector;

  _fHazeAdd  = -_haze_hp.hp_fNear;
  _fHazeAdd += _vViewer(1) * (vObjPosition(1) - _aprProjection->pr_vViewerPosition(1));
  _fHazeAdd += _vViewer(2) * (vObjPosition(2) - _aprProjection->pr_vViewerPosition(2));
  _fHazeAdd += _vViewer(3) * (vObjPosition(3) - _aprProjection->pr_vViewerPosition(3));

  GFXVertex *pvVtx;
  INDEX     *piIndices;
  INDEX ctVertices;
  INDEX ctIndices;
  // if this is tile 
  if(itt>=0) {
    CTerrainTile &tt = _ptrTerrain->tr_attTiles[itt];
    pvVtx      = &tt.GetVertices()[0];
    piIndices  = &tt.GetIndices()[0];
    ctVertices = tt.GetVertices().Count();
    ctIndices  = tt.GetIndices().Count();
  // else this are batched tiles
  } else {
    pvVtx      = &_avDelayedVertices[0];
    piIndices  = &_aiDelayedIndices[0];
    ctVertices = _avDelayedVertices.Count();
    ctIndices  = _aiDelayedIndices.Count();
  }

  GFXTexCoord *pfHazeTC  = _atcHaze.Push(ctVertices);
  GFXColor    *pcolHaze  = _acolHaze.Push(ctVertices);

  const COLOR colH = AdjustColor( _haze_hp.hp_colColor, _slTexHueShift, _slTexSaturation);
  GFXColor colHaze(colH);
  // for each vertex in tile
  for(INDEX ivx=0;ivx<ctVertices;ivx++) {
    GetHazeMapInVertex(pvVtx[ivx],pfHazeTC[ivx]);
    pcolHaze[ivx] = colHaze;
  }

  // render haze layer
  gfxDepthFunc(GFX_EQUAL);
  gfxSetTextureWrapping( GFX_CLAMP, GFX_CLAMP);
  gfxSetTexture( _haze_ulTexture, _haze_tpLocal);
  gfxSetTexCoordArray(pfHazeTC, FALSE);
  gfxSetColorArray(pcolHaze);
  gfxBlendFunc( GFX_SRC_ALPHA, GFX_INV_SRC_ALPHA);
  gfxEnableBlend();
  gfxDrawElements(ctIndices,piIndices);
  gfxDepthFunc(GFX_LESS_EQUAL);

  _atcHaze.PopAll();
  _acolHaze.PopAll();
}
예제 #4
0
// Draw one quad tree node ( draws terrain tile in wireframe mode if leaf node )
static void DrawWireQuadTreeNode(INDEX iqtn)
{
  ASSERT(_ptrTerrain!=NULL);
  CEntity *pen = _ptrTerrain->tr_penEntity;
  QuadTreeNode &qtn = _ptrTerrain->tr_aqtnQuadTreeNodes[iqtn];
  
  FLOATmatrix3D &mAbsToView = _aprProjection->pr_ViewerRotationMatrix;
  FLOATobbox3D obbox = FLOATobbox3D( qtn.qtn_aabbox, 
    (pen->en_plPlacement.pl_PositionVector-_aprProjection->pr_vViewerPosition)*mAbsToView, mAbsToView*pen->en_mRotation);

  INDEX iFrustumTest = _aprProjection->TestBoxToFrustum(obbox);
  if(iFrustumTest!=(-1)) {
    // is this leaf node
    if(qtn.qtn_iTileIndex != -1) {
      _ctNodesVis++;
      // draw terrain tile for this node 
      RenderWireTile(qtn.qtn_iTileIndex);
    // this node has some children
    } else {
      for(INDEX iqc=0;iqc<4;iqc++) {
        INDEX iChildNode = qtn.qtn_iChild[iqc];
        // if child node exists
        if(iChildNode != -1) {
          // draw child node
          DrawWireQuadTreeNode(qtn.qtn_iChild[iqc]);
        }
      }
    }
  }
}
예제 #5
0
static void RenderFogLayer(INDEX itt)
{
  FLOATmatrix3D &mViewer = _aprProjection->pr_ViewerRotationMatrix;
  FLOAT3D vObjPosition = _ptrTerrain->tr_penEntity->en_plPlacement.pl_PositionVector;

  // get viewer -z in object space
  _vFViewerObj = FLOAT3D(0,0,-1) * !_mObjectToView;
  // get fog direction in object space
  _vHDirObj = _fog_vHDirAbs * !(!mViewer*_mObjectToView);
  // get viewer offset
  _fFogAddZ  = _vViewer(1) * (vObjPosition(1) - _aprProjection->pr_vViewerPosition(1));
  _fFogAddZ += _vViewer(2) * (vObjPosition(2) - _aprProjection->pr_vViewerPosition(2));
  _fFogAddZ += _vViewer(3) * (vObjPosition(3) - _aprProjection->pr_vViewerPosition(3));
  // get fog offset
  _fFogAddH = (_fog_vHDirAbs % vObjPosition) + _fog_fp.fp_fH3;

  GFXVertex *pvVtx;
  INDEX     *piIndices;
  INDEX ctVertices;
  INDEX ctIndices;
  // if this is tile 
  if(itt>=0) {
    CTerrainTile &tt = _ptrTerrain->tr_attTiles[itt];
    pvVtx      = &tt.GetVertices()[0];
    piIndices  = &tt.GetIndices()[0];
    ctVertices = tt.GetVertices().Count();
    ctIndices  = tt.GetIndices().Count();
  // else this are batched tiles
  } else {
    pvVtx      = &_avDelayedVertices[0];
    piIndices  = &_aiDelayedIndices[0];
    ctVertices = _avDelayedVertices.Count();
    ctIndices  = _aiDelayedIndices.Count();
  }

  GFXTexCoord *pfFogTC  = _atcHaze.Push(ctVertices);
  GFXColor    *pcolFog  = _acolHaze.Push(ctVertices);

  const COLOR colF = AdjustColor( _fog_fp.fp_colColor, _slTexHueShift, _slTexSaturation);
  GFXColor colFog(colF);

  // for each vertex in tile
  for(INDEX ivx=0;ivx<ctVertices;ivx++) {
    GetFogMapInVertex(pvVtx[ivx],pfFogTC[ivx]);
    pcolFog[ivx] = colFog;
  }

  // render fog layer
  gfxDepthFunc(GFX_EQUAL);
  gfxSetTextureWrapping( GFX_CLAMP, GFX_CLAMP);
  gfxSetTexture( _fog_ulTexture, _fog_tpLocal);
  gfxSetTexCoordArray(pfFogTC, FALSE);
  gfxSetColorArray(pcolFog);
  gfxBlendFunc( GFX_SRC_ALPHA, GFX_INV_SRC_ALPHA);
  gfxEnableBlend();
  gfxDisableAlphaTest();
  gfxDrawElements(ctIndices,piIndices);
  gfxDepthFunc(GFX_LESS_EQUAL);

  _atcHaze.PopAll();
  _acolHaze.PopAll();
}
예제 #6
0
// setup CRenderModel class for rendering one model and eventually it's shadow(s)
void CModelObject::SetupModelRendering( CRenderModel &rm)
{
  _sfStats.IncrementCounter( CStatForm::SCI_MODELS);
  _pfModelProfile.StartTimer( CModelProfile::PTI_INITMODELRENDERING);
  _pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_INITMODELRENDERING);

  // get model's data and lerp info
  rm.rm_pmdModelData = (CModelData*)GetData();
  GetFrame( rm.rm_iFrame0, rm.rm_iFrame1, rm.rm_fRatio);
  const INDEX ctVertices = rm.rm_pmdModelData->md_VerticesCt;
  if( rm.rm_pmdModelData->md_Flags & MF_COMPRESSED_16BIT) {
    // set pFrame to point to last and next frames' vertices
    rm.rm_pFrame16_0 = &rm.rm_pmdModelData->md_FrameVertices16[rm.rm_iFrame0 *ctVertices];
    rm.rm_pFrame16_1 = &rm.rm_pmdModelData->md_FrameVertices16[rm.rm_iFrame1 *ctVertices];
  } else {
    // set pFrame to point to last and next frames' vertices
    rm.rm_pFrame8_0 = &rm.rm_pmdModelData->md_FrameVertices8[rm.rm_iFrame0 *ctVertices];
    rm.rm_pFrame8_1 = &rm.rm_pmdModelData->md_FrameVertices8[rm.rm_iFrame1 *ctVertices];
  }

  // obtain current rendering preferences
  rm.rm_rtRenderType = _mrpModelRenderPrefs.GetRenderType();
  // remember blending color
  rm.rm_colBlend = MulColors( rm.rm_colBlend, mo_colBlendColor);

  // get decompression/stretch factors
  FLOAT3D &vDataStretch = rm.rm_pmdModelData->md_Stretch;
  rm.rm_vStretch(1) = vDataStretch(1) * mo_Stretch(1);
  rm.rm_vStretch(2) = vDataStretch(2) * mo_Stretch(2);
  rm.rm_vStretch(3) = vDataStretch(3) * mo_Stretch(3);
  rm.rm_vOffset     = rm.rm_pmdModelData->md_vCompressedCenter;
  // check if object is inverted (in mirror)
  BOOL bXInverted = rm.rm_vStretch(1) < 0;
  BOOL bYInverted = rm.rm_vStretch(2) < 0;
  BOOL bZInverted = rm.rm_vStretch(3) < 0;
  rm.rm_ulFlags &= ~RMF_INVERTED;
  if( bXInverted != bYInverted != bZInverted != _aprProjection->pr_bInverted) rm.rm_ulFlags |= RMF_INVERTED;

  // prepare projections
  _pfModelProfile.StartTimer( CModelProfile::PTI_INITPROJECTION);
  _pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_INITPROJECTION);
  PrepareView(rm);
  _pfModelProfile.StopTimer( CModelProfile::PTI_INITPROJECTION);

  // get mip factor from projection (if needed)
  if( (INDEX&)rm.rm_fDistanceFactor==12345678) {
    FLOAT3D vObjectAbs;
    _aprProjection->PreClip( rm.rm_vObjectPosition, vObjectAbs);
    rm.rm_fDistanceFactor = _aprProjection->MipFactor( Min(vObjectAbs(3), 0.0f));
  }
  // adjust mip factor in case of dynamic stretch factor
  if( mo_Stretch != FLOAT3D(1,1,1)) {
    rm.rm_fMipFactor = rm.rm_fDistanceFactor - Log2( Max(mo_Stretch(1),Max(mo_Stretch(2),mo_Stretch(3))));
  } else {
    rm.rm_fMipFactor = rm.rm_fDistanceFactor;
  }
  // adjust mip factor by custom settings
  rm.rm_fMipFactor = rm.rm_fMipFactor*mdl_fLODMul +mdl_fLODAdd;

  // get current mip model using mip factor
  rm.rm_iMipLevel = GetMipModel( rm.rm_fMipFactor);
  mo_iLastRenderMipLevel = rm.rm_iMipLevel;
  // get current vertices mask
  rm.rm_pmmiMip = &rm.rm_pmdModelData->md_MipInfos[rm.rm_iMipLevel];

  // don't allow any shading, if shading is turned off
  if( rm.rm_rtRenderType & RT_SHADING_NONE) {
    rm.rm_colAmbient = C_WHITE|CT_OPAQUE;
    rm.rm_colLight   = C_BLACK;
  }

  // calculate light vector as seen from model, so that vertex normals
  // do not need to be transformed for lighting calculations
  FLOAT fLightDirection=(rm.rm_vLightDirection).Length();
  if( fLightDirection>0.001f) {
    rm.rm_vLightDirection /= fLightDirection;
  } else {
    rm.rm_vLightDirection = FLOAT3D(0,0,0);
  }
  rm.rm_vLightObj = rm.rm_vLightDirection * !rm.rm_mObjectRotation;

  // precalculate rendering data if needed
  extern void PrepareModelForRendering( CModelData &md);
  PrepareModelForRendering( *rm.rm_pmdModelData);

  // done with setup if viewing from this model
  if( rm.rm_ulFlags&RMF_SPECTATOR) {
    _pfModelProfile.StopTimer( CModelProfile::PTI_INITMODELRENDERING);
    return;
  }

  _pfModelProfile.StartTimer( CModelProfile::PTI_INITATTACHMENTS);
  // for each attachment on this model object
  FOREACHINLIST( CAttachmentModelObject, amo_lnInMain, mo_lhAttachments, itamo) {
    _pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_INITATTACHMENTS);
    CAttachmentModelObject *pamo = itamo;
    // create new render model structure
    pamo->amo_prm = &_armRenderModels.Push();
    const BOOL bVisible = CreateAttachment( rm, *pamo);
    if( !bVisible) { // skip if not visible
      pamo->amo_prm = NULL;
      _armRenderModels.Pop();
      continue;
    } // prepare if visible
    _pfModelProfile.StopTimer( CModelProfile::PTI_INITMODELRENDERING);
    _pfModelProfile.StopTimer( CModelProfile::PTI_INITATTACHMENTS);
    pamo->amo_moModelObject.SetupModelRendering( *pamo->amo_prm);
    _pfModelProfile.StartTimer( CModelProfile::PTI_INITATTACHMENTS);
    _pfModelProfile.StartTimer( CModelProfile::PTI_INITMODELRENDERING);
  }
예제 #7
0
// Create render model structure for rendering an attached model
BOOL CModelObject::CreateAttachment( CRenderModel &rmMain, CAttachmentModelObject &amo)
{
  _pfModelProfile.StartTimer( CModelProfile::PTI_CREATEATTACHMENT);
  _pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_CREATEATTACHMENT);
  CRenderModel &rmAttached = *amo.amo_prm;
  rmAttached.rm_ulFlags = rmMain.rm_ulFlags&(RMF_FOG|RMF_HAZE|RMF_WEAPON) | RMF_ATTACHMENT;

  // get the position
  rmMain.rm_pmdModelData->md_aampAttachedPosition.Lock();
  const CAttachedModelPosition &amp = rmMain.rm_pmdModelData->md_aampAttachedPosition[amo.amo_iAttachedPosition];
  rmMain.rm_pmdModelData->md_aampAttachedPosition.Unlock();

  // copy common values
  rmAttached.rm_vLightDirection = rmMain.rm_vLightDirection;
  rmAttached.rm_fDistanceFactor = rmMain.rm_fDistanceFactor;
  rmAttached.rm_colLight   = rmMain.rm_colLight;
  rmAttached.rm_colAmbient = rmMain.rm_colAmbient;
  rmAttached.rm_colBlend   = rmMain.rm_colBlend;

  // unpack the reference vertices
  FLOAT3D vCenter, vFront, vUp;
  const INDEX iCenter = amp.amp_iCenterVertex;
  const INDEX iFront  = amp.amp_iFrontVertex;
  const INDEX iUp     = amp.amp_iUpVertex;
  UnpackVertex( rmMain, iCenter, vCenter);
  UnpackVertex( rmMain, iFront,  vFront);
  UnpackVertex( rmMain, iUp,     vUp);

  // create front and up direction vectors
  FLOAT3D vY = vUp - vCenter;
  FLOAT3D vZ = vCenter - vFront;
  // project center and directions from object to absolute space
  const FLOATmatrix3D &mO2A = rmMain.rm_mObjectRotation;
  const FLOAT3D &vO2A = rmMain.rm_vObjectPosition;
  vCenter = vCenter*mO2A +vO2A;
  vY = vY *mO2A;
  vZ = vZ *mO2A;

  // make a rotation matrix from the direction vectors
  FLOAT3D vX = vY*vZ;
  vY = vZ*vX;
  vX.Normalize();
  vY.Normalize();
  vZ.Normalize();
  FLOATmatrix3D mOrientation;
  mOrientation(1,1) = vX(1);  mOrientation(1,2) = vY(1);  mOrientation(1,3) = vZ(1);
  mOrientation(2,1) = vX(2);  mOrientation(2,2) = vY(2);  mOrientation(2,3) = vZ(2);
  mOrientation(3,1) = vX(3);  mOrientation(3,2) = vY(3);  mOrientation(3,3) = vZ(3);

  // adjust for relative placement of the attachment
  FLOAT3D vOffset;
  FLOATmatrix3D mRelative;
  MakeRotationMatrixFast( mRelative, amo.amo_plRelative.pl_OrientationAngle);
  vOffset(1) = amo.amo_plRelative.pl_PositionVector(1) * mo_Stretch(1);
  vOffset(2) = amo.amo_plRelative.pl_PositionVector(2) * mo_Stretch(2);
  vOffset(3) = amo.amo_plRelative.pl_PositionVector(3) * mo_Stretch(3);
  FLOAT3D vO = vCenter + vOffset * mOrientation;
  mOrientation *= mRelative; // convert absolute to relative orientation
  rmAttached.SetObjectPlacement( vO, mOrientation);

  // done here if clipping optimizations are not allowed
  extern INDEX gap_iOptimizeClipping;
  if( gap_iOptimizeClipping<1) { 
    gap_iOptimizeClipping = 0;
    _pfModelProfile.StopTimer( CModelProfile::PTI_CREATEATTACHMENT);
    return TRUE;
  }

  // test attachment to frustum and/or mirror
  FLOAT3D vHandle;
  _aprProjection->PreClip( vO, vHandle);
  CalculateBoundingBox( &amo.amo_moModelObject, rmAttached);

  // compose view-space bounding box and sphere of an attacment
  const FLOAT fR = Max( rmAttached.rm_vObjectMinBB.Length(), rmAttached.rm_vObjectMaxBB.Length());
  const FLOATobbox3D boxEntity( FLOATaabbox3D(rmAttached.rm_vObjectMinBB, rmAttached.rm_vObjectMaxBB),
                                vHandle, _aprProjection->pr_ViewerRotationMatrix*mOrientation);
  // frustum test?
  if( gap_iOptimizeClipping>1) {
    // test sphere against frustrum
    INDEX iFrustumTest = _aprProjection->TestSphereToFrustum(vHandle,fR);
    if( iFrustumTest==0) {
      // test box if sphere cut one of frustum planes
      iFrustumTest = _aprProjection->TestBoxToFrustum(boxEntity);
    }
    // mark if attachment is fully inside frustum
         if( iFrustumTest>0) rmAttached.rm_ulFlags |= RMF_INSIDE; 
    else if( iFrustumTest<0) { // if completely outside of frustum
      // signal skip rendering only if doesn't have any attachments
      _pfModelProfile.StopTimer( CModelProfile::PTI_CREATEATTACHMENT);
      return !amo.amo_moModelObject.mo_lhAttachments.IsEmpty(); 
    }
  }
  // test sphere against mirror/warp plane (if any)
  if( _aprProjection->pr_bMirror || _aprProjection->pr_bWarp) {
    INDEX iMirrorPlaneTest;
    const FLOAT fPlaneDistance = _aprProjection->pr_plMirrorView.PointDistance(vHandle);
         if( fPlaneDistance < -fR) iMirrorPlaneTest = -1;
    else if( fPlaneDistance > +fR) iMirrorPlaneTest = +1;
    else { // test box if sphere cut mirror plane
      iMirrorPlaneTest = boxEntity.TestAgainstPlane(_aprProjection->pr_plMirrorView);
    }
    // mark if attachment is fully inside mirror
         if( iMirrorPlaneTest>0) rmAttached.rm_ulFlags |= RMF_INMIRROR; 
    else if( iMirrorPlaneTest<0) { // if completely outside mirror
      // signal skip rendering only if doesn't have any attachments
      _pfModelProfile.StopTimer( CModelProfile::PTI_CREATEATTACHMENT);
      return !amo.amo_moModelObject.mo_lhAttachments.IsEmpty(); 
    }
  } 
  // all done
  _pfModelProfile.StopTimer( CModelProfile::PTI_CREATEATTACHMENT);
  return TRUE;
}
예제 #8
0
void CAM_Render(CEntity *pen, CDrawPort *pdp)
{
  if( cam_bRecord) {
    if (!_bInitialized) {
      _bInitialized = TRUE;
      SetSpeed(1.0f);
      _fStartTime = _pTimer->CurrentTick();
    }
    FLOATmatrix3D m;
    MakeRotationMatrixFast(m, _cp.cp_aRot);
    FLOAT3D vX, vY, vZ;
    vX(1) = m(1,1); vX(2) = m(2,1); vX(3) = m(3,1);
    vY(1) = m(1,2); vY(2) = m(2,2); vY(3) = m(3,2);
    vZ(1) = m(1,3); vZ(2) = m(2,3); vZ(3) = m(3,3);

    _cp.cp_aRot(1)-=_pInput->GetAxisValue(MOUSE_X_AXIS)*0.5f;
    _cp.cp_aRot(2)-=_pInput->GetAxisValue(MOUSE_Y_AXIS)*0.5f;
    
    if( cam_bMoveForward)      { _cp.cp_vPos -= vZ *cam_fSpeed; };
    if( cam_bMoveBackward)     { _cp.cp_vPos += vZ *cam_fSpeed; };
    if( cam_bMoveLeft)         { _cp.cp_vPos -= vX *cam_fSpeed; };
    if( cam_bMoveRight)        { _cp.cp_vPos += vX *cam_fSpeed; };
    if( cam_bMoveUp)           { _cp.cp_vPos += vY *cam_fSpeed; };
    if( cam_bMoveDown)         { _cp.cp_vPos -= vY *cam_fSpeed; };
    if( cam_bTurnBankingLeft)  { _cp.cp_aRot(3) += 10.0f; };
    if( cam_bTurnBankingRight) { _cp.cp_aRot(3) -= 10.0f; };
    if( cam_bZoomIn)           { _cp.cp_aFOV -= 1.0f; };
    if( cam_bZoomOut)          { _cp.cp_aFOV += 1.0f; };
    if( cam_bZoomDefault)      { _cp.cp_aFOV  = 90.0f; };
    Clamp( _cp.cp_aFOV, 10.0f, 150.0f);

    if( cam_bResetToPlayer) {
      _cp.cp_vPos = pen->GetPlacement().pl_PositionVector;
      _cp.cp_aRot = pen->GetPlacement().pl_OrientationAngle;
    }

    if( cam_bSnapshot) {
      cam_bSnapshot = FALSE;
      WritePos(_cp);
    }

  } else {
    if (!_bInitialized) {
      _bInitialized = TRUE;
      ReadPos(_cp0);
      ReadPos(_cp1);
      SetSpeed(_cp0.cp_fSpeed);
      _fStartTime = _pTimer->CurrentTick();
    }
    TIME tmNow = _pTimer->GetLerpedCurrentTick()-_fStartTime;
    if (tmNow>_cp1.cp_tmTick) {
      _cp0 = _cp1;
      ReadPos(_cp1);
      SetSpeed(_cp0.cp_fSpeed);
    }
    FLOAT fRatio = (tmNow-_cp0.cp_tmTick)/(_cp1.cp_tmTick-_cp0.cp_tmTick);

    _cp.cp_vPos = Lerp(_cp0.cp_vPos, _cp1.cp_vPos, fRatio);
    _cp.cp_aRot = Lerp(_cp0.cp_aRot, _cp1.cp_aRot, fRatio);
    _cp.cp_aFOV = Lerp(_cp0.cp_aFOV, _cp1.cp_aFOV, fRatio);
  }

  CPlacement3D plCamera;
  plCamera.pl_PositionVector = _cp.cp_vPos;
  plCamera.pl_OrientationAngle = _cp.cp_aRot;

  // init projection parameters
  CPerspectiveProjection3D prPerspectiveProjection;
  prPerspectiveProjection.FOVL() = _cp.cp_aFOV;
  prPerspectiveProjection.ScreenBBoxL() = FLOATaabbox2D(
    FLOAT2D(0.0f, 0.0f), FLOAT2D((float)pdp->GetWidth(), (float)pdp->GetHeight())
  );
  prPerspectiveProjection.AspectRatioL() = 1.0f;
  prPerspectiveProjection.FrontClipDistanceL() = 0.3f;

  CAnyProjection3D prProjection;
  prProjection = prPerspectiveProjection;

  // set up viewer position
  prProjection->ViewerPlacementL() = plCamera;
  // render the view
  RenderView(*pen->en_pwoWorld, *(CEntity*)NULL, prProjection, *pdp);
}