Esempio n. 1
0
void Scene::Draw()
{
    TIME_PROFILE("Scene::Draw");

	//float timeElapsed = SystemTimer::Instance()->FrameDelta();

	shadowVolumes.clear();
    
    if(NULL != sceneGlobalMaterial)
    {
        NMaterialProperty* propShadowColor = sceneGlobalMaterial->GetMaterialProperty(NMaterial::PARAM_SHADOW_COLOR);
        if(NULL != propShadowColor)
        {
            DVASSERT(Shader::UT_FLOAT_VEC4 == propShadowColor->type);
            
            float32* propDataPtr = (float32*)propShadowColor->data;
            Color shadowColor(propDataPtr[0], propDataPtr[1], propDataPtr[2], propDataPtr[3]);
            renderSystem->SetShadowRectColor(shadowColor);
        }
    }
    
    uint64 time = SystemTimer::Instance()->AbsoluteMS();
    
    if(imposterManager)
	{
		//imposterManager->ProcessQueue();
	}
    
    renderSystem->Render(clearBuffers);
    
    //foliageSystem->DebugDrawVegetation();
    
	drawTime = SystemTimer::Instance()->AbsoluteMS() - time;
}
Esempio n. 2
0
void SwitchSystem::Process(float32 timeElapsed)
{
    TIME_PROFILE("SwitchSystem::Process");
    
	Set<Entity*>::iterator it;
	Set<Entity*>::const_iterator itEnd = updatableEntities.end();
	for(it = updatableEntities.begin(); it != itEnd; ++it)
	{
		Entity * entity = *it;
		SwitchComponent * sw = (SwitchComponent*)entity->GetComponent(Component::SWITCH_COMPONENT);

        if (sw->oldSwitchIndex != sw->newSwitchIndex)
		{
            SetSwitchHierarchy(entity, sw->newSwitchIndex);

			sw->oldSwitchIndex = sw->newSwitchIndex;
			
			ActionComponent* actionComponent = cast_if_equal<ActionComponent*>(entity->GetComponent(Component::ACTION_COMPONENT));
			if(NULL != actionComponent)
			{
				actionComponent->StartSwitch(sw->newSwitchIndex);
			}
		}
	}

	updatableEntities.clear();
}
void RenderUpdateSystem::Process(float32 timeElapsed)
{
    TIME_PROFILE("RenderUpdateSystem::Process");

    RenderSystem * renderSystem = GetScene()->GetRenderSystem();
    renderSystem->SetMainCamera(GetScene()->GetCurrentCamera());
    renderSystem->SetDrawCamera(GetScene()->GetDrawCamera());

    GetScene()->GetRenderSystem()->Update(timeElapsed);
}
Esempio n. 4
0
void Scene::Update(float timeElapsed)
{
    TIME_PROFILE("Scene::Update");
    
    uint64 time = SystemTimer::Instance()->AbsoluteMS();
    
    
	updatableSystem->UpdatePreTransform();

    transformSystem->Process();

	updatableSystem->UpdatePostTransform();

	if(RenderManager::Instance()->GetOptions()->IsOptionEnabled(RenderOptions::UPDATE_LODS))
	{
		lodSystem->SetCamera(currentCamera);
		lodSystem->Process();
	}
	

	switchSystem->Process();
    
//	entityManager->Flush();
	int32 size;
	
	size = (int32)animations.size();
	for (int32 animationIndex = 0; animationIndex < size; ++animationIndex)
	{
		SceneNodeAnimationList * anim = animations[animationIndex];
		anim->Update(timeElapsed);
	}
	

	referenceNodeSuffixChanged = false;

	if(RenderManager::Instance()->GetOptions()->IsOptionEnabled(RenderOptions::UPDATE_ANIMATED_MESHES))
	{
		size = (int32)animatedMeshes.size();
		for (int32 animatedMeshIndex = 0; animatedMeshIndex < size; ++animatedMeshIndex)
		{
			AnimatedMesh * mesh = animatedMeshes[animatedMeshIndex];
			mesh->Update(timeElapsed);
		}
	}

	//if(imposterManager)
	//{
	//	imposterManager->Update(timeElapsed);
	//}
    
    updateTime = SystemTimer::Instance()->AbsoluteMS() - time;
}
Esempio n. 5
0
void RenderLayer::Draw(const FastName & ownerRenderPass, Camera * camera, RenderLayerBatchArray * renderLayerBatchArray)
{
    TIME_PROFILE("RenderLayer::Draw");
    
    renderLayerBatchArray->Sort(camera);
    
#if CAN_INSTANCE_CHECK
    RenderBatch * prevBatch = 0;
    uint32 canBeInstanced = 0;
    
    Vector<int32> chain;
#endif
    uint32 size = (uint32)renderLayerBatchArray->GetRenderBatchCount();
    
    RenderOptions* options = RenderManager::Instance()->GetOptions();
    bool layerOcclustionStatsEnabled = options->IsOptionEnabled(RenderOptions::LAYER_OCCLUSION_STATS);
    
    if(layerOcclustionStatsEnabled)
    {
        if(NULL == occlusionQuery)
        {
            occlusionQuery = new OcclusionQuery();
            occlusionQuery->Init();
        }
    
        if(false == queryPending)
        {
            occlusionQuery->BeginQuery();
        }
    }
    
    for (uint32 k = 0; k < size; ++k)
    {
        RenderBatch * batch = renderLayerBatchArray->Get(k);

#if CAN_INSTANCE_CHECK
        if (prevBatch && batch->GetPolygonGroup() == prevBatch->GetPolygonGroup() && batch->GetMaterial()->GetParent() == prevBatch->GetMaterial()->GetParent())
        {
            canBeInstanced++;
        }else
        {
            if (canBeInstanced > 0)
                chain.push_back(canBeInstanced + 1);
            canBeInstanced = 0;
        }
#endif
        batch->Draw(ownerRenderPass, camera);
#if CAN_INSTANCE_CHECK
        prevBatch = batch;
#endif
    }
    
    if(layerOcclustionStatsEnabled)
    {
        if(false == queryPending)
        {
            occlusionQuery->EndQuery();
            queryPending = true;
        }
        
        if((true == queryPending) &&
           occlusionQuery->IsResultAvailable())
        {
            occlusionQuery->GetQuery(&lastFragmentsRenderedValue);
            queryPending = false;
        }
    }
    
#if CAN_INSTANCE_CHECK
    int32 realDrawEconomy = 0;
    for (uint32 k = 0; k < chain.size(); ++k)
    {
        realDrawEconomy += (chain[k] - 1);
    }
    
    Logger::Debug("Pass: %s Layer: %s Size: %d Can be instanced: %d Econ: %d", ownerRenderPass.c_str(), name.c_str(), size, chain.size(), realDrawEconomy);
    for (uint32 k = 0; k < chain.size(); ++k)
    {
        Logger::Debug("%d - %d", k, chain[k]);
    }
#endif
}
Esempio n. 6
0
void Scene::Update(float timeElapsed)
{
    TIME_PROFILE("Scene::Update");
    
    uint64 time = SystemTimer::Instance()->AbsoluteMS();

    bool needShowStaticOcclusion = RenderManager::Instance()->GetOptions()->IsOptionEnabled(RenderOptions::DEBUG_DRAW_STATIC_OCCLUSION);
    if (needShowStaticOcclusion&&!staticOcclusionDebugDrawSystem)
    {
        staticOcclusionDebugDrawSystem = new StaticOcclusionDebugDrawSystem(this);
        AddSystem(staticOcclusionDebugDrawSystem, (1 << Component::STATIC_OCCLUSION_COMPONENT), false, renderUpdateSystem);
    }else if (!needShowStaticOcclusion&&staticOcclusionDebugDrawSystem)
    {
        RemoveSystem(staticOcclusionDebugDrawSystem);
        SafeDelete(staticOcclusionDebugDrawSystem);
    }

    uint32 size = (uint32)systemsToProcess.size();
    for (uint32 k = 0; k < size; ++k)
    {
        SceneSystem * system = systemsToProcess[k];
        if((systemsMask & SCENE_SYSTEM_UPDATEBLE_FLAG) && system == transformSystem)
        {
            updatableSystem->UpdatePreTransform(timeElapsed);
            transformSystem->Process(timeElapsed);
            updatableSystem->UpdatePostTransform(timeElapsed);
        }
        else if(system == lodSystem)
        {
            if(RenderManager::Instance()->GetOptions()->IsOptionEnabled(RenderOptions::UPDATE_LODS))
            {
                lodSystem->Process(timeElapsed);
            }
        }
        else
        {
            system->Process(timeElapsed);
        }
    }

// 	int32 size;
// 	
// 	size = (int32)animations.size();
// 	for (int32 animationIndex = 0; animationIndex < size; ++animationIndex)
// 	{
// 		SceneNodeAnimationList * anim = animations[animationIndex];
// 		anim->Update(timeElapsed);
// 	}
// 
// 	if(RenderManager::Instance()->GetOptions()->IsOptionEnabled(RenderOptions::UPDATE_ANIMATED_MESHES))
// 	{
// 		size = (int32)animatedMeshes.size();
// 		for (int32 animatedMeshIndex = 0; animatedMeshIndex < size; ++animatedMeshIndex)
// 		{
// 			AnimatedMesh * mesh = animatedMeshes[animatedMeshIndex];
// 			mesh->Update(timeElapsed);
// 		}
// 	}

	//if(imposterManager)
	//{
	//	imposterManager->Update(timeElapsed);
	//}

    updateTime = SystemTimer::Instance()->AbsoluteMS() - time;
}
Esempio n. 7
0
void Scene::Draw()
{
    TIME_PROFILE("Scene::Draw");

    //Sprite * fboSprite = Sprite::CreateAsRenderTarget(512, 512, FORMAT_RGBA8888);
	//RenderManager::Instance()->SetRenderTarget(fboSprite);
	//RenderManager::Instance()->SetViewport(Rect(0, 0, 512, 512), false);
    nodeCounter = 0;
    uint64 time = SystemTimer::Instance()->AbsoluteMS();

	shadowVolumes.clear();
    
    //const GLenum discards[]  = {GL_DEPTH_ATTACHMENT, GL_COLOR_ATTACHMENT0};
    //RENDER_VERIFY(glDiscardFramebufferEXT(GL_FRAMEBUFFER,2,discards));
    //glDepthMask(GL_TRUE);
    //RENDER_VERIFY(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT));
    
    if(imposterManager)
	{
		//imposterManager->ProcessQueue();
	}
    
    RenderManager::Instance()->SetCullMode(FACE_BACK);
    RenderManager::Instance()->SetState(RenderState::DEFAULT_3D_STATE);
    RenderManager::Instance()->FlushState();
	RenderManager::Instance()->ClearDepthBuffer();
    //glDepthMask(GL_TRUE);
    //RENDER_VERIFY(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT));
    
	
    if (currentCamera)
    {
        currentCamera->Set();
    }
    
    Matrix4 prevMatrix = RenderManager::Instance()->GetMatrix(RenderManager::MATRIX_MODELVIEW);
    renderSystem->SetCamera(currentCamera);
    renderUpdateSystem->Process();
	actionSystem->Process(); //update action system before particles and render
	particleEffectSystem->Process();
    renderSystem->Render();
    debugRenderSystem->SetCamera(currentCamera);
    debugRenderSystem->Process();

    RenderManager::Instance()->SetMatrix(RenderManager::MATRIX_MODELVIEW, prevMatrix);
    
    if(imposterManager)
	{
		imposterManager->Draw();
	}
    


	RenderManager::Instance()->SetState(RenderState::DEFAULT_2D_STATE_BLEND);
	drawTime = SystemTimer::Instance()->AbsoluteMS() - time;

	//Image * image = Image::Create(512, 512, FORMAT_RGBA8888);
	//RENDER_VERIFY(glReadPixels(0, 0, 512, 512, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid *)image->data));
	//image->Save("img.png");
	//RenderManager::Instance()->RestoreRenderTarget();
}
Esempio n. 8
0
void Core::SystemProcessFrame()
{
    Stats::Instance()->BeginFrame();
    TIME_PROFILE("Core::SystemProcessFrame");
    
	if (!core) return;
	if (!isActive)return;
	
	SystemTimer::Instance()->Start();

	/**
		Check if device not in lost state first / after that be
	*/
	if (!RenderManager::Instance()->IsDeviceLost())
	{
// #ifdef __DAVAENGINE_DIRECTX9__
// 		if(firstRun)
// 		{
// 			core->BeginFrame();
// 			firstRun = false;
// 		}
// #else
        InputSystem::Instance()->OnBeforeUpdate();
		core->BeginFrame();
//#endif

#if !defined(__DAVAENGINE_ANDROID__)
		RenderResource::SaveAllResourcesToSystemMem();
#endif //#if !defined(__DAVAENGINE_ANDROID__)

		// recalc frame inside begin / end frame
		if (Core::Instance()->needTorecalculateMultipliers) 
		{
			Core::Instance()->CalculateScaleMultipliers();
			RenderManager::Instance()->SetRenderOrientation(screenOrientation);
            UIScreenManager::Instance()->ScreenSizeChanged();
            UIControlSystem::Instance()->ScreenSizeChanged();
		}

		float32 frameDelta = SystemTimer::Instance()->FrameDelta();
        SystemTimer::Instance()->UpdateGlobalTime(frameDelta);

		if(Replay::IsRecord())
		{
			Replay::Instance()->RecordFrame(frameDelta);
		}
		if(Replay::IsPlayback())
		{
			UIControlSystem::Instance()->ReplayEvents();
			frameDelta = Replay::Instance()->PlayFrameTime();
			if(Replay::IsPlayback()) //can be unset in previous string
			{
				SystemTimer::Instance()->SetFrameDelta(frameDelta);
			}
		}
		
		core->Update(frameDelta);
        InputSystem::Instance()->OnAfterUpdate();
		core->Draw();

		core->EndFrame();
// #ifdef __DAVAENGINE_DIRECTX9__
// 		core->BeginFrame();
// #endif
	}
    Stats::Instance()->EndFrame();
	globalFrameIndex++;
}
Esempio n. 9
0
bool MD_PZone::zoneAnimate(void)
{
	if (_fsmState == END)
		return(true);

	// work through things that stop us running this at all
	if (((_fsmState == PAUSE) && (millis() - _lastRunTime < _pauseTime)) ||
		(millis() - _lastRunTime < _tickTime) ||
		(_suspend))
			return(false);

	// save the time now, before we run the animation, so that the animation is part of the
	// delay between animations giving more accurate frame timing.
	_lastRunTime = millis();

	// any text to display?
	if (_pText != NULL)
	{
		switch (_fsmState)
		{
			case END:		// do nothing in this state
				PRINT_STATE("ANIMATE");
				break;

			case INITIALISE:
				PRINT_STATE("ANIMATE");

				setInitialConditions();
				zoneClear();
				_moveIn = true;
			// fall through to process the effect, first call will be with INITIALISE

			default: // All state except END are handled by the special effect functions
			switch (_moveIn ? _effectIn : _effectOut)
			{
				case PRINT:				effectPrint(_moveIn);			break;
				case SLICE:				effectSlice(_moveIn);			break;
				case WIPE:				effectWipe(false, _moveIn);		break;
				case WIPE_CURSOR:		effectWipe(true, _moveIn);		break;
				case OPENING:			effectOpen(false, _moveIn);		break;
				case OPENING_CURSOR:	effectOpen(true, _moveIn);		break;
				case CLOSING:			effectClose(false, _moveIn);	break;
				case CLOSING_CURSOR:	effectClose(true, _moveIn);		break;
				case BLINDS:			effectBlinds(_moveIn);			break;
				case DISSOLVE:			effectDissolve(_moveIn);		break;
				case SCAN_HORIZ:		effectHScan(_moveIn);			break;
				case SCAN_VERT:			effectVScan(_moveIn);			break;
				case GROW_UP:			effectGrow(true, _moveIn);		break;
				case GROW_DOWN:			effectGrow(false, _moveIn);		break;
				case SCROLL_UP:			effectVScroll(true, _moveIn);	break;
				case SCROLL_DOWN:		effectVScroll(false, _moveIn);	break;
				case SCROLL_LEFT:		effectHScroll(true, _moveIn);	break;
				case SCROLL_RIGHT:		effectHScroll(false, _moveIn);	break;
				case SCROLL_UP_LEFT:	effectDiag(true, true, _moveIn);	break;
				case SCROLL_UP_RIGHT:	effectDiag(true, false, _moveIn);	break;
				case SCROLL_DOWN_LEFT:	effectDiag(false, true, _moveIn);	break;
				case SCROLL_DOWN_RIGHT:	effectDiag(false, false, _moveIn);	break;
				default:
				_fsmState = END;
			}

			// one way toggle for input to output, reset on initialize
			_moveIn = _moveIn && !(_fsmState == PAUSE);
			break;
		}
	}

	TIME_PROFILE("\nAnimation time ");
	TIME_PROFILE(": Cycle time ");

	return(_fsmState == END);
}
Esempio n. 10
0
void FoliageSystem::Process(float32 timeElapsed)
{
    TIME_PROFILE("FoliageSystem::Process");
    
    VegetationRenderObject* vegetationRO = GetVegetation(foliageEntity);
    if(vegetationRO && vegetationRO->ReadyToRender())
    {
        WindSystem * windSystem = GetScene()->windSystem;
        
        Camera * camera = GetScene()->GetRenderSystem()->GetMainCamera();
        Vector<AbstractQuadTreeNode<VegetationSpatialData>*> & visibleCells = vegetationRO->BuildVisibleCellList(camera);
        uint32 cellsCount = visibleCells.size();
        
        Set<AbstractQuadTreeNode<VegetationSpatialData>* > updatableCells;
        for(uint32 i = 0; i < cellsCount; ++i)
        {
            AbstractQuadTreeNode<VegetationSpatialData>* cell = visibleCells[i];
            if(cell->data.width <= MAX_ANIMATED_CELL_WIDTH)
            {
                bool isMinAnimatedLod = (MIN_ANIMATED_CELL_WIDTH == cell->data.width);
                if(isMinAnimatedLod)
                {
                    updatableCells.insert(cell->parent);
                }
                else
                {
                    updatableCells.insert(cell);
                }
            }
        }
        
        Vector4 layersAnimationSpring = vegetationRO->GetLayersAnimationSpring();
        const Vector4& layerAnimationDrag = vegetationRO->GetLayerAnimationDragCoefficient();
        
        Set<AbstractQuadTreeNode<VegetationSpatialData>* >::iterator endIt = updatableCells.end();
        for(Set<AbstractQuadTreeNode<VegetationSpatialData>* >::iterator it = updatableCells.begin();
            it != endIt;
            ++it)
        {
            AbstractQuadTreeNode<VegetationSpatialData>* cell = *it;
            
            VegetationSpatialData& cellData = cell->data;
            
            const Vector3 & min = cellData.bbox.min;
            const Vector3 & max = cellData.bbox.max;
            
            Vector3 cellPos[4] = {
                Vector3(min.x, min.y, max.z),
                Vector3(min.x, max.y, max.z),
                Vector3(max.x, min.y, max.z),
                Vector3(max.x, max.y, max.z)
            };
            
            for(uint32 layerIndex = 0; layerIndex < 4; ++layerIndex)
            {
                Vector3 windVec = windSystem->GetWind(cellPos[layerIndex]);
                Vector2 windVec2D(windVec.x, windVec.y);
                
                Vector2 & offset = cellData.animationOffset[layerIndex];
                Vector2 & velocity = cellData.animationVelocity[layerIndex];
                
                velocity += (windVec2D - layersAnimationSpring.data[layerIndex] * offset - layerAnimationDrag.data[layerIndex] * velocity * velocity.Length()) * timeElapsed;
                offset += velocity * timeElapsed;
            }
            
            if(cell->children != NULL)
            {
                for(uint32 childIndex = 0; childIndex < 4; ++childIndex)
                {
                    cell->children[childIndex]->data.animationOffset[0] = cellData.animationOffset[0];
                    cell->children[childIndex]->data.animationOffset[1] = cellData.animationOffset[1];
                    cell->children[childIndex]->data.animationOffset[2] = cellData.animationOffset[2];
                    cell->children[childIndex]->data.animationOffset[3] = cellData.animationOffset[3];
                    
                    cell->children[childIndex]->data.animationVelocity[0] = cellData.animationVelocity[0];
                    cell->children[childIndex]->data.animationVelocity[1] = cellData.animationVelocity[1];
                    cell->children[childIndex]->data.animationVelocity[2] = cellData.animationVelocity[2];
                    cell->children[childIndex]->data.animationVelocity[3] = cellData.animationVelocity[3];
                }
            }
        }
    }
}
Esempio n. 11
0
void Core::SystemProcessFrame()
{
#ifdef __DAVAENGINE_NVIDIA_TEGRA_PROFILE__
	static bool isInit = false;
	static EGLuint64NV frequency;
	static PFNEGLGETSYSTEMTIMENVPROC eglGetSystemTimeNV;
	static PFNEGLGETSYSTEMTIMEFREQUENCYNVPROC eglGetSystemTimeFrequencyNV;
	if (!isInit)
	{
		eglGetSystemTimeNV = (PFNEGLGETSYSTEMTIMENVPROC) eglGetProcAddress("eglGetSystemTimeNV");
		eglGetSystemTimeFrequencyNV = (PFNEGLGETSYSTEMTIMEFREQUENCYNVPROC) eglGetProcAddress("eglGetSystemTimeFrequencyNV");
		if (!eglGetSystemTimeNV || !eglGetSystemTimeFrequencyNV)
		{
			DVASSERT(!"Error export eglGetSystemTimeNV, eglGetSystemTimeFrequencyNV");
			exit(0);
		}
		frequency = eglGetSystemTimeFrequencyNV();
	}
	EGLuint64NV start = eglGetSystemTimeNV() / frequency;
#endif //__DAVAENGINE_NVIDIA_TEGRA_PROFILE__
    Stats::Instance()->BeginFrame();
    TIME_PROFILE("Core::SystemProcessFrame");
    
	if (!core) return;
	if (!isActive)return;
	
	SystemTimer::Instance()->Start();

	/**
		Check if device not in lost state first / after that be
	*/
	if (!RenderManager::Instance()->IsDeviceLost())
	{
// #ifdef __DAVAENGINE_DIRECTX9__
// 		if(firstRun)
// 		{
// 			core->BeginFrame();
// 			firstRun = false;
// 		}
// #else
        InputSystem::Instance()->OnBeforeUpdate();
		core->BeginFrame();
//#endif

#if !defined(__DAVAENGINE_ANDROID__)
		RenderResource::SaveAllResourcesToSystemMem();
#endif //#if !defined(__DAVAENGINE_ANDROID__)

		// recalc frame inside begin / end frame
		if (Core::Instance()->needTorecalculateMultipliers) 
		{
			Core::Instance()->CalculateScaleMultipliers();
			RenderManager::Instance()->SetRenderOrientation(screenOrientation);
            UIScreenManager::Instance()->ScreenSizeChanged();
            UIControlSystem::Instance()->ScreenSizeChanged();
		}

		float32 frameDelta = SystemTimer::Instance()->FrameDelta();
        SystemTimer::Instance()->UpdateGlobalTime(frameDelta);

		if(Replay::IsRecord())
		{
			Replay::Instance()->RecordFrame(frameDelta);
		}
		if(Replay::IsPlayback())
		{
			UIControlSystem::Instance()->ReplayEvents();
			frameDelta = Replay::Instance()->PlayFrameTime();
			if(Replay::IsPlayback()) //can be unset in previous string
			{
				SystemTimer::Instance()->SetFrameDelta(frameDelta);
			}
		}
		
		LocalNotificationController::Instance()->Update();
        DownloadManager::Instance()->Update();
		JobManager::Instance()->Update();
		core->Update(frameDelta);
        InputSystem::Instance()->OnAfterUpdate();
		core->Draw();

		core->EndFrame();
// #ifdef __DAVAENGINE_DIRECTX9__
// 		core->BeginFrame();
// #endif
	}
    Stats::Instance()->EndFrame();
	globalFrameIndex++;
	
#ifdef __DAVAENGINE_NVIDIA_TEGRA_PROFILE__
	EGLuint64NV end = eglGetSystemTimeNV() / frequency;
	EGLuint64NV interval = end - start;
#endif //__DAVAENGINE_NVIDIA_TEGRA_PROFILE__
}
void RenderUpdateSystem::Process()
{
    TIME_PROFILE("RenderUpdateSystem::Process");
    float32 timeElapsed = SystemTimer::Instance()->FrameDelta();
    GetScene()->GetRenderSystem()->Update(timeElapsed);
}