diff options
| author | Dave Parks <davep@lindenlab.com> | 2010-04-19 23:33:34 -0500 | 
|---|---|---|
| committer | Dave Parks <davep@lindenlab.com> | 2010-04-19 23:33:34 -0500 | 
| commit | daabccebf4a488d4704172013678e44718016a2f (patch) | |
| tree | d23cb838a0a61a094962cfe242092fd86fa35759 | |
| parent | 4520c266b3cf25f4c3ce46f7083cb12b1ce01b99 (diff) | |
Remove foot shadows from llvoavatar and add terse update to LLViewerJointMesh::updateFaceData.
(transplanted from 14545d24820e032279c81cb386dd043eeee625f7)
| -rw-r--r-- | indra/newview/llviewerjoint.cpp | 4 | ||||
| -rw-r--r-- | indra/newview/llviewerjoint.h | 2 | ||||
| -rw-r--r-- | indra/newview/llviewerjointmesh.cpp | 121 | ||||
| -rw-r--r-- | indra/newview/llviewerjointmesh.h | 2 | ||||
| -rw-r--r-- | indra/newview/llvoavatar.cpp | 231 | ||||
| -rw-r--r-- | indra/newview/llvoavatar.h | 5 | 
6 files changed, 110 insertions, 255 deletions
diff --git a/indra/newview/llviewerjoint.cpp b/indra/newview/llviewerjoint.cpp index 95f05b5f5f..8f2006b431 100644 --- a/indra/newview/llviewerjoint.cpp +++ b/indra/newview/llviewerjoint.cpp @@ -440,13 +440,13 @@ void LLViewerJoint::updateFaceSizes(U32 &num_vertices, U32& num_indices, F32 pix  	}  } -void LLViewerJoint::updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_wind) +void LLViewerJoint::updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_wind, bool terse_update)  {  	for (child_list_t::iterator iter = mChildren.begin();  		 iter != mChildren.end(); ++iter)  	{  		LLViewerJoint* joint = (LLViewerJoint*)(*iter); -		joint->updateFaceData(face, pixel_area, damp_wind); +		joint->updateFaceData(face, pixel_area, damp_wind, terse_update);  	}  } diff --git a/indra/newview/llviewerjoint.h b/indra/newview/llviewerjoint.h index 0d3092a044..67bd7786c3 100644 --- a/indra/newview/llviewerjoint.h +++ b/indra/newview/llviewerjoint.h @@ -126,7 +126,7 @@ public:  	PickName getPickName() { return mPickName; }  	virtual void updateFaceSizes(U32 &num_vertices, U32& num_indices, F32 pixel_area); -	virtual void updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_wind = FALSE); +	virtual void updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_wind = FALSE, bool terse_update = false);  	virtual BOOL updateLOD(F32 pixel_area, BOOL activate);  	virtual void updateJointGeometry();  	virtual void dump(); diff --git a/indra/newview/llviewerjointmesh.cpp b/indra/newview/llviewerjointmesh.cpp index c65946a574..6be7c442ef 100644 --- a/indra/newview/llviewerjointmesh.cpp +++ b/indra/newview/llviewerjointmesh.cpp @@ -669,7 +669,7 @@ void LLViewerJointMesh::updateFaceSizes(U32 &num_vertices, U32& num_indices, F32  //-----------------------------------------------------------------------------  static LLFastTimer::DeclareTimer FTM_AVATAR_FACE("Avatar Face"); -void LLViewerJointMesh::updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_wind) +void LLViewerJointMesh::updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_wind, bool terse_update)  {  	mFace = face; @@ -704,22 +704,6 @@ void LLViewerJointMesh::updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_w  			vertex_weightsp += mMesh->mFaceVertexOffset;  			clothing_weightsp += mMesh->mFaceVertexOffset; -			U32* __restrict v = (U32*) verticesp.get(); -			const U32 vert_skip = verticesp.getSkip()/sizeof(U32); - -			U32* __restrict tc = (U32*) tex_coordsp.get(); -			const U32 tc_skip = tex_coordsp.getSkip()/sizeof(U32); - -			U32* __restrict n = (U32*) normalsp.get(); -			const U32 n_skip = normalsp.getSkip()/sizeof(U32); -			 -			U32* __restrict vw = (U32*) vertex_weightsp.get(); -			const U32 vw_skip = vertex_weightsp.getSkip()/sizeof(U32); - - -			U32* __restrict cw = (U32*) clothing_weightsp.get(); -			const U32 cw_skip = vertex_weightsp.getSkip()/sizeof(U32); -  			const U32* __restrict coords = (U32*) mMesh->getCoords();  			const U32* __restrict tex_coords = (U32*) mMesh->getTexCoords();  			const U32* __restrict normals = (U32*) mMesh->getNormals(); @@ -729,49 +713,84 @@ void LLViewerJointMesh::updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_w  			const U32 num_verts = mMesh->getNumVertices();  			U32 i = 0; -			do + +			const U32 skip = verticesp.getSkip()/sizeof(U32); + +			U32* __restrict v = (U32*) verticesp.get(); +			U32* __restrict n = (U32*) normalsp.get(); +			 +			if (terse_update)  			{ -				v[0] = *(coords++);  -				v[1] = *(coords++);  -				v[2] = *(coords++); -				v += vert_skip; - -				tc[0] = *(tex_coords++);  -				tc[1] = *(tex_coords++); -				tc += tc_skip; - -				n[0] = *(normals++);  -				n[1] = *(normals++); -				n[2] = *(normals++); -				n += n_skip; - -				vw[0] = *(weights++); -				vw += vw_skip; - -				cw[0] = *(cloth_weights++); -				cw[1] = *(cloth_weights++); -				cw[2] = *(cloth_weights++); -				cw[3] = *(cloth_weights++); -				cw += cw_skip; +				for (S32 i = num_verts; i > 0; --i) +				{ +					//morph target application only, only update positions and normals +					v[0] = coords[0];  +					v[1] = coords[1];  +					v[2] = coords[2];		 +					coords += 3; +					v += skip; +				} + +				for (S32 i = num_verts; i > 0; --i) +				{ +					n[0] = normals[0];  +					n[1] = normals[1]; +					n[2] = normals[2]; +					normals += 3; +					n += skip; +				}  			} -			while (++i < num_verts); +			else +				{ -			const U32 idx_count = mMesh->getNumFaces()*3; +				U32* __restrict tc = (U32*) tex_coordsp.get(); +				U32* __restrict vw = (U32*) vertex_weightsp.get(); +				U32* __restrict cw = (U32*) clothing_weightsp.get(); +				 +				do +				{ +					v[0] = *(coords++);  +					v[1] = *(coords++);  +					v[2] = *(coords++); +					v += skip; + +					tc[0] = *(tex_coords++);  +					tc[1] = *(tex_coords++); +					tc += skip; + +					n[0] = *(normals++);  +					n[1] = *(normals++); +					n[2] = *(normals++); +					n += skip; + +					vw[0] = *(weights++); +					vw += skip; + +					cw[0] = *(cloth_weights++); +					cw[1] = *(cloth_weights++); +					cw[2] = *(cloth_weights++); +					cw[3] = *(cloth_weights++); +					cw += skip; +				} +				while (++i < num_verts); -			indicesp += mMesh->mFaceIndexOffset; +				const U32 idx_count = mMesh->getNumFaces()*3; -			U16* __restrict idx = indicesp.get(); -			S32* __restrict src_idx = (S32*) mMesh->getFaces(); +				indicesp += mMesh->mFaceIndexOffset; -			i = 0; +				U16* __restrict idx = indicesp.get(); +				S32* __restrict src_idx = (S32*) mMesh->getFaces(); -			const S32 offset = (S32) mMesh->mFaceVertexOffset; +				i = 0; -			do -			{ -				*(idx++) = *(src_idx++)+offset; +				const S32 offset = (S32) mMesh->mFaceVertexOffset; + +				do +				{ +					*(idx++) = *(src_idx++)+offset; +				} +				while (++i < idx_count);  			} -			while (++i < idx_count);  		}  	}  } diff --git a/indra/newview/llviewerjointmesh.h b/indra/newview/llviewerjointmesh.h index d62b0ada85..3b8d9c82b8 100644 --- a/indra/newview/llviewerjointmesh.h +++ b/indra/newview/llviewerjointmesh.h @@ -140,7 +140,7 @@ public:  	/*virtual*/ U32 drawShape( F32 pixelArea, BOOL first_pass, BOOL is_dummy );  	/*virtual*/ void updateFaceSizes(U32 &num_vertices, U32& num_indices, F32 pixel_area); -	/*virtual*/ void updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_wind = FALSE); +	/*virtual*/ void updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_wind = FALSE, bool terse_update = false);  	/*virtual*/ BOOL updateLOD(F32 pixel_area, BOOL activate);  	/*virtual*/ void updateJointGeometry();  	/*virtual*/ void dump(); diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 5100f4e59a..f89139dcd8 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -694,10 +694,8 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,  		mBakedTextureDatas[i].mTextureIndex = LLVOAvatarDictionary::bakedToLocalTextureIndex((EBakedTextureIndex)i);  	} -	mDirtyMesh = TRUE;	// Dirty geometry, need to regenerate. +	mDirtyMesh = 2;	// Dirty geometry, need to regenerate.  	mMeshTexturesDirty = FALSE; -	mShadow0Facep = NULL; -	mShadow1Facep = NULL;  	mHeadp = NULL;  	mIsBuilt = FALSE; @@ -733,12 +731,6 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,  	mRippleTimeLast = 0.f; -	mShadowImagep = LLViewerTextureManager::getFetchedTextureFromFile("foot_shadow.j2c"); -	 -	// GL NOT ACTIVE HERE -	//gGL.getTexUnit(0)->bind(mShadowImagep.get()); -	//mShadowImagep->setAddressMode(LLTexUnit::TAM_CLAMP); -	  	mInAir = FALSE;  	mStepOnLand = TRUE; @@ -1924,7 +1916,7 @@ void LLVOAvatar::updateMeshData()  			}  			if(num_vertices < 1)//skip empty meshes  			{ -				break ; +				continue ;  			}  			if(last_v_num > 0)//put the last inserted part into next vertex buffer.  			{ @@ -1946,6 +1938,8 @@ void LLVOAvatar::updateMeshData()  			// resize immediately  			facep->setSize(num_vertices, num_indices); +			bool terse_update = false; +  			if(facep->mVertexBuffer.isNull())  			{  				facep->mVertexBuffer = new LLVertexBufferAvatar(); @@ -1953,7 +1947,15 @@ void LLVOAvatar::updateMeshData()  			}  			else  			{ -				facep->mVertexBuffer->resizeBuffer(num_vertices, num_indices) ; +				if (facep->mVertexBuffer->getRequestedIndices() == num_indices && +					facep->mVertexBuffer->getRequestedVerts() == num_vertices) +				{ +					terse_update = true; +				} +				else +				{ +					facep->mVertexBuffer->resizeBuffer(num_vertices, num_indices) ; +				}  			}  			facep->setGeomIndex(0); @@ -1968,7 +1970,7 @@ void LLVOAvatar::updateMeshData()  			for(S32 k = j ; k < part_index ; k++)  			{ -				mMeshLOD[k]->updateFaceData(facep, mAdjustedPixelArea, k == MESH_ID_HAIR); +				mMeshLOD[k]->updateFaceData(facep, mAdjustedPixelArea, k == MESH_ID_HAIR, terse_update);  			}  			stop_glerror(); @@ -2321,12 +2323,6 @@ void LLVOAvatar::idleUpdateMisc(bool detailed_update)  	LLJoint::sNumUpdates = 0;  	LLJoint::sNumTouches = 0; -	// *NOTE: this is necessary for the floating name text above your head. -	if (mDrawable.notNull()) -	{ -		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_SHADOW, TRUE); -	} -  	BOOL visible = isVisible() || mNeedsAnimUpdate;  	// update attachments positions @@ -3654,12 +3650,19 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass)  		return num_indices;  	} -	if (mDirtyMesh || mDrawable->isState(LLDrawable::REBUILD_GEOMETRY)) +	LLFace* face = mDrawable->getFace(0); + +	bool needs_rebuild = !face || face->mVertexBuffer.isNull(); + +	if (needs_rebuild || mDirtyMesh || mDrawable->isState(LLDrawable::REBUILD_GEOMETRY))  	{	//LOD changed or new mesh created, allocate new vertex buffer if needed -		updateMeshData(); -		mDirtyMesh = FALSE; -		mNeedsSkin = TRUE; -		mDrawable->clearState(LLDrawable::REBUILD_GEOMETRY); +		if (needs_rebuild || mDirtyMesh >= 2 || mVisibilityRank <= 4) +		{ +			updateMeshData(); +			mDirtyMesh = 0; +			mNeedsSkin = TRUE; +			mDrawable->clearState(LLDrawable::REBUILD_GEOMETRY); +		}  	}  	if (LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_AVATAR) <= 0) @@ -3907,54 +3910,6 @@ U32 LLVOAvatar::renderRigid()  	return num_indices;  } -U32 LLVOAvatar::renderFootShadows() -{ -	U32 num_indices = 0; - -	if (!mIsBuilt) -	{ -		return 0; -	} - -	if (isSelf() && (!gAgent.needsRenderAvatar() || !gAgent.needsRenderHead())) -	{ -		return 0; -	} -	 -	if (!mIsBuilt) -	{ -		return 0; -	} -	 -	// Don't render foot shadows if your lower body is completely invisible. -	// (non-humanoid avatars rule!) -	if (!isTextureVisible(TEX_LOWER_BAKED)) -	{ -		return 0; -	} - -	// Update the shadow, tractor, and text label geometry. -	if (mDrawable->isState(LLDrawable::REBUILD_SHADOW) && !isImpostor()) -	{ -		updateShadowFaces(); -		mDrawable->clearState(LLDrawable::REBUILD_SHADOW); -	} - -	U32 foot_mask = LLVertexBuffer::MAP_VERTEX | -					LLVertexBuffer::MAP_TEXCOORD0; - -	LLGLDepthTest test(GL_TRUE, GL_FALSE); -	//render foot shadows -	LLGLEnable blend(GL_BLEND); -	gGL.getTexUnit(0)->bind(mShadowImagep, TRUE); -	glColor4fv(mShadow0Facep->getRenderColor().mV); -	mShadow0Facep->renderIndexed(foot_mask); -	glColor4fv(mShadow1Facep->getRenderColor().mV); -	mShadow1Facep->renderIndexed(foot_mask); -	 -	return num_indices; -} -  U32 LLVOAvatar::renderImpostor(LLColor4U color, S32 diffuse_channel)  {  	if (!mImpostor.isComplete()) @@ -4075,11 +4030,6 @@ void LLVOAvatar::updateTextures()  	{  		setDebugText(llformat("%4.0f:%4.0f", fsqrtf(mMinPixelArea),fsqrtf(mMaxPixelArea)));  	}	 -	 -	if( render_avatar ) -	{ -		mShadowImagep->addTextureStats(mPixelArea); -	}  } @@ -5223,7 +5173,7 @@ BOOL LLVOAvatar::updateJointLODs()   		if (res)  		{  			sNumLODChangesThisFrame++; -			dirtyMesh(); +			dirtyMesh(2);  			return TRUE;  		}  	} @@ -5247,18 +5197,9 @@ LLDrawable *LLVOAvatar::createDrawable(LLPipeline *pipeline)  	mDrawable->addFace(poolp, NULL);  	mDrawable->setRenderType(LLPipeline::RENDER_TYPE_AVATAR); -	LLFace *facep; - -	// Add faces for the foot shadows -	facep = mDrawable->addFace((LLFacePool*) NULL, mShadowImagep); -	mShadow0Facep = facep; - -	facep = mDrawable->addFace((LLFacePool*) NULL, mShadowImagep); -	mShadow1Facep = facep; -  	mNumInitFaces = mDrawable->getNumFaces() ; -	dirtyMesh(); +	dirtyMesh(2);  	return mDrawable;  } @@ -5298,107 +5239,6 @@ BOOL LLVOAvatar::updateGeometry(LLDrawable *drawable)  }  //----------------------------------------------------------------------------- -// updateShadowFaces() -//----------------------------------------------------------------------------- -void LLVOAvatar::updateShadowFaces() -{ -	LLFace *face0p = mShadow0Facep; -	LLFace *face1p = mShadow1Facep; - -	// -	// render avatar shadows -	// -	if (mInAir || mUpdatePeriod >= IMPOSTOR_PERIOD) -	{ -		face0p->setSize(0, 0); -		face1p->setSize(0, 0); -		return; -	} - -	LLSprite sprite(mShadowImagep.notNull() ? mShadowImagep->getID() : LLUUID::null); -	sprite.setFollow(FALSE); -	const F32 cos_angle = gSky.getSunDirection().mV[2]; -	F32 cos_elev = sqrt(1 - cos_angle * cos_angle); -	if (cos_angle < 0) cos_elev = -cos_elev; -	sprite.setSize(0.4f + cos_elev * 0.8f, 0.3f); -	LLVector3 sun_vec = gSky.mVOSkyp ? gSky.mVOSkyp->getToSun() : LLVector3(0.f, 0.f, 0.f); - -	if (mShadowImagep->hasGLTexture()) -	{ -		LLVector3 normal; -		LLVector3d shadow_pos; -		LLVector3 shadow_pos_agent; -		F32 foot_height; - -		if (mFootLeftp) -		{ -			LLVector3 joint_world_pos = mFootLeftp->getWorldPosition(); -			// this only does a ray straight down from the foot, as our client-side ray-tracing is very limited now -			// but we make an explicit ray trace call in expectation of future improvements -			resolveRayCollisionAgent(gAgent.getPosGlobalFromAgent(joint_world_pos),  -									 gAgent.getPosGlobalFromAgent(gSky.getSunDirection() + joint_world_pos), shadow_pos, normal); -			shadow_pos_agent = gAgent.getPosAgentFromGlobal(shadow_pos); -			foot_height = joint_world_pos.mV[VZ] - shadow_pos_agent.mV[VZ]; - -			// Pull sprite in direction of surface normal -			shadow_pos_agent += normal * SHADOW_OFFSET_AMT; - -			// Render sprite -			sprite.setNormal(normal); -			if (isSelf() && gAgentCamera.getCameraMode() == CAMERA_MODE_MOUSELOOK) -			{ -				sprite.setColor(0.f, 0.f, 0.f, 0.f); -			} -			else -			{ -				sprite.setColor(0.f, 0.f, 0.f, clamp_rescale(foot_height, MIN_SHADOW_HEIGHT, MAX_SHADOW_HEIGHT, 0.5f, 0.f)); -			} -			sprite.setPosition(shadow_pos_agent); - -			LLVector3 foot_to_knee = mKneeLeftp->getWorldPosition() - joint_world_pos; -			//foot_to_knee.normalize(); -			foot_to_knee -= projected_vec(foot_to_knee, sun_vec); -			sprite.setYaw(azimuth(sun_vec - foot_to_knee)); -		 -			sprite.updateFace(*face0p); -		} - -		if (mFootRightp) -		{ -			LLVector3 joint_world_pos = mFootRightp->getWorldPosition(); -			// this only does a ray straight down from the foot, as our client-side ray-tracing is very limited now -			// but we make an explicit ray trace call in expectation of future improvements -			resolveRayCollisionAgent(gAgent.getPosGlobalFromAgent(joint_world_pos),  -									 gAgent.getPosGlobalFromAgent(gSky.getSunDirection() + joint_world_pos), shadow_pos, normal); -			shadow_pos_agent = gAgent.getPosAgentFromGlobal(shadow_pos); -			foot_height = joint_world_pos.mV[VZ] - shadow_pos_agent.mV[VZ]; - -			// Pull sprite in direction of surface normal -			shadow_pos_agent += normal * SHADOW_OFFSET_AMT; - -			// Render sprite -			sprite.setNormal(normal); -			if (isSelf() && gAgentCamera.getCameraMode() == CAMERA_MODE_MOUSELOOK) -			{ -				sprite.setColor(0.f, 0.f, 0.f, 0.f); -			} -			else -			{ -				sprite.setColor(0.f, 0.f, 0.f, clamp_rescale(foot_height, MIN_SHADOW_HEIGHT, MAX_SHADOW_HEIGHT, 0.5f, 0.f)); -			} -			sprite.setPosition(shadow_pos_agent); - -			LLVector3 foot_to_knee = mKneeRightp->getWorldPosition() - joint_world_pos; -			//foot_to_knee.normalize(); -			foot_to_knee -= projected_vec(foot_to_knee, sun_vec); -			sprite.setYaw(azimuth(sun_vec - foot_to_knee)); -	 -			sprite.updateFace(*face1p); -		} -	} -} - -//-----------------------------------------------------------------------------  // updateSexDependentLayerSets()  //-----------------------------------------------------------------------------  void LLVOAvatar::updateSexDependentLayerSets( BOOL upload_bake ) @@ -5411,9 +5251,9 @@ void LLVOAvatar::updateSexDependentLayerSets( BOOL upload_bake )  //-----------------------------------------------------------------------------  // dirtyMesh()  //----------------------------------------------------------------------------- -void LLVOAvatar::dirtyMesh() +void LLVOAvatar::dirtyMesh(S32 priority)  { -	mDirtyMesh = TRUE; +	mDirtyMesh = llmax(mDirtyMesh, priority);  }  //----------------------------------------------------------------------------- @@ -7584,18 +7424,15 @@ BOOL LLVOAvatar::updateLOD()  	BOOL res = updateJointLODs();  	LLFace* facep = mDrawable->getFace(0); -	if (facep->mVertexBuffer.isNull() || -		(LLVertexBuffer::sEnableVBOs && -		((facep->mVertexBuffer->getUsage() == GL_STATIC_DRAW ? TRUE : FALSE) != -		 (facep->getPool()->getVertexShaderLevel() > 0 ? TRUE : FALSE)))) +	if (facep->mVertexBuffer.isNull())  	{ -		mDirtyMesh = TRUE; +		dirtyMesh(2);  	} -	if (mDirtyMesh || mDrawable->isState(LLDrawable::REBUILD_GEOMETRY)) +	if (mDirtyMesh >= 2 || mDrawable->isState(LLDrawable::REBUILD_GEOMETRY))  	{	//LOD changed or new mesh created, allocate new vertex buffer if needed  		updateMeshData(); -		mDirtyMesh = FALSE; +		mDirtyMesh = 0;  		mNeedsSkin = TRUE;  		mDrawable->clearState(LLDrawable::REBUILD_GEOMETRY);  	} diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 8da4c226ed..445bb03274 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -337,7 +337,6 @@ private:   **/  public: -	U32 		renderFootShadows();  	U32 		renderImpostor(LLColor4U color = LLColor4U(255,255,255,255), S32 diffuse_channel = 0);  	U32 		renderRigid();  	U32 		renderSkinned(EAvatarRenderPass pass); @@ -565,13 +564,13 @@ private:  public:  	void 			updateMeshTextures();  	void 			updateSexDependentLayerSets(BOOL upload_bake); -	void 			dirtyMesh(); // Dirty the avatar mesh +	void 			dirtyMesh(S32 priority = 1); // Dirty the avatar mesh  	void 			updateMeshData();  protected:  	void 			releaseMeshData();  	virtual void restoreMeshData();  private: -	BOOL 			mDirtyMesh; +	S32 			mDirtyMesh; // 0 -- not dirty, 1 -- morphed, 2 -- LOD  	BOOL			mMeshTexturesDirty;  	typedef std::multimap<std::string, LLPolyMesh*> polymesh_map_t;  | 
