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