1 /*
2 -----------------------------------------------------------------------------
3 This source file is part of OGRE
4 (Object-oriented Graphics Rendering Engine)
5 For the latest info, see http://www.ogre3d.org/
6 
7 Copyright (c) 2000-2013 Torus Knot Software Ltd
8 
9 Permission is hereby granted, free of charge, to any person obtaining a copy
10 of this software and associated documentation files (the "Software"), to deal
11 in the Software without restriction, including without limitation the rights
12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom the Software is
14 furnished to do so, subject to the following conditions:
15 
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 THE SOFTWARE.
26 -----------------------------------------------------------------------------
27 */
28 #include "OgreStableHeaders.h"
29 #include "OgreInstanceBatchHW.h"
30 #include "OgreSubMesh.h"
31 #include "OgreRenderOperation.h"
32 #include "OgreHardwareBufferManager.h"
33 #include "OgreInstancedEntity.h"
34 #include "OgreMaterial.h"
35 #include "OgreTechnique.h"
36 #include "OgreRoot.h"
37 
38 namespace Ogre
39 {
InstanceBatchHW(InstanceManager * creator,MeshPtr & meshReference,const MaterialPtr & material,size_t instancesPerBatch,const Mesh::IndexMap * indexToBoneMap,const String & batchName)40 	InstanceBatchHW::InstanceBatchHW( InstanceManager *creator, MeshPtr &meshReference,
41 										const MaterialPtr &material, size_t instancesPerBatch,
42 										const Mesh::IndexMap *indexToBoneMap, const String &batchName ) :
43 				InstanceBatch( creator, meshReference, material, instancesPerBatch,
44 								indexToBoneMap, batchName ),
45 				mKeepStatic( false )
46 	{
47 		//Override defaults, so that InstancedEntities don't create a skeleton instance
48 		mTechnSupportsSkeletal = false;
49 	}
50 
~InstanceBatchHW()51 	InstanceBatchHW::~InstanceBatchHW()
52 	{
53 	}
54 
55 	//-----------------------------------------------------------------------
calculateMaxNumInstances(const SubMesh * baseSubMesh,uint16 flags) const56 	size_t InstanceBatchHW::calculateMaxNumInstances( const SubMesh *baseSubMesh, uint16 flags ) const
57 	{
58 		size_t retVal = 0;
59 
60 		RenderSystem *renderSystem = Root::getSingleton().getRenderSystem();
61 		const RenderSystemCapabilities *capabilities = renderSystem->getCapabilities();
62 
63 		if( capabilities->hasCapability( RSC_VERTEX_BUFFER_INSTANCE_DATA ) )
64 		{
65 			//This value is arbitrary (theorical max is 2^30 for D3D9) but is big enough and safe
66 			retVal = 65535;
67 		}
68 
69 		return retVal;
70 	}
71 	//-----------------------------------------------------------------------
buildFrom(const SubMesh * baseSubMesh,const RenderOperation & renderOperation)72 	void InstanceBatchHW::buildFrom( const SubMesh *baseSubMesh, const RenderOperation &renderOperation )
73 	{
74 		InstanceBatch::buildFrom( baseSubMesh, renderOperation );
75 
76 		//We need to clone the VertexData (but just reference all buffers, except the last one)
77 		//because last buffer contains data specific to this batch, we need a different binding
78 		mRenderOperation.vertexData	= mRenderOperation.vertexData->clone( false );
79 		VertexData *thisVertexData		= mRenderOperation.vertexData;
80 		const unsigned short lastSource	= thisVertexData->vertexDeclaration->getMaxSource();
81 		HardwareVertexBufferSharedPtr vertexBuffer =
82 										HardwareBufferManager::getSingleton().createVertexBuffer(
83 										thisVertexData->vertexDeclaration->getVertexSize(lastSource),
84 										mInstancesPerBatch,
85 										HardwareBuffer::HBU_STATIC_WRITE_ONLY );
86 		thisVertexData->vertexBufferBinding->setBinding( lastSource, vertexBuffer );
87 		vertexBuffer->setIsInstanceData( true );
88 		vertexBuffer->setInstanceDataStepRate( 1 );
89 	}
90 	//-----------------------------------------------------------------------
setupVertices(const SubMesh * baseSubMesh)91 	void InstanceBatchHW::setupVertices( const SubMesh* baseSubMesh )
92 	{
93 		mRenderOperation.vertexData = baseSubMesh->vertexData->clone();
94 		mRemoveOwnVertexData = true; //Raise flag to remove our own vertex data in the end (not always needed)
95 
96 		VertexData *thisVertexData = mRenderOperation.vertexData;
97 
98 		//No skeletal animation support in this technique, sorry
99 		removeBlendData();
100 
101 		//Modify the declaration so it contains an extra source, where we can put the per instance data
102 		size_t offset				= 0;
103 		unsigned short nextTexCoord	= thisVertexData->vertexDeclaration->getNextFreeTextureCoordinate();
104 		const unsigned short newSource = thisVertexData->vertexDeclaration->getMaxSource() + 1;
105 		for( unsigned char i=0; i<3 + mCreator->getNumCustomParams(); ++i )
106 		{
107 			thisVertexData->vertexDeclaration->addElement( newSource, offset, VET_FLOAT4,
108 															VES_TEXTURE_COORDINATES, nextTexCoord++ );
109 			offset = thisVertexData->vertexDeclaration->getVertexSize( newSource );
110 		}
111 
112 		//Create the vertex buffer containing per instance data
113 		HardwareVertexBufferSharedPtr vertexBuffer =
114 										HardwareBufferManager::getSingleton().createVertexBuffer(
115 										thisVertexData->vertexDeclaration->getVertexSize(newSource),
116 										mInstancesPerBatch,
117 										HardwareBuffer::HBU_STATIC_WRITE_ONLY );
118 		thisVertexData->vertexBufferBinding->setBinding( newSource, vertexBuffer );
119 		vertexBuffer->setIsInstanceData( true );
120 		vertexBuffer->setInstanceDataStepRate( 1 );
121 	}
122 	//-----------------------------------------------------------------------
setupIndices(const SubMesh * baseSubMesh)123 	void InstanceBatchHW::setupIndices( const SubMesh* baseSubMesh )
124 	{
125 		//We could use just a reference, but the InstanceManager will in the end attampt to delete
126 		//the pointer, and we can't give it something that doesn't belong to us.
127 		mRenderOperation.indexData = baseSubMesh->indexData->clone( true );
128 		mRemoveOwnIndexData = true;	//Raise flag to remove our own index data in the end (not always needed)
129 	}
130 	//-----------------------------------------------------------------------
removeBlendData()131 	void InstanceBatchHW::removeBlendData()
132 	{
133 		VertexData *thisVertexData = mRenderOperation.vertexData;
134 
135 		unsigned short safeSource = 0xFFFF;
136 		const VertexElement* blendIndexElem = thisVertexData->vertexDeclaration->findElementBySemantic(
137 																				VES_BLEND_INDICES );
138 		if( blendIndexElem )
139 		{
140 			//save the source in order to prevent the next stage from unbinding it.
141 			safeSource = blendIndexElem->getSource();
142 			// Remove buffer reference
143 			thisVertexData->vertexBufferBinding->unsetBinding( blendIndexElem->getSource() );
144 		}
145 		// Remove blend weights
146 		const VertexElement* blendWeightElem = thisVertexData->vertexDeclaration->findElementBySemantic(
147 																				VES_BLEND_WEIGHTS );
148 		if( blendWeightElem && blendWeightElem->getSource() != safeSource )
149 		{
150 			// Remove buffer reference
151 			thisVertexData->vertexBufferBinding->unsetBinding( blendWeightElem->getSource() );
152 		}
153 
154 		thisVertexData->vertexDeclaration->removeElement(VES_BLEND_INDICES);
155 		thisVertexData->vertexDeclaration->removeElement(VES_BLEND_WEIGHTS);
156 		thisVertexData->closeGapsInBindings();
157 	}
158 	//-----------------------------------------------------------------------
checkSubMeshCompatibility(const SubMesh * baseSubMesh)159 	bool InstanceBatchHW::checkSubMeshCompatibility( const SubMesh* baseSubMesh )
160 	{
161 		//Max number of texture coordinates is _usually_ 8, we need at least 3 available
162 		if( baseSubMesh->vertexData->vertexDeclaration->getNextFreeTextureCoordinate() > 8-2 )
163 		{
164 			OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "Given mesh must have at "
165 														"least 3 free TEXCOORDs",
166 						"InstanceBatchHW::checkSubMeshCompatibility");
167 		}
168 		if( baseSubMesh->vertexData->vertexDeclaration->getNextFreeTextureCoordinate() >
169 			8-2-mCreator->getNumCustomParams() ||
170 			3 + mCreator->getNumCustomParams() >= 8 )
171 		{
172 			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "There are not enough free TEXCOORDs to hold the "
173 														"custom parameters (required: " +
174 														Ogre::StringConverter::toString( 3 + mCreator->
175 														getNumCustomParams() ) + "). See InstanceManager"
176 														"::setNumCustomParams documentation",
177 						"InstanceBatchHW::checkSubMeshCompatibility");
178 		}
179 
180 		return InstanceBatch::checkSubMeshCompatibility( baseSubMesh );
181 	}
182 	//-----------------------------------------------------------------------
updateVertexBuffer(Camera * currentCamera)183 	size_t InstanceBatchHW::updateVertexBuffer( Camera *currentCamera )
184 	{
185 		size_t retVal = 0;
186 
187 		//Now lock the vertex buffer and copy the 4x3 matrices, only those who need it!
188 		const size_t bufferIdx = mRenderOperation.vertexData->vertexBufferBinding->getBufferCount()-1;
189 		float *pDest = static_cast<float*>(mRenderOperation.vertexData->vertexBufferBinding->
190 											getBuffer(bufferIdx)->lock( HardwareBuffer::HBL_DISCARD ));
191 
192 		InstancedEntityVec::const_iterator itor = mInstancedEntities.begin();
193 		InstancedEntityVec::const_iterator end  = mInstancedEntities.end();
194 
195 		unsigned char numCustomParams			= mCreator->getNumCustomParams();
196 		size_t customParamIdx					= 0;
197 
198 		while( itor != end )
199 		{
200 			//Cull on an individual basis, the less entities are visible, the less instances we draw.
201 			//No need to use null matrices at all!
202 			if( (*itor)->findVisible( currentCamera ) )
203 			{
204 				const size_t floatsWritten = (*itor)->getTransforms3x4( pDest );
205 
206 				if( mManager->getCameraRelativeRendering() )
207 					makeMatrixCameraRelative3x4( pDest, floatsWritten );
208 
209 				pDest += floatsWritten;
210 
211 				//Write custom parameters, if any
212 				for( unsigned char i=0; i<numCustomParams; ++i )
213 				{
214 					*pDest++ = mCustomParams[customParamIdx+i].x;
215 					*pDest++ = mCustomParams[customParamIdx+i].y;
216 					*pDest++ = mCustomParams[customParamIdx+i].z;
217 					*pDest++ = mCustomParams[customParamIdx+i].w;
218 				}
219 
220 				++retVal;
221 			}
222 			++itor;
223 
224 			customParamIdx += numCustomParams;
225 		}
226 
227 		mRenderOperation.vertexData->vertexBufferBinding->getBuffer(bufferIdx)->unlock();
228 
229 		return retVal;
230 	}
231 	//-----------------------------------------------------------------------
_boundsDirty(void)232 	void InstanceBatchHW::_boundsDirty(void)
233 	{
234 		//Don't update if we're static, but still mark we're dirty
235 		if( !mBoundsDirty && !mKeepStatic )
236 			mCreator->_addDirtyBatch( this );
237 		mBoundsDirty = true;
238 	}
239 	//-----------------------------------------------------------------------
setStaticAndUpdate(bool bStatic)240 	void InstanceBatchHW::setStaticAndUpdate( bool bStatic )
241 	{
242 		//We were dirty but didn't update bounds. Do it now.
243 		if( mKeepStatic && mBoundsDirty )
244 			mCreator->_addDirtyBatch( this );
245 
246 		mKeepStatic = bStatic;
247 		if( mKeepStatic )
248 		{
249 			//One final update, since there will be none from now on
250 			//(except further calls to this function). Pass NULL because
251 			//we want to include only those who were added to the scene
252 			//but we don't want to perform culling
253 			mRenderOperation.numberOfInstances = updateVertexBuffer( 0 );
254 		}
255 	}
256 	//-----------------------------------------------------------------------
getWorldTransforms(Matrix4 * xform) const257 	void InstanceBatchHW::getWorldTransforms( Matrix4* xform ) const
258 	{
259 		*xform = Matrix4::IDENTITY;
260 	}
261 	//-----------------------------------------------------------------------
getNumWorldTransforms(void) const262 	unsigned short InstanceBatchHW::getNumWorldTransforms(void) const
263 	{
264 		return 1;
265 	}
266 	//-----------------------------------------------------------------------
_updateRenderQueue(RenderQueue * queue)267 	void InstanceBatchHW::_updateRenderQueue( RenderQueue* queue )
268 	{
269 		if( !mKeepStatic )
270 		{
271 			//Completely override base functionality, since we don't cull on an "all-or-nothing" basis
272 			//and we don't support skeletal animation
273 			if( (mRenderOperation.numberOfInstances = updateVertexBuffer( mCurrentCamera )) )
274 				queue->addRenderable( this, mRenderQueueID, mRenderQueuePriority );
275 		}
276 		else
277 		{
278 			if( mManager->getCameraRelativeRendering() )
279 			{
280 				OGRE_EXCEPT(Exception::ERR_INVALID_STATE, "Camera-relative rendering is incompatible"
281 					" with Instancing's static batches. Disable at least one of them",
282 					"InstanceBatch::_updateRenderQueue");
283 			}
284 
285 			//Don't update when we're static
286 			if( mRenderOperation.numberOfInstances )
287 				queue->addRenderable( this, mRenderQueueID, mRenderQueuePriority );
288 		}
289 	}
290 }
291