/*------------------------------------------------------------------------------------- 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. -------------------------------------------------------------------------------------*/ //WindBatchedGeometry.h //A "lightweight" version of Ogre::StaticGeometry, which gives you a little more control //over the batch materials, etc. //------------------------------------------------------------------------------------- #include "WindBatchedGeometry.h" #include "PagedGeometry.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Ogre; namespace Forests { //------------------------------------------------------------------------------------- WindBatchedGeometry::WindBatchedGeometry(SceneManager *mgr, SceneNode *rootSceneNode):BatchedGeometry(mgr, rootSceneNode) { mGeom = NULL; } void WindBatchedGeometry::addEntity(Entity *ent, const Vector3 &position, const Quaternion &orientation, const Vector3 &scale, const Ogre::ColourValue &color) { MeshPtr mesh = ent->getMesh(); if (mesh->sharedVertexData != NULL) OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Shared vertex data not allowed", "BatchedGeometry::addEntity()"); //For each subentity for (uint32 i = 0; i < ent->getNumSubEntities(); ++i){ //Get the subentity SubEntity *subEntity = ent->getSubEntity(i); SubMesh *subMesh = subEntity->getSubMesh(); //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... WindSubBatch *batch; SubBatchMap::iterator batchIter = subBatchMap.find(formatStr); if (batchIter != subBatchMap.end()){ //Use the batch batch = dynamic_cast(batchIter->second); } else { //Otherwise create a new batch batch = new WindSubBatch(this, subEntity); subBatchMap.insert(std::pair(formatStr, batch)); } //Now add the submesh to the compatible batch batch->addSubEntity(subEntity, position, orientation, scale, color, ent); } //Update bounding box Matrix4 mat(orientation); mat.setScale(scale); AxisAlignedBox entBounds = ent->getBoundingBox(); entBounds.transform(mat); 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); } } WindBatchedGeometry::WindSubBatch::WindSubBatch(WindBatchedGeometry *parent, SubEntity *ent):BatchedGeometry::SubBatch(parent, ent) {} void WindBatchedGeometry::WindSubBatch::build() { assert(!built); //Misc. setup Vector3 batchCenter = dynamic_cast(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(indexData->indexBuffer->lock(HardwareBuffer::HBL_DISCARD)); else indexBuffer16 = static_cast(indexData->indexBuffer->lock(HardwareBuffer::HBL_DISCARD)); //Allocate & lock the vertex buffers std::vector vertexBuffers; std::vector vertexBufferElements; VertexBufferBinding *vertBinding = vertexData->vertexBufferBinding; VertexDeclaration *vertDecl = vertexData->vertexDeclaration; unsigned short texCoordCount = 0; for (unsigned short j = 0; j < vertexData->vertexDeclaration->getElementCount(); ++j) { const VertexElement *el = vertexData->vertexDeclaration->getElement(j); if (el->getSemantic() == VES_TEXTURE_COORDINATES) { ++ texCoordCount; } } Ogre::ushort k = (Ogre::ushort)vertBinding->getBufferCount(); vertDecl->addElement(k-1, vertDecl->getVertexSize(0), VET_FLOAT4 , VES_TEXTURE_COORDINATES, texCoordCount); vertDecl->addElement(k-1, vertDecl->getVertexSize(0), VET_FLOAT4 , VES_TEXTURE_COORDINATES, texCoordCount+1); 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); vertexBuffers.push_back(static_cast(buffer->lock(HardwareBuffer::HBL_DISCARD))); vertexBufferElements.push_back(vertDecl->findElementsBySource(i)); } //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(); 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); vertexBuffers.push_back(static_cast(buffer->lock(HardwareBuffer::HBL_DISCARD))); vertexBufferElements.push_back(vertDecl->findElementsBySource(i)); } Pass *p = material->getTechnique(0)->getPass(0); p->setVertexColourTracking(TVC_AMBIENT); } std::string entityName; Ogre::AxisAlignedBox entityBounds; //For each queued mesh... MeshQueueIterator it; size_t indexOffset = 0; for (it = meshQueue.begin(); it != meshQueue.end(); ++it) { const QueuedMesh queuedMesh = (*it); //const QueuedMesh queuedMesh = dynamic_cast((*it)); const IndexData *sourceIndexData = queuedMesh.mesh->indexData; const VertexData *sourceVertexData = queuedMesh.mesh->vertexData; Entity * ent = static_cast(queuedMesh.userData); entityName = ent->getName(); entityBounds = ent->getBoundingBox(); // vector to stock the original y value of every vertex because batchCenter doesn't take consider the height of the ground Vector3 vertexPos; float maxHeight = entityBounds.getMaximum().y; float factorX = dynamic_cast(parent)->mGeom->getCustomParam(entityName, "windFactorX", 0); // amplitude in X float factorY = dynamic_cast(parent)->mGeom->getCustomParam(entityName, "windFactorY", 0); // amplitude in Y //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(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++; //Transform tmp = (queuedMesh.orientation * (tmp * queuedMesh.scale)) + queuedMesh.position; vertexPos = tmp - 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; 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); *((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; case VES_TEXTURE_COORDINATES: if (elem.getIndex() == texCoordCount) { // parameters to be passed to the shader *destPtr++ = vertexPos.x; // radius coefficient *destPtr++ = vertexPos.y / maxHeight; // height coefficient *destPtr++ = factorX; *destPtr++ = factorY; } else { if (elem.getIndex() == texCoordCount + 1 ) { // original position for each vertex *destPtr++ = queuedMesh.position.x; *destPtr++ = queuedMesh.position.y; *destPtr++ = queuedMesh.position.z; *destPtr++ = 0; } else { memcpy(destPtr, sourcePtr, VertexElement::getTypeSize(elem.getType())); } } break; default: //Raw copy memcpy(destPtr, sourcePtr, VertexElement::getTypeSize(elem.getType())); break; }; } // Increment both pointers destBase += vertDecl->getVertexSize(i); sourceBase += sourceBuffer->getVertexSize(); } //Unlock the input buffer 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 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(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(*source++ + indexOffset); } //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(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(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(*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; } }