// 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; }