// 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); } }
// 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); }
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(); }
// 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]); } } } } }
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(); }
// 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); }
// 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 & = 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; }
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); }