2008-02-25 23:19:29 +00:00
/*-------------------------------------------------------------------------------------
Copyright ( c ) 2006 John Judnich
This software is provided ' as - is ' , without any express or implied warranty . In no event will the authors be held liable for any damages arising from the use of this software .
Permission is granted to anyone to use this software for any purpose , including commercial applications , and to alter it and redistribute it freely , subject to the following restrictions :
1. The origin of this software must not be misrepresented ; you must not claim that you wrote the original software . If you use this software in a product , an acknowledgment in the product documentation would be appreciated but is not required .
2. Altered source versions must be plainly marked as such , and must not be misrepresented as being the original software .
3. This notice may not be removed or altered from any source distribution .
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//BatchedGeometry.h
//A "lightweight" version of Ogre::StaticGeometry, which gives you a little more control
//over the batch materials, etc.
//-------------------------------------------------------------------------------------
# include "BatchedGeometry.h"
# include <OgreRoot.h>
# include <OgreRenderSystem.h>
# include <OgreCamera.h>
2019-12-26 21:32:58 +01:00
# include <OgreVector.h>
2008-02-25 23:19:29 +00:00
# include <OgreQuaternion.h>
# include <OgreSceneNode.h>
# include <OgreString.h>
# include <OgreStringConverter.h>
# include <OgreEntity.h>
# include <OgreSubMesh.h>
# include <OgreSubEntity.h>
# include <OgreMesh.h>
# include <OgreMeshManager.h>
# include <OgreHardwareBufferManager.h>
2009-07-06 22:12:17 +02:00
# include <OgreHardwareBuffer.h>
# include <OgreMaterialManager.h>
# include <OgreMaterial.h>
2017-08-16 23:16:31 +02:00
# include <OgreTechnique.h>
2009-07-06 22:12:17 +02:00
# include <string>
using namespace Ogre ;
# ifndef max
# define max(a,b) (((a) > (b)) ? (a) : (b))
2017-08-16 23:16:31 +02:00
# endif
namespace Forests {
//-------------------------------------------------------------------------------------
BatchedGeometry : : BatchedGeometry ( SceneManager * mgr , SceneNode * rootSceneNode )
2022-01-23 20:35:27 +01:00
:
2017-08-16 23:16:31 +02:00
sceneMgr ( mgr ) ,
2022-01-23 20:35:27 +01:00
sceneNode ( nullptr ) ,
2008-04-25 00:59:58 +02:00
parentSceneNode ( rootSceneNode ) ,
minDistanceSquared ( 0 ) ,
2022-01-23 20:35:27 +01:00
withinFarDistance ( false ) ,
boundsUndefined ( true ) ,
2008-04-25 00:59:58 +02:00
built ( false )
2017-08-16 23:16:31 +02:00
{
clear ( ) ;
}
2008-02-25 23:19:29 +00:00
BatchedGeometry : : ~ BatchedGeometry ( )
{
clear ( ) ;
}
2009-07-06 22:12:17 +02:00
void BatchedGeometry : : addEntity ( Entity * ent , const Vector3 & position , const Quaternion & orientation , const Vector3 & scale , const Ogre : : ColourValue & color )
{
MeshPtr mesh = ent - > getMesh ( ) ;
//If shared vertex data is used, extract into non-shared data
extractVertexDataFromShared ( mesh ) ;
//For each subentity
for ( uint32 i = 0 ; i < ent - > getNumSubEntities ( ) ; + + i ) {
//Get the subentity
2008-02-25 23:19:29 +00:00
SubEntity * subEntity = ent - > getSubEntity ( i ) ;
SubMesh * subMesh = subEntity - > getSubMesh ( ) ;
2009-07-06 22:12:17 +02:00
//Generate a format string that uniquely identifies this material & vertex/index format
if ( subMesh - > vertexData = = NULL )
OGRE_EXCEPT ( Exception : : ERR_INTERNAL_ERROR , " SubMesh vertex data not found! " , " BatchedGeometry::addEntity() " ) ;
String formatStr = getFormatString ( subEntity ) ;
//If a batch using an identical format exists...
2008-02-25 23:19:29 +00:00
SubBatch * batch ;
SubBatchMap : : iterator batchIter = subBatchMap . find ( formatStr ) ;
if ( batchIter ! = subBatchMap . end ( ) ) {
//Use the batch
batch = batchIter - > second ;
} else {
//Otherwise create a new batch
batch = new SubBatch ( this , subEntity ) ;
subBatchMap . insert ( std : : pair < String , SubBatch * > ( formatStr , batch ) ) ;
}
//Now add the submesh to the compatible batch
batch - > addSubEntity ( subEntity , position , orientation , scale , color ) ;
}
//Update bounding box
Matrix4 mat ( orientation ) ;
mat . setScale ( scale ) ;
AxisAlignedBox entBounds = ent - > getBoundingBox ( ) ;
entBounds . transform ( mat ) ;
2009-07-06 22:12:17 +02:00
2008-02-25 23:19:29 +00:00
if ( boundsUndefined ) {
bounds . setMinimum ( entBounds . getMinimum ( ) + position ) ;
bounds . setMaximum ( entBounds . getMaximum ( ) + position ) ;
boundsUndefined = false ;
} else {
Vector3 min = bounds . getMinimum ( ) ;
Vector3 max = bounds . getMaximum ( ) ;
min . makeFloor ( entBounds . getMinimum ( ) + position ) ;
max . makeCeil ( entBounds . getMaximum ( ) + position ) ;
bounds . setMinimum ( min ) ;
bounds . setMaximum ( max ) ;
2009-07-06 22:12:17 +02:00
}
}
uint32 CountUsedVertices ( IndexData * id , std : : map < uint32 , uint32 > & ibmap )
{
uint32 i , count ;
switch ( id - > indexBuffer - > getType ( ) ) {
case HardwareIndexBuffer : : IT_16BIT :
{
uint16 * data = ( uint16 * ) id - > indexBuffer - > lock ( id - > indexStart * sizeof ( uint16 ) ,
id - > indexCount * sizeof ( uint16 ) , HardwareBuffer : : HBL_READ_ONLY ) ;
for ( i = 0 ; i < id - > indexCount ; i + + ) {
uint16 index = data [ i ] ;
Fixed issue with incorrect vertex data.
This bug took quite some time to track down.
It appears that on at least gcc 4.8.3 there's some kind of bug which
makes the value assigned be off by one. I.e. if the map is empty, and
we're inserting the first instance, the "size()" method would return
"0", and that's the value we would be inserting. However, for some
reason which might have to do with caching, optimization or out-of-order
execution, the value written is actually the size _after_ the insertion.
So if the map was empty, the first entry would get the value of "1",
which would be the size _after_ the insertion.
And this would of course cause issues later on, with the memcpy writing
beyond the data, thus corrupting the memory, leading to segfaults.
2014-11-15 15:49:06 +01:00
if ( ibmap . find ( index ) = = ibmap . end ( ) ) {
2014-11-23 14:11:36 +01:00
//We'd like to use std::map::emplace(), but this isn't supported on gcc 4.7 which we still support
ibmap . insert ( std : : make_pair ( index , ( uint32 ) ( ibmap . size ( ) ) ) ) ;
Fixed issue with incorrect vertex data.
This bug took quite some time to track down.
It appears that on at least gcc 4.8.3 there's some kind of bug which
makes the value assigned be off by one. I.e. if the map is empty, and
we're inserting the first instance, the "size()" method would return
"0", and that's the value we would be inserting. However, for some
reason which might have to do with caching, optimization or out-of-order
execution, the value written is actually the size _after_ the insertion.
So if the map was empty, the first entry would get the value of "1",
which would be the size _after_ the insertion.
And this would of course cause issues later on, with the memcpy writing
beyond the data, thus corrupting the memory, leading to segfaults.
2014-11-15 15:49:06 +01:00
}
2009-07-06 22:12:17 +02:00
}
count = ( uint32 ) ibmap . size ( ) ;
id - > indexBuffer - > unlock ( ) ;
}
break ;
case HardwareIndexBuffer : : IT_32BIT :
{
uint32 * data = ( uint32 * ) id - > indexBuffer - > lock ( id - > indexStart * sizeof ( uint32 ) ,
id - > indexCount * sizeof ( uint32 ) , HardwareBuffer : : HBL_READ_ONLY ) ;
for ( i = 0 ; i < id - > indexCount ; i + + ) {
uint32 index = data [ i ] ;
Fixed issue with incorrect vertex data.
This bug took quite some time to track down.
It appears that on at least gcc 4.8.3 there's some kind of bug which
makes the value assigned be off by one. I.e. if the map is empty, and
we're inserting the first instance, the "size()" method would return
"0", and that's the value we would be inserting. However, for some
reason which might have to do with caching, optimization or out-of-order
execution, the value written is actually the size _after_ the insertion.
So if the map was empty, the first entry would get the value of "1",
which would be the size _after_ the insertion.
And this would of course cause issues later on, with the memcpy writing
beyond the data, thus corrupting the memory, leading to segfaults.
2014-11-15 15:49:06 +01:00
if ( ibmap . find ( index ) = = ibmap . end ( ) ) {
2014-11-23 15:48:30 +01:00
//We'd like to use std::map::emplace(), but this isn't supported on gcc 4.7 which we still support
ibmap . insert ( std : : make_pair ( index , ( uint32 ) ( ibmap . size ( ) ) ) ) ;
Fixed issue with incorrect vertex data.
This bug took quite some time to track down.
It appears that on at least gcc 4.8.3 there's some kind of bug which
makes the value assigned be off by one. I.e. if the map is empty, and
we're inserting the first instance, the "size()" method would return
"0", and that's the value we would be inserting. However, for some
reason which might have to do with caching, optimization or out-of-order
execution, the value written is actually the size _after_ the insertion.
So if the map was empty, the first entry would get the value of "1",
which would be the size _after_ the insertion.
And this would of course cause issues later on, with the memcpy writing
beyond the data, thus corrupting the memory, leading to segfaults.
2014-11-15 15:49:06 +01:00
}
2009-07-06 22:12:17 +02:00
}
count = ( uint32 ) ibmap . size ( ) ;
id - > indexBuffer - > unlock ( ) ;
}
break ;
default :
throw new Ogre : : Exception ( 0 , " Unknown index buffer type " , " Converter.cpp::CountVertices " ) ;
break ;
}
return count ;
}
void BatchedGeometry : : extractVertexDataFromShared ( MeshPtr mesh )
{
if ( mesh - > sharedVertexData = = NULL )
return ;
// Get shared vertex data
VertexData * oldVertexData = mesh - > sharedVertexData ;
2017-08-16 23:16:31 +02:00
for ( SubMesh * subMesh : mesh - > getSubMeshes ( ) ) {
2009-07-06 22:12:17 +02:00
// Get index data
IndexData * indexData = subMesh - > indexData ;
HardwareIndexBufferSharedPtr ib = indexData - > indexBuffer ;
// Create new nonshared vertex data
std : : map < uint32 , uint32 > indicesMap ;
Fixed issue with incorrect vertex data.
This bug took quite some time to track down.
It appears that on at least gcc 4.8.3 there's some kind of bug which
makes the value assigned be off by one. I.e. if the map is empty, and
we're inserting the first instance, the "size()" method would return
"0", and that's the value we would be inserting. However, for some
reason which might have to do with caching, optimization or out-of-order
execution, the value written is actually the size _after_ the insertion.
So if the map was empty, the first entry would get the value of "1",
which would be the size _after_ the insertion.
And this would of course cause issues later on, with the memcpy writing
beyond the data, thus corrupting the memory, leading to segfaults.
2014-11-15 15:49:06 +01:00
VertexData * newVertexData = OGRE_NEW VertexData ( ) ;
2009-07-06 22:12:17 +02:00
newVertexData - > vertexCount = CountUsedVertices ( indexData , indicesMap ) ;
//delete newVertexData->vertexDeclaration;
newVertexData - > vertexDeclaration = oldVertexData - > vertexDeclaration - > clone ( ) ;
// Create new vertex buffers
uint32 buffersCount = ( uint32 ) oldVertexData - > vertexBufferBinding - > getBufferCount ( ) ;
for ( uint32 bufferIndex = 0 ; bufferIndex < buffersCount ; bufferIndex + + ) {
// Lock shared vertex buffer
HardwareVertexBufferSharedPtr oldVertexBuffer = oldVertexData - > vertexBufferBinding - > getBuffer ( bufferIndex ) ;
size_t vertexSize = oldVertexBuffer - > getVertexSize ( ) ;
uint8 * oldLock = ( uint8 * ) oldVertexBuffer - > lock ( 0 , oldVertexData - > vertexCount * vertexSize , HardwareBuffer : : HBL_READ_ONLY ) ;
// Create and lock nonshared vertex buffer
HardwareVertexBufferSharedPtr newVertexBuffer = HardwareBufferManager : : getSingleton ( ) . createVertexBuffer (
vertexSize , newVertexData - > vertexCount , oldVertexBuffer - > getUsage ( ) , oldVertexBuffer - > hasShadowBuffer ( ) ) ;
uint8 * newLock = ( uint8 * ) newVertexBuffer - > lock ( 0 , newVertexData - > vertexCount * vertexSize , HardwareBuffer : : HBL_NORMAL ) ;
// Copy vertices from shared vertex buffer into nonshared vertex buffer
std : : map < uint32 , uint32 > : : iterator i , iend = indicesMap . end ( ) ;
for ( i = indicesMap . begin ( ) ; i ! = iend ; i + + ) {
memcpy ( newLock + vertexSize * i - > second , oldLock + vertexSize * i - > first , vertexSize ) ;
}
// Unlock vertex buffers
oldVertexBuffer - > unlock ( ) ;
newVertexBuffer - > unlock ( ) ;
// Bind new vertex buffer
newVertexData - > vertexBufferBinding - > setBinding ( bufferIndex , newVertexBuffer ) ;
}
// Re-create index buffer
switch ( indexData - > indexBuffer - > getType ( ) ) {
case HardwareIndexBuffer : : IT_16BIT :
{
uint16 * data = ( uint16 * ) indexData - > indexBuffer - > lock ( indexData - > indexStart * sizeof ( uint16 ) ,
indexData - > indexCount * sizeof ( uint16 ) , HardwareBuffer : : HBL_NORMAL ) ;
for ( uint32 i = 0 ; i < indexData - > indexCount ; i + + ) {
data [ i ] = ( uint16 ) indicesMap [ data [ i ] ] ;
}
indexData - > indexBuffer - > unlock ( ) ;
}
break ;
case HardwareIndexBuffer : : IT_32BIT :
{
uint32 * data = ( uint32 * ) indexData - > indexBuffer - > lock ( indexData - > indexStart * sizeof ( uint32 ) ,
indexData - > indexCount * sizeof ( uint32 ) , HardwareBuffer : : HBL_NORMAL ) ;
for ( uint32 i = 0 ; i < indexData - > indexCount ; i + + ) {
data [ i ] = ( uint32 ) indicesMap [ data [ i ] ] ;
}
indexData - > indexBuffer - > unlock ( ) ;
}
break ;
default :
throw new Ogre : : Exception ( 0 , " Unknown index buffer type " , " Converter.cpp::CountVertices " ) ;
break ;
}
// Store new attributes
subMesh - > useSharedVertices = false ;
subMesh - > vertexData = newVertexData ;
}
// Release shared vertex data
Fixed issue with incorrect vertex data.
This bug took quite some time to track down.
It appears that on at least gcc 4.8.3 there's some kind of bug which
makes the value assigned be off by one. I.e. if the map is empty, and
we're inserting the first instance, the "size()" method would return
"0", and that's the value we would be inserting. However, for some
reason which might have to do with caching, optimization or out-of-order
execution, the value written is actually the size _after_ the insertion.
So if the map was empty, the first entry would get the value of "1",
which would be the size _after_ the insertion.
And this would of course cause issues later on, with the memcpy writing
beyond the data, thus corrupting the memory, leading to segfaults.
2014-11-15 15:49:06 +01:00
OGRE_DELETE mesh - > sharedVertexData ;
2009-07-06 22:12:17 +02:00
mesh - > sharedVertexData = NULL ;
}
BatchedGeometry : : SubBatchIterator BatchedGeometry : : getSubBatchIterator ( ) const
{
return BatchedGeometry : : SubBatchIterator ( ( SubBatchMap & ) subBatchMap ) ;
2008-02-25 23:19:29 +00:00
}
String BatchedGeometry : : getFormatString ( SubEntity * ent )
{
2017-08-16 23:16:31 +02:00
Ogre : : StringStream str ;
2008-02-25 23:19:29 +00:00
str < < ent - > getMaterialName ( ) < < " | " ;
str < < ent - > getSubMesh ( ) - > indexData - > indexBuffer - > getType ( ) < < " | " ;
const VertexDeclaration : : VertexElementList & elemList = ent - > getSubMesh ( ) - > vertexData - > vertexDeclaration - > getElements ( ) ;
VertexDeclaration : : VertexElementList : : const_iterator i ;
for ( i = elemList . begin ( ) ; i ! = elemList . end ( ) ; + + i )
{
const VertexElement & element = * i ;
str < < element . getSource ( ) < < " | " ;
str < < element . getSemantic ( ) < < " | " ;
str < < element . getType ( ) < < " | " ;
}
return str . str ( ) ;
}
void BatchedGeometry : : build ( )
{
2011-03-20 17:05:24 +01:00
//Make sure the batch hasn't already been built
2008-02-25 23:19:29 +00:00
if ( built )
OGRE_EXCEPT ( Exception : : ERR_DUPLICATE_ITEM , " Invalid call to build() - geometry is already batched (call clear() first) " , " BatchedGeometry::GeomBatch::build() " ) ;
if ( subBatchMap . size ( ) ! = 0 ) {
//Finish bounds information
center = bounds . getCenter ( ) ; //Calculate bounds center
bounds . setMinimum ( bounds . getMinimum ( ) - center ) ; //Center the bounding box
bounds . setMaximum ( bounds . getMaximum ( ) - center ) ; //Center the bounding box
radius = bounds . getMaximum ( ) . length ( ) ; //Calculate BB radius
2009-07-06 22:12:17 +02:00
2008-02-25 23:19:29 +00:00
//Create scene node
sceneNode = parentSceneNode - > createChildSceneNode ( center ) ;
//Build each batch
for ( SubBatchMap : : iterator i = subBatchMap . begin ( ) ; i ! = subBatchMap . end ( ) ; + + i ) {
i - > second - > build ( ) ;
}
//Attach the batch to the scene node
sceneNode - > attachObject ( this ) ;
//Debug
//sceneNode->showBoundingBox(true);
built = true ;
}
2009-07-06 22:12:17 +02:00
2008-02-25 23:19:29 +00:00
}
void BatchedGeometry : : clear ( )
{
//Remove the batch from the scene
if ( sceneNode ) {
sceneNode - > removeAllChildren ( ) ;
2017-09-06 17:31:44 +02:00
sceneMgr - > destroySceneNode ( sceneNode ) ;
2008-02-25 23:19:29 +00:00
sceneNode = NULL ;
}
//Reset bounds information
boundsUndefined = true ;
center = Vector3 : : ZERO ;
radius = 0 ;
//Delete each batch
for ( SubBatchMap : : iterator i = subBatchMap . begin ( ) ; i ! = subBatchMap . end ( ) ; + + i ) {
delete i - > second ;
}
subBatchMap . clear ( ) ;
built = false ;
}
void BatchedGeometry : : _updateRenderQueue ( RenderQueue * queue )
{
//If visible...
if ( isVisible ( ) ) {
//Ask each batch to add itself to the render queue if appropriate
for ( SubBatchMap : : iterator i = subBatchMap . begin ( ) ; i ! = subBatchMap . end ( ) ; + + i ) {
i - > second - > addSelfToRenderQueue ( queue , getRenderQueueGroup ( ) ) ;
}
}
}
bool BatchedGeometry : : isVisible ( )
{
return mVisible & & withinFarDistance ;
}
void BatchedGeometry : : _notifyCurrentCamera ( Camera * cam )
{
if ( getRenderingDistance ( ) = = 0 ) {
withinFarDistance = true ;
} else {
2009-07-06 22:12:17 +02:00
//Calculate camera distance
Vector3 camVec = _convertToLocal ( cam - > getDerivedPosition ( ) ) - center ;
Real centerDistanceSquared = camVec . squaredLength ( ) ;
minDistanceSquared = max ( 0.0f , centerDistanceSquared - ( radius * radius ) ) ;
//Note: centerDistanceSquared measures the distance between the camera and the center of the GeomBatch,
//while minDistanceSquared measures the closest distance between the camera and the closest edge of the
//geometry's bounding sphere.
2008-02-25 23:19:29 +00:00
//Determine whether the BatchedGeometry is within the far rendering distance
withinFarDistance = minDistanceSquared < = Math : : Sqr ( getRenderingDistance ( ) ) ;
}
}
Ogre : : Vector3 BatchedGeometry : : _convertToLocal ( const Vector3 & globalVec ) const
{
assert ( parentSceneNode ) ;
//Convert from the given global position to the local coordinate system of the parent scene node.
return ( parentSceneNode - > getOrientation ( ) . Inverse ( ) * globalVec ) ;
}
BatchedGeometry : : SubBatch : : SubBatch ( BatchedGeometry * parent , SubEntity * ent )
{
2009-07-06 22:12:17 +02:00
meshType = ent - > getSubMesh ( ) ;
this - > parent = parent ;
built = false ;
2013-11-06 23:54:45 +01:00
requireVertexColors = false ;
// Material must always exist
2017-08-16 23:16:31 +02:00
Material * origMat = ( ( MaterialPtr ) MaterialManager : : getSingleton ( ) . getByName ( ent - > getMaterialName ( ) ) ) . get ( ) ;
2013-11-06 23:54:45 +01:00
if ( origMat ) {
material = MaterialManager : : getSingleton ( ) . getByName ( getMaterialClone ( * origMat ) - > getName ( ) ) ;
} else {
2017-08-16 23:16:31 +02:00
auto result = MaterialManager : : getSingleton ( ) . createOrRetrieve ( " PagedGeometry_Batched_Material " , " General " ) ;
if ( ! result . first ) {
2013-11-06 23:54:45 +01:00
OGRE_EXCEPT ( Exception : : ERR_INVALIDPARAMS , " BatchedGeometry failed to create a material for entity with invalid material. " , " BatchedGeometry::SubBatch::SubBatch(BatchedGeometry *parent, SubEntity *ent) " ) ;
}
2017-08-16 23:16:31 +02:00
material = Ogre : : static_pointer_cast < Ogre : : Material > ( result . first ) ;
2013-11-06 23:54:45 +01:00
}
2008-02-25 23:19:29 +00:00
//Setup vertex/index data structure
vertexData = meshType - > vertexData - > clone ( false ) ;
indexData = meshType - > indexData - > clone ( false ) ;
//Remove blend weights from vertex format
const VertexElement * blendIndices = vertexData - > vertexDeclaration - > findElementBySemantic ( VES_BLEND_INDICES ) ;
const VertexElement * blendWeights = vertexData - > vertexDeclaration - > findElementBySemantic ( VES_BLEND_WEIGHTS ) ;
if ( blendIndices & & blendWeights )
{
//Check for format errors
assert ( blendIndices - > getSource ( ) = = blendWeights - > getSource ( )
& & " Blend indices and weights should be in the same buffer " ) ;
assert ( blendIndices - > getSize ( ) + blendWeights - > getSize ( ) = = vertexData - > vertexBufferBinding - > getBuffer ( blendIndices - > getSource ( ) ) - > getVertexSize ( )
& & " Blend indices and blend buffers should have buffer to themselves! " ) ;
//Remove the blend weights
vertexData - > vertexBufferBinding - > unsetBinding ( blendIndices - > getSource ( ) ) ;
vertexData - > vertexDeclaration - > removeElement ( VES_BLEND_INDICES ) ;
vertexData - > vertexDeclaration - > removeElement ( VES_BLEND_WEIGHTS ) ;
# if OGRE_VERSION_MAJOR == 1 && OGRE_VERSION_MINOR > 2
vertexData - > closeGapsInBindings ( ) ;
# endif
}
//Reset vertex/index count
vertexData - > vertexStart = 0 ;
vertexData - > vertexCount = 0 ;
indexData - > indexStart = 0 ;
indexData - > indexCount = 0 ;
}
BatchedGeometry : : SubBatch : : ~ SubBatch ( )
{
clear ( ) ;
delete vertexData ;
2009-07-06 22:12:17 +02:00
delete indexData ;
}
Material * BatchedGeometry : : SubBatch : : getMaterialClone ( Material & mat )
{
String clonedName = mat . getName ( ) + " _Batched " ;
MaterialPtr clonedMat = MaterialManager : : getSingleton ( ) . getByName ( clonedName ) ;
2017-08-16 23:16:31 +02:00
if ( ! clonedMat )
2009-07-06 22:12:17 +02:00
clonedMat = mat . clone ( clonedName ) ;
2017-08-16 23:16:31 +02:00
return clonedMat . get ( ) ;
2009-07-06 22:12:17 +02:00
}
void BatchedGeometry : : SubBatch : : addSubEntity ( SubEntity * ent , const Vector3 & position , const Quaternion & orientation , const Vector3 & scale , const Ogre : : ColourValue & color , void * userData )
{
assert ( ! built ) ;
2008-02-25 23:19:29 +00:00
//Add this submesh to the queue
QueuedMesh newMesh ;
newMesh . mesh = ent - > getSubMesh ( ) ;
2009-07-06 22:12:17 +02:00
newMesh . position = position ;
newMesh . orientation = orientation ;
newMesh . scale = scale ;
newMesh . userData = userData ;
newMesh . color = color ;
if ( newMesh . color ! = ColourValue : : White ) {
requireVertexColors = true ;
2022-08-05 21:24:23 +02:00
std : : swap ( newMesh . color . r , newMesh . color . b ) ;
2009-07-06 22:12:17 +02:00
}
meshQueue . push_back ( newMesh ) ;
2008-02-25 23:19:29 +00:00
//Increment the vertex/index count so the buffers will have room for this mesh
vertexData - > vertexCount + = ent - > getSubMesh ( ) - > vertexData - > vertexCount ;
indexData - > indexCount + = ent - > getSubMesh ( ) - > indexData - > indexCount ;
}
void BatchedGeometry : : SubBatch : : build ( )
{
assert ( ! built ) ;
//Misc. setup
Vector3 batchCenter = parent - > center ;
HardwareIndexBuffer : : IndexType srcIndexType = meshType - > indexData - > indexBuffer - > getType ( ) ;
HardwareIndexBuffer : : IndexType destIndexType ;
if ( vertexData - > vertexCount > 0xFFFF | | srcIndexType = = HardwareIndexBuffer : : IT_32BIT )
destIndexType = HardwareIndexBuffer : : IT_32BIT ;
else
destIndexType = HardwareIndexBuffer : : IT_16BIT ;
//Allocate the index buffer
indexData - > indexBuffer = HardwareBufferManager : : getSingleton ( )
. createIndexBuffer ( destIndexType , indexData - > indexCount , HardwareBuffer : : HBU_STATIC_WRITE_ONLY ) ;
//Lock the index buffer
uint32 * indexBuffer32 ;
uint16 * indexBuffer16 ;
if ( destIndexType = = HardwareIndexBuffer : : IT_32BIT )
indexBuffer32 = static_cast < uint32 * > ( indexData - > indexBuffer - > lock ( HardwareBuffer : : HBL_DISCARD ) ) ;
else
indexBuffer16 = static_cast < uint16 * > ( indexData - > indexBuffer - > lock ( HardwareBuffer : : HBL_DISCARD ) ) ;
//Allocate & lock the vertex buffers
std : : vector < uchar * > vertexBuffers ;
std : : vector < VertexDeclaration : : VertexElementList > vertexBufferElements ;
VertexBufferBinding * vertBinding = vertexData - > vertexBufferBinding ;
VertexDeclaration * vertDecl = vertexData - > vertexDeclaration ;
for ( Ogre : : ushort i = 0 ; i < vertBinding - > getBufferCount ( ) ; + + i )
{
HardwareVertexBufferSharedPtr buffer = HardwareBufferManager : : getSingleton ( )
. createVertexBuffer ( vertDecl - > getVertexSize ( i ) , vertexData - > vertexCount , HardwareBuffer : : HBU_STATIC_WRITE_ONLY ) ;
vertBinding - > setBinding ( i , buffer ) ;
2009-07-06 22:12:17 +02:00
2008-02-25 23:19:29 +00:00
vertexBuffers . push_back ( static_cast < uchar * > ( buffer - > lock ( HardwareBuffer : : HBL_DISCARD ) ) ) ;
vertexBufferElements . push_back ( vertDecl - > findElementsBySource ( i ) ) ;
2009-07-06 22:12:17 +02:00
}
//If no vertex colors are used, make sure the final batch includes them (so the shade values work)
if ( requireVertexColors ) {
if ( ! vertexData - > vertexDeclaration - > findElementBySemantic ( VES_DIFFUSE ) ) {
Ogre : : ushort i = ( Ogre : : ushort ) vertBinding - > getBufferCount ( ) ;
2009-05-16 01:09:46 +02:00
vertDecl - > addElement ( i , 0 , VET_COLOUR , VES_DIFFUSE ) ;
HardwareVertexBufferSharedPtr buffer = HardwareBufferManager : : getSingleton ( )
. createVertexBuffer ( vertDecl - > getVertexSize ( i ) , vertexData - > vertexCount , HardwareBuffer : : HBU_STATIC_WRITE_ONLY ) ;
vertBinding - > setBinding ( i , buffer ) ;
2009-07-06 22:12:17 +02:00
2009-05-16 01:09:46 +02:00
vertexBuffers . push_back ( static_cast < uchar * > ( buffer - > lock ( HardwareBuffer : : HBL_DISCARD ) ) ) ;
vertexBufferElements . push_back ( vertDecl - > findElementsBySource ( i ) ) ;
2009-07-06 22:12:17 +02:00
}
Pass * p = material - > getTechnique ( 0 ) - > getPass ( 0 ) ;
p - > setVertexColourTracking ( TVC_AMBIENT ) ;
}
//For each queued mesh...
MeshQueueIterator it ;
size_t indexOffset = 0 ;
2008-02-25 23:19:29 +00:00
for ( it = meshQueue . begin ( ) ; it ! = meshQueue . end ( ) ; + + it ) {
const QueuedMesh queuedMesh = ( * it ) ;
const IndexData * sourceIndexData = queuedMesh . mesh - > indexData ;
const VertexData * sourceVertexData = queuedMesh . mesh - > vertexData ;
//Copy mesh vertex data into the vertex buffer
VertexBufferBinding * sourceBinds = sourceVertexData - > vertexBufferBinding ;
VertexBufferBinding * destBinds = vertexData - > vertexBufferBinding ;
for ( Ogre : : ushort i = 0 ; i < destBinds - > getBufferCount ( ) ; + + i )
{
if ( i < sourceBinds - > getBufferCount ( ) ) {
//Lock the input buffer
HardwareVertexBufferSharedPtr sourceBuffer = sourceBinds - > getBuffer ( i ) ;
uchar * sourceBase = static_cast < uchar * > ( sourceBuffer - > lock ( HardwareBuffer : : HBL_READ_ONLY ) ) ;
//Get the locked output buffer
uchar * destBase = vertexBuffers [ i ] ;
//Copy vertices
float * sourcePtr , * destPtr ;
for ( size_t v = 0 ; v < sourceVertexData - > vertexCount ; + + v )
{
// Iterate over vertex elements
VertexDeclaration : : VertexElementList & elems = vertexBufferElements [ i ] ;
VertexDeclaration : : VertexElementList : : iterator ei ;
for ( ei = elems . begin ( ) ; ei ! = elems . end ( ) ; + + ei )
{
VertexElement & elem = * ei ;
elem . baseVertexPointerToElement ( sourceBase , & sourcePtr ) ;
elem . baseVertexPointerToElement ( destBase , & destPtr ) ;
Vector3 tmp ;
uint32 tmpColor ;
uint8 tmpR , tmpG , tmpB , tmpA ;
switch ( elem . getSemantic ( ) )
{
case VES_POSITION :
tmp . x = * sourcePtr + + ;
tmp . y = * sourcePtr + + ;
tmp . z = * sourcePtr + + ;
2009-07-06 22:12:17 +02:00
2008-02-25 23:19:29 +00:00
//Transform
tmp = ( queuedMesh . orientation * ( tmp * queuedMesh . scale ) ) + queuedMesh . position ;
tmp - = batchCenter ; //Adjust for batch center
* destPtr + + = tmp . x ;
* destPtr + + = tmp . y ;
* destPtr + + = tmp . z ;
break ;
case VES_NORMAL :
tmp . x = * sourcePtr + + ;
tmp . y = * sourcePtr + + ;
tmp . z = * sourcePtr + + ;
//Rotate
tmp = queuedMesh . orientation * tmp ;
* destPtr + + = tmp . x ;
* destPtr + + = tmp . y ;
* destPtr + + = tmp . z ;
break ;
2009-07-06 22:12:17 +02:00
case VES_DIFFUSE :
tmpColor = * ( ( uint32 * ) sourcePtr + + ) ;
tmpR = ( ( tmpColor ) & 0xFF ) * queuedMesh . color . r ;
tmpG = ( ( tmpColor > > 8 ) & 0xFF ) * queuedMesh . color . g ;
tmpB = ( ( tmpColor > > 16 ) & 0xFF ) * queuedMesh . color . b ;
tmpA = ( tmpColor > > 24 ) & 0xFF ;
tmpColor = tmpR | ( tmpG < < 8 ) | ( tmpB < < 16 ) | ( tmpA < < 24 ) ;
2008-02-25 23:19:29 +00:00
* ( ( uint32 * ) destPtr + + ) = tmpColor ;
break ;
case VES_TANGENT :
case VES_BINORMAL :
tmp . x = * sourcePtr + + ;
tmp . y = * sourcePtr + + ;
tmp . z = * sourcePtr + + ;
//Rotate
tmp = queuedMesh . orientation * tmp ;
* destPtr + + = tmp . x ;
* destPtr + + = tmp . y ;
* destPtr + + = tmp . z ;
break ;
default :
//Raw copy
memcpy ( destPtr , sourcePtr , VertexElement : : getTypeSize ( elem . getType ( ) ) ) ;
break ;
} ;
}
// Increment both pointers
destBase + = sourceBuffer - > getVertexSize ( ) ;
sourceBase + = sourceBuffer - > getVertexSize ( ) ;
}
//Unlock the input buffer
2009-07-06 22:12:17 +02:00
vertexBuffers [ i ] = destBase ;
sourceBuffer - > unlock ( ) ;
} else {
assert ( requireVertexColors ) ;
//Get the locked output buffer
uint32 * startPtr = ( uint32 * ) vertexBuffers [ vertBinding - > getBufferCount ( ) - 1 ] ;
uint32 * endPtr = startPtr + sourceVertexData - > vertexCount ;
//Generate color
uint8 tmpR = queuedMesh . color . r * 255 ;
uint8 tmpG = queuedMesh . color . g * 255 ;
uint8 tmpB = queuedMesh . color . b * 255 ;
uint32 tmpColor = tmpR | ( tmpG < < 8 ) | ( tmpB < < 16 ) | ( 0xFF < < 24 ) ;
//Copy colors
2008-02-25 23:19:29 +00:00
while ( startPtr < endPtr ) {
* startPtr + + = tmpColor ;
}
vertexBuffers [ vertBinding - > getBufferCount ( ) - 1 ] + = ( sizeof ( uint32 ) * sourceVertexData - > vertexCount ) ;
}
}
//Copy mesh index data into the index buffer
if ( srcIndexType = = HardwareIndexBuffer : : IT_32BIT ) {
//Lock the input buffer
uint32 * source = static_cast < uint32 * > ( sourceIndexData - > indexBuffer - > lock (
sourceIndexData - > indexStart , sourceIndexData - > indexCount , HardwareBuffer : : HBL_READ_ONLY
) ) ;
uint32 * sourceEnd = source + sourceIndexData - > indexCount ;
//And copy it to the output buffer
while ( source ! = sourceEnd ) {
* indexBuffer32 + + = static_cast < uint32 > ( * source + + + indexOffset ) ;
}
2009-07-06 22:12:17 +02:00
2008-02-25 23:19:29 +00:00
//Unlock the input buffer
sourceIndexData - > indexBuffer - > unlock ( ) ;
//Increment the index offset
indexOffset + = sourceVertexData - > vertexCount ;
} else {
if ( destIndexType = = HardwareIndexBuffer : : IT_32BIT ) {
//-- Convert 16 bit to 32 bit indices --
//Lock the input buffer
uint16 * source = static_cast < uint16 * > ( sourceIndexData - > indexBuffer - > lock (
sourceIndexData - > indexStart , sourceIndexData - > indexCount , HardwareBuffer : : HBL_READ_ONLY
) ) ;
uint16 * sourceEnd = source + sourceIndexData - > indexCount ;
//And copy it to the output buffer
while ( source ! = sourceEnd ) {
uint32 indx = * source + + ;
* indexBuffer32 + + = ( indx + indexOffset ) ;
}
//Unlock the input buffer
sourceIndexData - > indexBuffer - > unlock ( ) ;
//Increment the index offset
indexOffset + = sourceVertexData - > vertexCount ;
} else {
//Lock the input buffer
uint16 * source = static_cast < uint16 * > ( sourceIndexData - > indexBuffer - > lock (
sourceIndexData - > indexStart , sourceIndexData - > indexCount , HardwareBuffer : : HBL_READ_ONLY
) ) ;
uint16 * sourceEnd = source + sourceIndexData - > indexCount ;
//And copy it to the output buffer
while ( source ! = sourceEnd ) {
* indexBuffer16 + + = static_cast < uint16 > ( * source + + + indexOffset ) ;
}
//Unlock the input buffer
sourceIndexData - > indexBuffer - > unlock ( ) ;
//Increment the index offset
indexOffset + = sourceVertexData - > vertexCount ;
}
}
}
//Unlock buffers
indexData - > indexBuffer - > unlock ( ) ;
for ( Ogre : : ushort i = 0 ; i < vertBinding - > getBufferCount ( ) ; + + i )
vertBinding - > getBuffer ( i ) - > unlock ( ) ;
//Clear mesh queue
meshQueue . clear ( ) ;
built = true ;
}
void BatchedGeometry : : SubBatch : : clear ( )
{
//If built, delete the batch
if ( built ) {
//Delete buffers
2017-08-16 23:16:31 +02:00
indexData - > indexBuffer . reset ( ) ;
2008-02-25 23:19:29 +00:00
vertexData - > vertexBufferBinding - > unsetAllBindings ( ) ;
//Reset vertex/index count
vertexData - > vertexStart = 0 ;
vertexData - > vertexCount = 0 ;
indexData - > indexStart = 0 ;
indexData - > indexCount = 0 ;
}
//Clear mesh queue
meshQueue . clear ( ) ;
built = false ;
}
void BatchedGeometry : : SubBatch : : addSelfToRenderQueue ( RenderQueue * queue , uint8 group )
{
2009-07-06 22:12:17 +02:00
if ( built ) {
//Update material technique based on camera distance
2017-08-16 23:16:31 +02:00
assert ( material ) ;
2009-07-06 22:12:17 +02:00
bestTechnique = material - > getBestTechnique ( material - > getLodIndex ( parent - > minDistanceSquared * parent - > minDistanceSquared ) ) ;
//Add to render queue
queue - > addRenderable ( this , group ) ;
2008-02-25 23:19:29 +00:00
}
}
void BatchedGeometry : : SubBatch : : getRenderOperation ( RenderOperation & op )
{
op . operationType = RenderOperation : : OT_TRIANGLE_LIST ;
op . srcRenderable = this ;
op . useIndexes = true ;
op . vertexData = vertexData ;
op . indexData = indexData ;
}
Real BatchedGeometry : : SubBatch : : getSquaredViewDepth ( const Camera * cam ) const
{
Vector3 camVec = parent - > _convertToLocal ( cam - > getDerivedPosition ( ) ) - parent - > center ;
return camVec . squaredLength ( ) ;
}
# if OGRE_VERSION_MAJOR == 1 && OGRE_VERSION_MINOR <= 2
//Dagon-compatible getLights()
const Ogre : : LightList & BatchedGeometry : : SubBatch : : getLights ( void ) const
{
return parent - > sceneNode - > findLights ( parent - > radius ) ;
}
# else
//Eihort-compatible getLights()
const Ogre : : LightList & BatchedGeometry : : SubBatch : : getLights ( void ) const
2009-07-06 22:12:17 +02:00
{
return parent - > queryLights ( ) ;
}
}
# endif