////////////////////////////////////////////////////////////////////// // // Crytek CryENGINE Source code // // File:EntityRender.cpp // Description: rendering of the entity and other relatated to visials stuff // // History: // 14.04.2001:Created by Vladimir Kajalin // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include #include "Entity.h" #include "EntitySystem.h" #include #include #include #include #include "itimer.h" //#include "list2.h" #if defined(_DEBUG) && !defined(LINUX) static char THIS_FILE[] = __FILE__; #define DEBUG_CLIENTBLOCK new( _NORMAL_BLOCK, THIS_FILE, __LINE__) #define new DEBUG_CLIENTBLOCK #endif bool CEntity::DrawEntity(const SRendParams & _EntDrawParams) { FUNCTION_PROFILER( m_pISystem,PROFILE_3DENGINE ); int nRecursionLevel = (int)m_pISystem->GetIRenderer()->EF_Query(EFQ_RecurseLevel) - 1; if(nRecursionLevel==0) { // movement detection (if no recursion) m_vPrevDrawCenter = m_center; m_vPrevDrawAngles = m_angles; m_fPrevDrawScale = m_fScale; } if (m_bHidden) return 0; if(!GetRadius()) return 0; // to check is something was really drawn bool bSomethingWasDrawn = false; // some parameters will be modified SRendParams rParms = _EntDrawParams; // enable lightmaps if alowed /* if(GetRndFlags()&ERF_USELIGHTMAPS && HasLightmap()) { rParms.pLightMapInfo = GetLightmap(); rParms.m_pLMTCBuffer = GetLightmapTexCoord(); } else*/ { rParms.pLightMapInfo = 0; rParms.pLMTCBuffer = 0; } if (!gPrecacheResourcesMode) { // remember last rendered frames if(nRecursionLevel>=0 && nRecursionLevel<=1) SetDrawFrame( m_pISystem->GetIRenderer()->GetFrameID(), nRecursionLevel ); // If entity is drawn and it is set to be update only when visible, awake it for few frames. if (m_eUpdateVisLevel == eUT_PhysicsVisible || m_eUpdateVisLevel == eUT_Visible) { m_awakeCounter = 2; } } // gometry bbox test if(!(GetRndFlags()&ERF_DONOTCHECKVIS)) if(!rParms.pShadowVolumeLightSource && !(rParms.dwFObjFlags & FOB_RENDER_INTO_SHADOWMAP)) { AABB abEntityBBox(m_vBoxMin + m_center, m_vBoxMax + m_center); if(m_bForceBBox) { // if bbox was forced - make it bigger for frustum culling Vec3d vSize = m_vBoxMax - m_vBoxMin; abEntityBBox = AABB(m_vBoxMin - vSize + m_center, m_vBoxMax + vSize + m_center); } if(!m_pISystem->GetViewCamera().IsAABBVisibleFast( abEntityBBox )) return false; } // entity do not cast shadows from it own light if((m_pDynLight && !GetContainer() && rParms.pShadowVolumeLightSource) || (m_pDynLight && m_pDynLight == rParms.pShadowVolumeLightSource)) return false; if (!gPrecacheResourcesMode) { // [PETAR] We need to remember the last frame id when this entity was rendered m_nLastVisibleFrameID = m_pISystem->GetIRenderer()->GetFrameID(); // Awake entity on the first time it is seen. if (m_bSleeping) SetSleep(false); } // set entity params rParms.fScale = GetScale(); if(!(rParms.dwFObjFlags & FOB_RENDER_INTO_SHADOWMAP)) rParms.vPos = GetPos(); rParms.vAngles = GetAngles(); //[Timur][22/3/2003] /* // set custom materials if(m_lstMaterials.Count()) rParms.pMaterials = &m_lstMaterials;*/ IMatInfo * pPrevMaterial = rParms.pMaterial; rParms.pMaterial = m_pMaterial; if(m_bHasEnvLighting) rParms.dwFObjFlags |= FOB_ENVLIGHTING; if(m_arrShaderParams.Num()) rParms.pShaderParams = &m_arrShaderParams; // calculate entity matrix once for all entity objects Matrix44 EntityMatrix; if(rParms.dwFObjFlags & FOB_RENDER_INTO_SHADOWMAP) { //EntityMatrix.Identity(); //EntityMatrix.SetTranslation(rParms.vPos); //EntityMatrix = GetRotationZYX44(-gf_DEGTORAD*rParms.vAngles)*EntityMatrix; //NOTE: angles in radians and negated //EntityMatrix = GetScale33( Vec3d(rParms.fScale,rParms.fScale,rParms.fScale) )*EntityMatrix; //OPTIMISED_BY_IVO Matrix33diag diag = Vec3(rParms.fScale,rParms.fScale,rParms.fScale); //use diag-matrix for scaling Matrix34 rt34 = Matrix34::CreateRotationXYZ( Deg2Rad(rParms.vAngles),rParms.vPos ); //set scaling and translation in one function call EntityMatrix = rt34*diag; //optimised concatenation: m34*t*diag } else { //EntityMatrix.Identity(); //EntityMatrix = GetTranslationMat(GetPos())*EntityMatrix; //EntityMatrix = GetRotationZYX44(-gf_DEGTORAD*rParms.vAngles)*EntityMatrix; //NOTE: angles in radians and negated //EntityMatrix = GetScale33( Vec3d(rParms.fScale,rParms.fScale,rParms.fScale) )*EntityMatrix; //OPTIMISED_BY_IVO Matrix33diag diag = Vec3(rParms.fScale,rParms.fScale,rParms.fScale); //use diag-matrix for scaling Matrix34 rt34 = Matrix34::CreateRotationXYZ( Deg2Rad(rParms.vAngles),GetPos() ); //set scaling and translation in one function call EntityMatrix = rt34*diag; //optimised concatenation: m34*t*diag } EntityMatrix = GetTransposed44(EntityMatrix); //TODO: remove this after E3 and use Matrix34 instead of Matrix44 // lod depends on distance and size and entity settings int nLod = max(0,(int)(rParms.fDistance*GetLodRatioNormilized()/(m_pISystem->GetI3DEngine()->GetObjectsLODRatio()*GetRadius()))); // disable scissoring for entities inside portals if(m_pVisArea && ((IVisArea*)m_pVisArea)->IsPortal()) rParms.dwFObjFlags |= FOB_NOSCISSOR; // draw static components for (unsigned int k=0; kobject; if (!obj || !(cb->flags & ETY_OBJ_INFO_DRAW)) continue; // render if (cb->flags & ETY_OBJ_USE_MATRIX) { rParms.pMatrix = &cb->mtx; if (!rParms.pShadowVolumeLightSource) obj->Render(rParms,Vec3(zero), nLod); else if(GetRndFlags()&ERF_CASTSHADOWVOLUME) obj->RenderShadowVolumes(&rParms); } else { if (rParms.pShadowVolumeLightSource) { rParms.pMatrix = &cb->mtx; // use matrix calculated on prev z/ambient pass rParms.vPos(0,0,0); rParms.vAngles(0,0,0); if (GetRndFlags()&ERF_CASTSHADOWVOLUME) obj->RenderShadowVolumes(&rParms); } else { //Matrix44 mat; //mat.Identity(); //mat=GetTranslationMat(cb->pos)*mat; //mat=GetRotationZYX44(-gf_DEGTORAD*cb->angles)*mat; //NOTE: angles in radians and negated //OPTIMISED_BY_IVO Matrix44 mat=Matrix34::CreateRotationXYZ( Deg2Rad(cb->angles),cb->pos); mat=GetTransposed44(mat); //TODO: remove this after E3 and use Matrix34 instead of Matrix44 mat = mat*EntityMatrix; rParms.pMatrix = &mat; obj->Render(rParms,Vec3(zero), nLod); // cache for shadow volumes and decals cb->mtx = mat; } } bSomethingWasDrawn = true; } //[Timur] Always draw container if its present. if (m_pContainer) { m_pContainer->OnDraw( rParms ); bSomethingWasDrawn = true; } // draw animated component for (int i=0; iGetFlags() & CS_FLAG_DRAW_MODEL)) { if (m_pContainer) { //[Timur] Container Is not used Only to draw characters. This is a general modification of entity. //[Timur] m_pContainer->OnDraw( rParms ); } else { SRendParams RenderParams = rParms; //RenderParams.vAngles = m_physic && m_physic->GetType()==PE_ARTICULATED ? Vec3d(0,0,0) : m_angles; //RenderParams.vAngles = m_angles; if (rParms.pShadowVolumeLightSource) { if(GetRndFlags()&ERF_CASTSHADOWVOLUME) cmodel->RenderShadowVolumes(&RenderParams, (GetRndFlags()&ERF_SELFSHADOW) ? 0 : 10); } else cmodel->Draw(RenderParams,Vec3(123,123,123)); } bSomethingWasDrawn = true; } } DrawEntityDebugInfo(rParms); // restore material to original. rParms.pMaterial = pPrevMaterial; return bSomethingWasDrawn; } void CEntity::DrawEntityDebugInfo(const SRendParams & rParms) { // debug if(m_pEntitySystem->m_pEntityBBoxes->GetIVal()) if (!rParms.pShadowVolumeLightSource) { Vec3d mins,maxs; GetBBox(mins,maxs); m_pISystem->GetIRenderer()->Draw3dBBox(mins,maxs); /*for (unsigned int k=0;kobject; if (!obj) continue; maxs = obj->GetBoxMax() * m_fScale; mins = obj->GetBoxMin() * m_fScale; m_pISystem->GetIRenderer()->Draw3dBBox(m_center+mins,m_center+maxs); }*/ } // debug if(m_pEntitySystem->m_pEntityHelpers->GetIVal()) if (!m_objects.empty()) { std::vector < CEntityObject>::iterator oi; for (oi = m_objects.begin(); oi != m_objects.end(); oi++) { CEntityObject eo =(*oi); if (!eo.object) continue; { CStatObj *so = (CStatObj*)eo.object; Vec3d pos; //CryQuat qRot; int idx=0; char* name; while( name=(char*)eo.object->GetHelperById(idx++, pos) ) { Vec3d hSize = Vec3d( .5f, .5f, .5f ); //Matrix44 mtx; //mtx.Identity(); //mtx=GetTranslationMat(m_center)*mtx; //mtx=GetRotationZYX44(-gf_DEGTORAD*m_angles)*mtx; //NOTE: angles in radians and negated //OPTIMISED_BY_IVO Matrix44 mtx = Matrix34::CreateRotationXYZ( Deg2Rad(m_angles),m_center); mtx=GetTransposed44(mtx); //TODO: remove this after E3 and use Matrix34 instead of Matrix44 pos = mtx.TransformPointOLD(pos); Vec3d mins,maxs; mins = pos - hSize; maxs = pos + hSize; m_pISystem->GetIRenderer()->Draw3dBBox(mins,maxs); m_pISystem->GetIRenderer()->DrawLabel(pos, .73f, name); } } } } } // set bEntityHasLights flag if there are any light sources in entity objects void CEntity::CheckEntityLightSourcesInEntityObjects() { m_bEntityHasLights = false; // Static objects for (unsigned int k=0; kobject; if (!obj)// || !(cb->flags & ETY_OBJ_INFO_DRAW)) continue; if(obj->GetLightSources(0)) m_bEntityHasLights = true; } // Animated components for (int i=0;i GetBoundLight(0)) m_bEntityHasLights = true; } } void CEntity::ProcessEntityLightSources() { //prepare entity matrix //Matrix44 EntityMatrix; //EntityMatrix.Identity(); //EntityMatrix=GetTranslationMat(m_center)*EntityMatrix; //EntityMatrix=GetRotationZYX44(-gf_DEGTORAD*m_angles)*EntityMatrix; //NOTE: angles in radians and negated //EntityMatrix=GetScale33( Vec3d(m_fScale,m_fScale,m_fScale) )*EntityMatrix; FUNCTION_PROFILER( m_pISystem,PROFILE_ENTITY ); if (!m_pEntitySystem->m_pUpdateCoocooEgg->GetIVal()) return; Matrix33diag diag = Vec3d(m_fScale,m_fScale,m_fScale); //use diag-matrix for scaling Matrix34 rt34 = Matrix34::CreateRotationXYZ(Deg2Rad(m_angles),m_center); //set rotation and translation in one function call Matrix44 EntityMatrix = rt34*diag; //optimised concatenation: m34*diag EntityMatrix = GetTransposed44(EntityMatrix); //TODO: remove this after E3 and use Matrix34 instead of Matrix44 // entity light id int nLightId=0; // Static objects for (unsigned int k=0;kobject; if (!obj || !(cb->flags & ETY_OBJ_INFO_DRAW)) continue; if (cb->flags & ETY_OBJ_USE_MATRIX) { for(int i=0; obj->GetLightSources(i); i++) m_pISystem->GetI3DEngine()->AddDynamicLightSource(*obj->GetLightSources(i),this,nLightId++,&cb->mtx); } else { //Matrix44 mat; //mat.Identity(); //mat=GetTranslationMat(cb->pos)*mat; //mat=GetRotationZYX44(-gf_DEGTORAD*cb->angles)*mat; //NOTE: angles in radians and negated //OPTIMISED_BY_IVO Matrix44 mat = Matrix34::CreateRotationXYZ( Deg2Rad(cb->angles),cb->pos); mat=GetTransposed44(mat); //TODO: remove this after E3 and use Matrix34 instead of Matrix44 mat = EntityMatrix*mat; for(int i=0; obj->GetLightSources(i); i++) m_pISystem->GetI3DEngine()->AddDynamicLightSource(*obj->GetLightSources(i),this,nLightId++,&mat); } } // Animated objects for (int i=0;iGetFlags() & CS_FLAG_DRAW_MODEL) && cmodel->GetBoundLight(0)) { assert(!IsStatic()); Matrix44 LightMatrix; if (m_pContainer) { // use character angles and entity pos //LightMatrix.Identity(); //LightMatrix=GetTranslationMat(m_center)*LightMatrix; //LightMatrix=GetRotationZYX44(-gf_DEGTORAD*GetAngles())*LightMatrix; //NOTE: angles in radians and negated //OPTIMISED_BY_IVO LightMatrix = Matrix34::CreateRotationXYZ( Deg2Rad(GetAngles()),m_center); LightMatrix=GetTransposed44(LightMatrix); //TODO: remove this after E3 and use Matrix34 instead of Matrix44 } else LightMatrix = EntityMatrix; for(int i=0; cmodel->GetBoundLight(i); i++) m_pISystem->GetI3DEngine()->AddDynamicLightSource(*cmodel->GetBoundLight(i),this,nLightId++,&LightMatrix); } } } void CEntity::SetEntityStatObj( unsigned int nSlot, IStatObj * pStatObj, Matrix44 * pMatrix ) { if(nSlot>=0 /*&& nSlot= m_objects.size()) m_objects.resize(nSlot+1); if(pMatrix) { m_objects[nSlot].mtx = *pMatrix; m_objects[nSlot].flags |=ETY_OBJ_USE_MATRIX; } else m_objects[nSlot].flags &=~ETY_OBJ_USE_MATRIX; // Release previous object. if (m_objects[nSlot].object != pStatObj && m_objects[nSlot].object != NULL) m_pISystem->GetI3DEngine()->ReleaseObject(m_objects[nSlot].object); m_objects[nSlot].object = pStatObj; } } IStatObj * CEntity::GetEntityStatObj( unsigned int nSlot, Matrix44* pMatrix, bool bReturnOnlyVisible) { if(nSlot>=0 && nSlot=0 && nSlotGetI3DEngine()->MakeEntityRenderState(); CheckEntityLightSourcesInEntityObjects(); } } bool CEntity::IsEntityHasSomethingToRender() { bool bItHas = false; // test static component for (unsigned int k=0; kobject; if (!obj || !(cb->flags & ETY_OBJ_INFO_DRAW)) continue; bItHas = true; break; } // test animated component for (int i=0; iGetFlags() & CS_FLAG_DRAW_MODEL)) { bItHas = true; break; } } return bItHas; } ////////////////////////////////////////////////////////////////////////// void CEntity::PlayParticleSoundEffects( IParticleEffect *pEffect ) { } // vlad's todo: move ParticleEmitter into 3dengine int CEntity::CreateEntityParticleEmitter(int nSlotId, const ParticleParams & PartParams, float fSpawnPeriod,Vec3d vOffSet,Vec3d vDir, IParticleEffect *pEffect,float fScale) { if(nSlotId>16) { m_pISystem->GetILog()->Log("Error: CEntity::CreateEntityParticleEmitter: nSlotId=%d is out of range", nSlotId); return -1; } if (!m_pParticleEmitters) m_pParticleEmitters = new PatricleEmitters; m_bUpdateEmitters = true; if ((int)m_pParticleEmitters->size() <= nSlotId) { m_pParticleEmitters->resize(nSlotId+1); } EntPartEmitter &emitter = (*m_pParticleEmitters)[nSlotId]; emitter.pEmitter = m_pISystem->GetI3DEngine()->CreateParticleEmitter(); if (!pEffect) { emitter.pEmitter->SetParams( PartParams ); } else { emitter.pEmitter->SetEffect( pEffect ); } emitter.fScale = fScale; emitter.vOffset = vOffSet; emitter.vDir = vDir; emitter.fSpawnPeriod = fSpawnPeriod; emitter.pEffect = pEffect; // Override spawn period. emitter.pEmitter->SetSpawnPeriod( fSpawnPeriod ); emitter.pEmitter->SetUnlimitedLife(); // Unlimited lifetime. emitter.pEmitter->SetEntity( this ); // Assign material only if entity doesnt contain any geometry. if (GetNumObjects() == 0 && m_nMaxCharNum == 0) emitter.pEmitter->SetMaterial( m_pMaterial ); InitEntityRenderState(); return nSlotId; } ////////////////////////////////////////////////////////////////////////// void CEntity::DeleteParticleEmitter(int nId) { if(m_pParticleEmitters && nId >= 0 && nId < (int)m_pParticleEmitters->size()) { EntPartEmitter &emitter = (*m_pParticleEmitters)[nId]; if (emitter.pEmitter) m_pISystem->GetI3DEngine()->DeleteParticleEmitter( emitter.pEmitter ); emitter.pEmitter = 0; emitter.pEffect = 0; } } ////////////////////////////////////////////////////////////////////////// void CEntity::UpdateParticleEmitters( SEntityUpdateContext &ctx ) { if(!m_pParticleEmitters) return; FUNCTION_PROFILER( m_pISystem,PROFILE_ENTITY ); //[Timur] //@FIXME !!!! this must not be here. //@HACK m_pISystem->GetI3DEngine()->RegisterEntity(this); // if(GetEntityVisArea() && !IsEntityAreasVisible()) // return; //float fDist = GetDistance(m_pISystem->GetViewCamera().GetPos(), GetPos()); float fCurrTime = ctx.fCurrTime; // Keep emitters alive. for (int i=0; i < (int)m_pParticleEmitters->size(); i++) { EntPartEmitter &emitter = (*m_pParticleEmitters)[i]; if (emitter.pEmitter) { // calculate entity rotation matrix Matrix44 EntityMatrix; EntityMatrix.SetIdentity(); //EntityMatrix.RotateMatrix_fix(GetAngles()); EntityMatrix=Matrix44::CreateRotationZYX(-gf_DEGTORAD*GetAngles())*EntityMatrix; //NOTE: angles in radians and negated Vec3 pos = GetPos() + EntityMatrix.TransformVectorOLD( emitter.vOffset ); Vec3 dir = EntityMatrix.TransformVectorOLD( emitter.vDir ); // emitter.pEmitter->SetPos( pos,dir,emitter.fScale ); // Assign material only if entity doesnt contain any geometry. if (GetNumObjects() == 0 && m_nMaxCharNum == 0) { if (m_pMaterial) emitter.pEmitter->SetMaterial( m_pMaterial ); else emitter.pEmitter->SetMaterial( NULL ); } } } //[kirill] this vould stop some entities with eUT_Always from being updated - so commented it out // on first succesfull update - enable potential visibility check // SetUpdateVisLevel(eUT_PotVisible); } bool CEntity::IsEntityAreasVisible() { if(!GetEntityRS()) return false; IVisArea * pArea = (IVisArea *)m_pVisArea; // test area vis if(pArea && pArea->GetVisFrameId() == m_pISystem->GetIRenderer()->GetFrameID()) return true; // visible if(m_pDynLight && !(m_pDynLight->m_Flags & DLF_THIS_AREA_ONLY)) // tmp hack, should be set from the script { // test neighbours IVisArea * Areas[64]; int nCount = pArea->GetVisAreaConnections(Areas,64); for (int i=0; iGetVisFrameId() == m_pISystem->GetIRenderer()->GetFrameID()) return true; // visible } return false; // not visible } bool CEntity::CheckUpdateVisLevel( SEntityUpdateContext &ctx,EEntityUpdateVisLevel eUpdateVisLevel ) { switch(eUpdateVisLevel) { case eUT_Always: // always update return true; case eUT_InViewRange: // update if distance is less than camera view distance { bool bVisible = false; if (ctx.nFrameID - GetDrawFrame() < MAX_FRAME_ID_STEP_PER_FRAME) bVisible = true; // visible float fDistSquared = GetLengthSquared( ctx.vCameraPos - GetPos() ); if (m_fUpdateRadius > 0) { // Within update radius. if (fDistSquared < m_fUpdateRadius*m_fUpdateRadius) return bVisible; // Update, because it is visible and within update radius. else return false; // Never update entity outside update radius. } return bVisible || fDistSquared < ctx.fMaxViewDistSquared; } case eUT_PotVisible: // update if entity visarea or terrain sector is visible { if (m_fUpdateRadius > 0) { // Within update radius. float fDistSquared = GetLengthSquared( ctx.vCameraPos - GetPos() ); if (fDistSquared > m_fUpdateRadius*m_fUpdateRadius) return false; // Out of update radius. } if (ctx.nFrameID - GetDrawFrame() < MAX_FRAME_ID_STEP_PER_FRAME) return true; // visible float fDistSquared = GetLengthSquared( ctx.vCameraPos - GetPos() ); if (fDistSquared > ctx.fMaxViewDistSquared) return false; // not visible float fAddRadius = m_fUpdateRadius; // tmp hack for lights, will be removed when static light concept will be used if (m_pDynLight && !(m_pDynLight->m_Flags & DLF_THIS_AREA_ONLY) && m_pDynLight->m_Origin!=Vec3d(0,0,0)) fAddRadius += 8; return m_pISystem->GetI3DEngine()->IsPotentiallyVisible(this,fAddRadius); } case eUT_Visible: // update if visible if (ctx.nFrameID - GetDrawFrame() < MAX_FRAME_ID_STEP_PER_FRAME) { if (m_fUpdateRadius > 0) { float fDistSquared = GetLengthSquared( ctx.vCameraPos - GetPos() ); // Within update radius. if (fDistSquared < m_fUpdateRadius*m_fUpdateRadius) return true; // Update, because it is visible and within update radius. } else return true; // Update, because it is visible and no update radius specified. } break; case eUT_Never: // newer update return false; case eUT_Physics: // Only update due to physics. case eUT_PhysicsVisible: case eUT_Unconditional: return true; } // Not update. return false; } ////////////////////////////////////////////////////////////////////////// void CEntity::SetMaterial( IMatInfo *pMatInfo ) { m_pMaterial = pMatInfo; if(m_pMaterial) m_pMaterial->SetFlags(m_pMaterial->GetFlags()|MIF_WASUSED); } ////////////////////////////////////////////////////////////////////////// IMatInfo* CEntity::GetMaterial() const { return m_pMaterial; } ////////////////////////////////////////////////////////////////////////// void CEntity::PreloadInstanceResources(Vec3d vPrevPortalPos, float fPrevPortalDistance, float fTime) { if(!GetEntityStatObj(0) || !GetEntityStatObj(0)->GetLeafBuffer()) return; float fDistance = fPrevPortalDistance + GetPos().GetDistance(vPrevPortalPos); float fMaxViewDist = GetMaxViewDist(); if(fDistanceGetViewCamera().GetZMax()) { for (unsigned int k=0; kobject; if (obj && (cb->flags & ETY_OBJ_INFO_DRAW)) obj->PreloadResources(fDistance,fTime,0); } for (int i=0; iGetFlags() & CS_FLAG_DRAW_MODEL)) cmodel->PreloadResources ( fDistance, 1.f, 0 ); } } } float CEntity::GetMaxViewDist() { I3DEngine * p3DEngine = m_pISystem->GetI3DEngine(); return max(p3DEngine->GetObjectsMinViewDist(), GetRenderRadius()*p3DEngine->GetObjectsViewDistRatio()*GetViewDistRatioNormilized()); } void CEntity::SetShaderFloat(const char *Name, float Val) { char name[128]; int i; strcpy(name, Name); strlwr(name); for (i=0; i