/src/ogre/OgreMain/src/OgreInstanceBatchHW.cpp
Line | Count | Source (jump to first uncovered line) |
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-2014 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 "OgreRenderOperation.h" |
31 | | #include "OgreInstancedEntity.h" |
32 | | |
33 | | namespace Ogre |
34 | | { |
35 | | InstanceBatchHW::InstanceBatchHW( InstanceManager *creator, MeshPtr &meshReference, |
36 | | const MaterialPtr &material, size_t instancesPerBatch, |
37 | | const Mesh::IndexMap *indexToBoneMap, const String &batchName ) : |
38 | 0 | InstanceBatch( creator, meshReference, material, instancesPerBatch, |
39 | 0 | indexToBoneMap, batchName ), |
40 | 0 | mKeepStatic( false ) |
41 | 0 | { |
42 | | //Override defaults, so that InstancedEntities don't create a skeleton instance |
43 | 0 | mTechnSupportsSkeletal = false; |
44 | 0 | } |
45 | | |
46 | | InstanceBatchHW::~InstanceBatchHW() |
47 | 0 | { |
48 | 0 | } |
49 | | |
50 | | //----------------------------------------------------------------------- |
51 | | size_t InstanceBatchHW::calculateMaxNumInstances( const SubMesh *baseSubMesh, uint16 flags ) const |
52 | 0 | { |
53 | 0 | size_t retVal = 0; |
54 | |
|
55 | 0 | RenderSystem *renderSystem = Root::getSingleton().getRenderSystem(); |
56 | 0 | const RenderSystemCapabilities *capabilities = renderSystem->getCapabilities(); |
57 | |
|
58 | 0 | if( capabilities->hasCapability( RSC_VERTEX_BUFFER_INSTANCE_DATA ) ) |
59 | 0 | { |
60 | | //This value is arbitrary (theorical max is 2^30 for D3D9) but is big enough and safe |
61 | 0 | retVal = 65535; |
62 | 0 | } |
63 | |
|
64 | 0 | return retVal; |
65 | 0 | } |
66 | | //----------------------------------------------------------------------- |
67 | | void InstanceBatchHW::buildFrom( const SubMesh *baseSubMesh, const RenderOperation &renderOperation ) |
68 | 0 | { |
69 | 0 | InstanceBatch::buildFrom( baseSubMesh, renderOperation ); |
70 | | |
71 | | //We need to clone the VertexData (but just reference all buffers, except the last one) |
72 | | //because last buffer contains data specific to this batch, we need a different binding |
73 | 0 | mRenderOperation.vertexData = mRenderOperation.vertexData->clone( false ); |
74 | 0 | mRemoveOwnVertexData = true; |
75 | 0 | VertexData *thisVertexData = mRenderOperation.vertexData; |
76 | 0 | const unsigned short lastSource = thisVertexData->vertexDeclaration->getMaxSource(); |
77 | 0 | auto vertexBuffer = HardwareBufferManager::getSingleton().createVertexBuffer( |
78 | 0 | thisVertexData->vertexDeclaration->getVertexSize(lastSource), mInstancesPerBatch, HBU_CPU_TO_GPU); |
79 | 0 | thisVertexData->vertexBufferBinding->setBinding( lastSource, vertexBuffer ); |
80 | 0 | vertexBuffer->setIsInstanceData( true ); |
81 | 0 | vertexBuffer->setInstanceDataStepRate( 1 ); |
82 | 0 | } |
83 | | //----------------------------------------------------------------------- |
84 | | void InstanceBatchHW::setupVertices( const SubMesh* baseSubMesh ) |
85 | 0 | { |
86 | | //No skeletal animation support in this technique, sorry |
87 | 0 | mRenderOperation.vertexData = baseSubMesh->vertexData->_cloneRemovingBlendData(); |
88 | 0 | mRemoveOwnVertexData = true; //Raise flag to remove our own vertex data in the end (not always needed) |
89 | | |
90 | 0 | VertexData *thisVertexData = mRenderOperation.vertexData; |
91 | | |
92 | | //Modify the declaration so it contains an extra source, where we can put the per instance data |
93 | 0 | size_t offset = 0; |
94 | 0 | unsigned short nextTexCoord = thisVertexData->vertexDeclaration->getNextFreeTextureCoordinate(); |
95 | 0 | const unsigned short newSource = thisVertexData->vertexDeclaration->getMaxSource() + 1; |
96 | 0 | for (unsigned char i = 0; i < 3 + mCreator->getNumCustomParams(); ++i) |
97 | 0 | { |
98 | 0 | thisVertexData->vertexDeclaration->addElement( newSource, offset, VET_FLOAT4, |
99 | 0 | VES_TEXTURE_COORDINATES, nextTexCoord++ ); |
100 | 0 | offset = thisVertexData->vertexDeclaration->getVertexSize( newSource ); |
101 | 0 | } |
102 | | |
103 | | //Create the vertex buffer containing per instance data |
104 | 0 | auto vertexBuffer = HardwareBufferManager::getSingleton().createVertexBuffer( |
105 | 0 | thisVertexData->vertexDeclaration->getVertexSize(newSource), mInstancesPerBatch, HBU_CPU_TO_GPU); |
106 | 0 | thisVertexData->vertexBufferBinding->setBinding( newSource, vertexBuffer ); |
107 | 0 | vertexBuffer->setIsInstanceData( true ); |
108 | 0 | vertexBuffer->setInstanceDataStepRate( 1 ); |
109 | 0 | } |
110 | | //----------------------------------------------------------------------- |
111 | | void InstanceBatchHW::setupIndices( const SubMesh* baseSubMesh ) |
112 | 0 | { |
113 | | //We could use just a reference, but the InstanceManager will in the end attampt to delete |
114 | | //the pointer, and we can't give it something that doesn't belong to us. |
115 | 0 | mRenderOperation.indexData = baseSubMesh->indexData->clone( true ); |
116 | 0 | mRemoveOwnIndexData = true; //Raise flag to remove our own index data in the end (not always needed) |
117 | 0 | } |
118 | | //----------------------------------------------------------------------- |
119 | | bool InstanceBatchHW::checkSubMeshCompatibility( const SubMesh* baseSubMesh ) |
120 | 0 | { |
121 | | //Max number of texture coordinates is _usually_ 8, we need at least 3 available |
122 | 0 | if( baseSubMesh->vertexData->vertexDeclaration->getNextFreeTextureCoordinate() > 8-2 ) |
123 | 0 | { |
124 | 0 | OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "Given mesh must have at " |
125 | 0 | "least 3 free TEXCOORDs", |
126 | 0 | "InstanceBatchHW::checkSubMeshCompatibility"); |
127 | 0 | } |
128 | 0 | if( baseSubMesh->vertexData->vertexDeclaration->getNextFreeTextureCoordinate() > |
129 | 0 | 8-2-mCreator->getNumCustomParams() || |
130 | 0 | 3 + mCreator->getNumCustomParams() >= 8 ) |
131 | 0 | { |
132 | 0 | OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "There are not enough free TEXCOORDs to hold the " |
133 | 0 | "custom parameters (required: " + |
134 | 0 | Ogre::StringConverter::toString( 3 + mCreator-> |
135 | 0 | getNumCustomParams() ) + "). See InstanceManager" |
136 | 0 | "::setNumCustomParams documentation", |
137 | 0 | "InstanceBatchHW::checkSubMeshCompatibility"); |
138 | 0 | } |
139 | | |
140 | 0 | return InstanceBatch::checkSubMeshCompatibility( baseSubMesh ); |
141 | 0 | } |
142 | | //----------------------------------------------------------------------- |
143 | | size_t InstanceBatchHW::updateVertexBuffer( Camera *currentCamera ) |
144 | 0 | { |
145 | 0 | size_t retVal = 0; |
146 | | |
147 | | //Now lock the vertex buffer and copy the 4x3 matrices, only those who need it! |
148 | 0 | VertexBufferBinding* binding = mRenderOperation.vertexData->vertexBufferBinding; |
149 | 0 | const ushort bufferIdx = ushort(binding->getBufferCount()-1); |
150 | 0 | HardwareBufferLockGuard vertexLock(binding->getBuffer(bufferIdx), HardwareBuffer::HBL_DISCARD); |
151 | 0 | float *pDest = static_cast<float*>(vertexLock.pData); |
152 | 0 | unsigned char numCustomParams = mCreator->getNumCustomParams(); |
153 | 0 | size_t customParamIdx = 0; |
154 | |
|
155 | 0 | for (auto *e : mInstancedEntities) |
156 | 0 | { |
157 | | //Cull on an individual basis, the less entities are visible, the less instances we draw. |
158 | | //No need to use null matrices at all! |
159 | 0 | if (e->findVisible(currentCamera )) |
160 | 0 | { |
161 | 0 | const size_t floatsWritten = e->getTransforms3x4( (Matrix3x4f*)pDest); |
162 | |
|
163 | 0 | if( mManager->getCameraRelativeRendering() ) |
164 | 0 | makeMatrixCameraRelative3x4((Matrix3x4f*)pDest, floatsWritten / 12); |
165 | |
|
166 | 0 | pDest += floatsWritten; |
167 | | |
168 | | //Write custom parameters, if any |
169 | 0 | for (unsigned char i = 0; i < numCustomParams; ++i) |
170 | 0 | { |
171 | 0 | memcpy(pDest, mCustomParams[customParamIdx+i].ptr(), sizeof(Vector4f)); |
172 | 0 | pDest += 4; |
173 | 0 | } |
174 | 0 | ++retVal; |
175 | 0 | } |
176 | 0 | customParamIdx += numCustomParams; |
177 | 0 | } |
178 | |
|
179 | 0 | return retVal; |
180 | 0 | } |
181 | | //----------------------------------------------------------------------- |
182 | | void InstanceBatchHW::_boundsDirty(void) |
183 | 0 | { |
184 | | //Don't update if we're static, but still mark we're dirty |
185 | 0 | if( !mBoundsDirty && !mKeepStatic ) |
186 | 0 | mCreator->_addDirtyBatch( this ); |
187 | 0 | mBoundsDirty = true; |
188 | 0 | } |
189 | | //----------------------------------------------------------------------- |
190 | | void InstanceBatchHW::setStaticAndUpdate( bool bStatic ) |
191 | 0 | { |
192 | | //We were dirty but didn't update bounds. Do it now. |
193 | 0 | if( mKeepStatic && mBoundsDirty ) |
194 | 0 | mCreator->_addDirtyBatch( this ); |
195 | |
|
196 | 0 | mKeepStatic = bStatic; |
197 | 0 | if( mKeepStatic ) |
198 | 0 | { |
199 | | //One final update, since there will be none from now on |
200 | | //(except further calls to this function). Pass NULL because |
201 | | //we want to include only those who were added to the scene |
202 | | //but we don't want to perform culling |
203 | 0 | mRenderOperation.numberOfInstances = updateVertexBuffer( 0 ); |
204 | 0 | } |
205 | 0 | } |
206 | | //----------------------------------------------------------------------- |
207 | | void InstanceBatchHW::getWorldTransforms( Matrix4* xform ) const |
208 | 0 | { |
209 | 0 | *xform = Matrix4::IDENTITY; |
210 | 0 | } |
211 | | //----------------------------------------------------------------------- |
212 | | void InstanceBatchHW::_updateRenderQueue( RenderQueue* queue ) |
213 | 0 | { |
214 | 0 | if( !mKeepStatic ) |
215 | 0 | { |
216 | | //Completely override base functionality, since we don't cull on an "all-or-nothing" basis |
217 | | //and we don't support skeletal animation |
218 | 0 | if( (mRenderOperation.numberOfInstances = updateVertexBuffer( mCurrentCamera )) ) |
219 | 0 | queue->addRenderable( this, mRenderQueueID, mRenderQueuePriority ); |
220 | 0 | } |
221 | 0 | else |
222 | 0 | { |
223 | 0 | OgreAssert(!mManager->getCameraRelativeRendering(), |
224 | 0 | "Camera-relative rendering is incompatible with Instancing's static batches. " |
225 | 0 | "Disable at least one of them"); |
226 | | |
227 | | //Don't update when we're static |
228 | 0 | if( mRenderOperation.numberOfInstances ) |
229 | 0 | queue->addRenderable( this, mRenderQueueID, mRenderQueuePriority ); |
230 | 0 | } |
231 | 0 | } |
232 | | } |