1. Steve Streeting
  2. ogre

Commits

dark_sylinc  committed afe2d5e

Added per-instanced-entity custom parameters. Currently only HW Basic implements them.

  • Participants
  • Parent commits abe0b9b
  • Branches v1-9

Comments (0)

Files changed (7)

File OgreMain/include/OgreInstanceBatch.h

View file
     class _OgreExport InstanceBatch : public Renderable, public MovableObject
     {
     public:
-        typedef vector<InstancedEntity*>::type InstancedEntityVec;
+        typedef vector<InstancedEntity*>::type  InstancedEntityVec;
+        typedef vector<Vector4>::type           CustomParamsVec;
     protected:
         RenderOperation     mRenderOperation;
         size_t              mInstancesPerBatch;
         InstancedEntityVec  mInstancedEntities;
         InstancedEntityVec  mUnusedEntities;
 
+        ///@see InstanceManager::setNumCustomParams(). Because this may not even be used,
+        ///our implementations keep the params separate from the InstancedEntity to lower
+        ///the memory overhead. They default to Vector4::ZERO
+        CustomParamsVec		mCustomParams;
+
         /// This bbox contains all (visible) instanced entities
         AxisAlignedBox      mFullBoundingBox;
         Real                mBoundingRadius;
         void updateVisibility(void);
 
         /** @see _defragmentBatch */
-        void defragmentBatchNoCull( InstancedEntityVec &usedEntities );
+        void defragmentBatchNoCull( InstancedEntityVec &usedEntities, CustomParamsVec &usedParams );
 
         /** @see _defragmentBatch
             This one takes the entity closest to the minimum corner of the bbox, then starts
             gathering entities closest to this entity. There might be much better algorithms (i.e.
             involving space partition), but this one is simple and works well enough
         */
-        void defragmentBatchDoCull( InstancedEntityVec &usedEntities );
+        void defragmentBatchDoCull( InstancedEntityVec &usedEntities, CustomParamsVec &usedParams );
 
     public:
         InstanceBatch( InstanceManager *creator, MeshPtr &meshReference, const MaterialPtr &material,
         /** Fills the input vector with the instances that are currently being used or were requested.
             Used for defragmentation, @see InstanceManager::defragmentBatches
         */
-        void getInstancedEntitiesInUse( InstancedEntityVec &outEntities );
+		void getInstancedEntitiesInUse( InstancedEntityVec &outEntities, CustomParamsVec &outParams );
 
         /** @see InstanceManager::defragmentBatches
             This function takes InstancedEntities and pushes back all entities it can fit here
         @param optimizeCulling true will call the DoCull version, false the NoCull
         @param usedEntities Array of InstancedEntities to parent with this batch. Those reparented
             are removed from this input vector
-        @remarks
+			@param Array of Custom parameters correlated with the InstancedEntities in usedEntities.
+			They follow the fate of the entities in that vector.
+		@remarks:
 			This function assumes caller holds data to mInstancedEntities! Otherwise
 			you can get memory leaks. Don't call this directly if you don't know what you're doing!
 		*/
-		void _defragmentBatch( bool optimizeCulling, InstancedEntityVec &usedEntities );
+		void _defragmentBatch( bool optimizeCulling, InstancedEntityVec &usedEntities,
+								CustomParamsVec &usedParams );
 
 		/** @see InstanceManager::_defragmentBatchDiscard
 			Destroys unused entities and clears the mInstancedEntity container which avoids leaving
 		/** Tells that the list of entity instances with shared transforms has changed */
 		void _markTransformSharingDirty() { mTransformSharingDirty = true; }
 
+		/** @see InstancedEntity::setCustomParam */
+		void _setCustomParam( InstancedEntity *instancedEntity, unsigned char idx, const Vector4 &newParam );
+
+		/** @see InstancedEntity::getCustomParam */
+		const Vector4& _getCustomParam( InstancedEntity *instancedEntity, unsigned char idx );
+
 		//Renderable overloads
         /** @copydoc Renderable::getMaterial. */
 		const MaterialPtr& getMaterial(void) const		{ return mMaterial; }

File OgreMain/include/OgreInstanceManager.h

View file
         SceneManager*           mSceneManager;
 
         size_t                  mMaxLookupTableInstances;
+        unsigned char			mNumCustomParams;		//Number of custom params per instance.
+
         /** Finds a batch with at least one free instanced entity we can use.
             If none found, creates one.
         */
         /** @see defragmentBatches overload, this takes care of an array of batches
             for a specific material */
         void defragmentBatches( bool optimizeCull, vector<InstancedEntity*>::type &entities,
-                                InstanceBatchVec &fragmentedBatches );
+								vector<Ogre::Vector4>::type &usedParams,
+								InstanceBatchVec &fragmentedBatches );
 
         /** @see setSetting. This function helps it by setting the given parameter to all batches
             in container.
         */
         void setMaxLookupTableInstances( size_t maxLookupTableInstances );
 
+		/** Sets the number of custom parameters per instance. Some techniques (i.e. HWInstancingBasic)
+			support this, but not all of them. They also may have limitations to the max number. All
+			instancing implementations assume each instance param is a Vector4 (4 floats).
+		@remarks
+			This function cannot be called after the first batch has been created. Otherwise
+			it will raise an exception. If the technique doesn't support custom params, it will
+			raise an exception at the time of building the first InstanceBatch.
+
+			HWInstancingBasic:
+				* Each custom params adds an additional float4 TEXCOORD.
+			HWInstancingVTF:
+				* Not implemented. (Recommendation: Implement this as an additional float4 VTF fetch)
+			TextureVTF:
+				* Not implemented. (see HWInstancingVTF's recommendation)
+			ShaderBased:
+				* Not supported.
+		@param Number of custom parameters each instance will have. Default: 0
+		*/
+		void setNumCustomParams( unsigned char numCustomParams );
+
+		unsigned char getNumCustomParams() const
+		{ return mNumCustomParams; }
+
+        /** @return Instancing technique this manager was created for. Can't be changed after creation */
+        InstancingTechnique getInstancingTechnique() const
+        { return mInstancingTechnique; }
+
         /** Calculates the maximum (or the best amount, depending on flags) of instances
             per batch given the suggested size for the technique this manager was created for.
         @remarks

File OgreMain/include/OgreInstancedEntity.h

View file
 			return mInUse;
 		}
 
-
-
+		/** Sets the custom parameter for this instance @see InstanceManager::setNumCustomParams
+			Because not all techniques support custom params, and some users may not need it while
+			using millions of InstancedEntities, the params have been detached from InstancedEntity
+			and stored in it's InstanceBatch instead, to reduce memory overhead.
+			@param Index of the param. In the range [0; InstanceManager::getNumCustomParams())
+			@param New parameter
+		*/
+		void setCustomParam( unsigned char idx, const Vector4 &newParam );
+		const Vector4& getCustomParam( unsigned char idx );
 	};
 }
 

File OgreMain/src/OgreInstanceBatch.cpp

View file
 		mFullBoundingBox.setExtents( -Vector3::ZERO, Vector3::ZERO );
 
 		mName = batchName;
+
+		mCustomParams.resize( mCreator->getNumCustomParams() * mInstancesPerBatch, Ogre::Vector4::ZERO );
 	}
 
 	InstanceBatch::~InstanceBatch()
 						"InstanceBatch::checkSubMeshCompatibility");
 		}
 
+		if( !mCustomParams.empty() && mCreator->getInstancingTechnique() != InstanceManager::HWInstancingBasic )
+		{
+			//Implementing this for ShaderBased is impossible. All other variants can be.
+			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Custom parameters not supported for this "
+														"technique. Do you dare implementing it?"
+														"See InstanceManager::setNumCustomParams "
+														"documentation.",
+						"InstanceBatch::checkSubMeshCompatibility");
+		}
+
 		return true;
 	}
 	//-----------------------------------------------------------------------
 		mUnusedEntities.push_back( instancedEntity );
 	}
 	//-----------------------------------------------------------------------
-	void InstanceBatch::getInstancedEntitiesInUse( InstancedEntityVec &outEntities )
+	void InstanceBatch::getInstancedEntitiesInUse( InstancedEntityVec &outEntities,
+													CustomParamsVec &outParams )
 	{
 		InstancedEntityVec::const_iterator itor = mInstancedEntities.begin();
 		InstancedEntityVec::const_iterator end  = mInstancedEntities.end();
 		while( itor != end )
 		{
 			if( (*itor)->isInUse() )
+			{
 				outEntities.push_back( *itor );
+
+				for( unsigned char i=0; i<mCreator->getNumCustomParams(); ++i )
+					outParams.push_back( _getCustomParam( *itor, i ) );
+			}
+
 			++itor;
 		}
 	}
 	//-----------------------------------------------------------------------
-	void InstanceBatch::defragmentBatchNoCull( InstancedEntityVec &usedEntities )
+	void InstanceBatch::defragmentBatchNoCull( InstancedEntityVec &usedEntities,
+												CustomParamsVec &usedParams )
 	{
 		const size_t maxInstancesToCopy = std::min( mInstancesPerBatch, usedEntities.size() );
 		InstancedEntityVec::iterator first = usedEntities.end() - maxInstancesToCopy;
+		CustomParamsVec::iterator firstParams = usedParams.end() - maxInstancesToCopy *
+																	mCreator->getNumCustomParams();
 
 		//Copy from the back to front, into m_instancedEntities
 		mInstancedEntities.insert( mInstancedEntities.begin(), first, usedEntities.end() );
 		//Remove them from the array
 		usedEntities.resize( usedEntities.size() - maxInstancesToCopy );	
+
+		mCustomParams.insert( mCustomParams.begin(), firstParams, usedParams.end() );
 	}
 	//-----------------------------------------------------------------------
-	void InstanceBatch::defragmentBatchDoCull( InstancedEntityVec &usedEntities )
+	void InstanceBatch::defragmentBatchDoCull( InstancedEntityVec &usedEntities,
+												CustomParamsVec &usedParams )
 	{
 		//Get the the entity closest to the minimum bbox edge and put into "first"
 		InstancedEntityVec::const_iterator itor   = usedEntities.begin();
 			}
 
 			mInstancedEntities.push_back( *closest );
+			//Now the custom params
+			const size_t idx = closest - usedEntities.begin();	
+			for( unsigned char i=0; i<mCreator->getNumCustomParams(); ++i )
+			{
+				mCustomParams.push_back( usedParams[idx + i] );
+			}
 
-			//Remove 'closest' from usedEntities using swap and pop_back trick
+			//Remove 'closest' from usedEntities & usedParams using swap and pop_back trick
 			*closest = *(usedEntities.end() - 1);
 			usedEntities.pop_back();
+
+			for( unsigned char i=1; i<=mCreator->getNumCustomParams(); ++i )
+			{
+				usedParams[idx + mCreator->getNumCustomParams() - i] = *(usedParams.end() - 1);
+				usedParams.pop_back();
+			}
 		}
 	}
 	//-----------------------------------------------------------------------
-	void InstanceBatch::_defragmentBatch( bool optimizeCulling, InstancedEntityVec &usedEntities )
+	void InstanceBatch::_defragmentBatch( bool optimizeCulling, InstancedEntityVec &usedEntities,
+											CustomParamsVec &usedParams )
 	{
 		//Remove and clear what we don't need
 		mInstancedEntities.clear();
+		mCustomParams.clear();
 		deleteUnusedInstancedEntities();
 
 		if( !optimizeCulling )
-			defragmentBatchNoCull( usedEntities );
+			defragmentBatchNoCull( usedEntities, usedParams );
 		else
-			defragmentBatchDoCull( usedEntities );
+			defragmentBatchDoCull( usedEntities, usedParams );
 
 		//Reassign instance IDs and tell we're the new parent
 		uint32 instanceId = 0;
 		assert( (signed)(mInstancesPerBatch) - (signed)(mInstancedEntities.size()) >= 0 );
 		mInstancedEntities.reserve( mInstancesPerBatch );
 		mUnusedEntities.reserve( mInstancesPerBatch );
+		mCustomParams.reserve( mCreator->getNumCustomParams() * mInstancesPerBatch );
 		for( size_t i=mInstancedEntities.size(); i<mInstancesPerBatch; ++i )
 		{
 			InstancedEntity *instance = generateInstancedEntity(i);
 			mInstancedEntities.push_back( instance );
 			mUnusedEntities.push_back( instance );
+			mCustomParams.push_back( Ogre::Vector4::ZERO );
 		}
 
 		//We've potentially changed our bounds
 	{
 		visitor->visit( this, 0, false );
 	}
+	//-----------------------------------------------------------------------
+	void InstanceBatch::_setCustomParam( InstancedEntity *instancedEntity, unsigned char idx,
+										 const Vector4 &newParam )
+	{
+		mCustomParams[instancedEntity->mInstanceId * mCreator->getNumCustomParams() + idx] = newParam;
+	}
+	//-----------------------------------------------------------------------
+	const Vector4& InstanceBatch::_getCustomParam( InstancedEntity *instancedEntity, unsigned char idx )
+	{
+		return mCustomParams[instancedEntity->mInstanceId * mCreator->getNumCustomParams() + idx];
+	}
 }

File OgreMain/src/OgreInstanceBatchHW.cpp

View file
 		size_t offset				= 0;
 		unsigned short nextTexCoord	= thisVertexData->vertexDeclaration->getNextFreeTextureCoordinate();
 		const unsigned short newSource = thisVertexData->vertexDeclaration->getMaxSource() + 1;
-		for( int i=0; i<3; ++i )
+		for( unsigned char i=0; i<3 + mCreator->getNumCustomParams(); ++i )
 		{
 			thisVertexData->vertexDeclaration->addElement( newSource, offset, VET_FLOAT4,
 															VES_TEXTURE_COORDINATES, nextTexCoord++ );
 														"least 3 free TEXCOORDs",
 						"InstanceBatchHW::checkSubMeshCompatibility");
 		}
+		if( baseSubMesh->vertexData->vertexDeclaration->getNextFreeTextureCoordinate() >
+			8-2-mCreator->getNumCustomParams() ||
+			3 + mCreator->getNumCustomParams() >= 8 )
+		{
+			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "There are not enough free TEXCOORDs to hold the "
+														"custom parameters (required: " +
+														Ogre::StringConverter::toString( 3 + mCreator->
+														getNumCustomParams() ) + "). See InstanceManager"
+														"::setNumCustomParams documentation",
+						"InstanceBatchHW::checkSubMeshCompatibility");
+		}
 
 		return InstanceBatch::checkSubMeshCompatibility( baseSubMesh );
 	}
 		InstancedEntityVec::const_iterator itor = mInstancedEntities.begin();
 		InstancedEntityVec::const_iterator end  = mInstancedEntities.end();
 
+		unsigned char numCustomParams			= mCreator->getNumCustomParams();
+		size_t customParamIdx					= 0;
+
 		while( itor != end )
 		{
 			//Cull on an individual basis, the less entities are visible, the less instances we draw.
 
 				pDest += floatsWritten;
 
+				//Write custom parameters, if any
+				for( unsigned char i=0; i<numCustomParams; ++i )
+				{
+					*pDest++ = mCustomParams[customParamIdx+i].x;
+					*pDest++ = mCustomParams[customParamIdx+i].y;
+					*pDest++ = mCustomParams[customParamIdx+i].z;
+					*pDest++ = mCustomParams[customParamIdx+i].w;
+				}
+
 				++retVal;
 			}
 			++itor;
+
+			customParamIdx += numCustomParams;
 		}
 
 		mRenderOperation.vertexData->vertexBufferBinding->getBuffer(bufferIdx)->unlock();

File OgreMain/src/OgreInstanceManager.cpp

View file
 				mInstancingFlags( instancingFlags ),
 				mSubMeshIdx( subMeshIdx ),
                 mSceneManager( sceneManager ),
-				mMaxLookupTableInstances(16)
+				mMaxLookupTableInstances(16),
+				mNumCustomParams( 0 )
 	{
 		mMeshReference = MeshManager::getSingleton().load( meshName, groupName );
 
 		if( !mInstanceBatches.empty() )
 		{
 			OGRE_EXCEPT(Exception::ERR_INVALID_STATE, "Instances per batch can only be changed before"
-				" building the batch.", "InstanceManager::setInstancesPerBatch");
+				" building the batch.", "InstanceManager::setMaxLookupTableInstances");
 		}
 
 		mMaxLookupTableInstances = maxLookupTableInstances;
 	}
 	
 	//----------------------------------------------------------------------
+	void InstanceManager::setNumCustomParams( unsigned char numCustomParams )
+	{
+		if( !mInstanceBatches.empty() )
+		{
+			OGRE_EXCEPT(Exception::ERR_INVALID_STATE, "setNumCustomParams can only be changed before"
+				" building the batch.", "InstanceManager::setNumCustomParams");
+		}
+
+		mNumCustomParams = numCustomParams;
+	}
+	//----------------------------------------------------------------------
 	size_t InstanceManager::getMaxOrBestNumInstancesPerBatch( String materialName, size_t suggestedSize,
 																uint16 flags )
 	{
 	//-----------------------------------------------------------------------
 	void InstanceManager::defragmentBatches( bool optimizeCull,
 												InstanceBatch::InstancedEntityVec &usedEntities,
+												InstanceBatch::CustomParamsVec &usedParams,
 												InstanceBatchVec &fragmentedBatches )
 	{
 		InstanceBatchVec::iterator itor = fragmentedBatches.begin();
 		while( itor != end && !usedEntities.empty() )
 		{
 			if( !(*itor)->isStatic() )
-				(*itor)->_defragmentBatch( optimizeCull, usedEntities );
+				(*itor)->_defragmentBatch( optimizeCull, usedEntities, usedParams );
 			++itor;
 		}
 
 
 		while( itor != end )
 		{
-			InstanceBatch::InstancedEntityVec usedEntities;
+			InstanceBatch::InstancedEntityVec	usedEntities;
+			InstanceBatch::CustomParamsVec		usedParams;
 			usedEntities.reserve( itor->second.size() * mInstancesPerBatch );
 
 			//Collect all Instanced Entities being used by _all_ batches from this material
 				//Don't collect instances from static batches, we assume they're correctly set
 				//Plus, we don't want to put InstancedEntities from non-static into static batches
 				if( !(*it)->isStatic() )
-					(*it)->getInstancedEntitiesInUse( usedEntities );
+					(*it)->getInstancedEntitiesInUse( usedEntities, usedParams );
 				++it;
 			}
 
-			defragmentBatches( optimizeCulling, usedEntities, itor->second );
+			defragmentBatches( optimizeCulling, usedEntities, usedParams, itor->second );
 
 			++itor;
 		}

File OgreMain/src/OgreInstancedEntity.cpp

View file
 		//Remove the use of local transform if the object is deleted
 		mUseLocalTransform &= used;
 	}
+	//---------------------------------------------------------------------------
+	void InstancedEntity::setCustomParam( unsigned char idx, const Vector4 &newParam )
+	{
+		mBatchOwner->_setCustomParam( this, idx, newParam );
+	}
+	//---------------------------------------------------------------------------
+	const Vector4& InstancedEntity::getCustomParam( unsigned char idx )
+	{
+		return mBatchOwner->_getCustomParam( this, idx );
+	}
 }