#include "CmMesh.h" #include "CmMeshRTTI.h" #include "CmMeshData.h" #include "CmVector2.h" #include "CmVector3.h" #include "CmDebug.h" #include "CmHardwareBufferManager.h" #include "CmMeshManager.h" #include "CmCoreThread.h" #include "CmAsyncOp.h" #include "CmAABox.h" #include "CmVertexDataDesc.h" #include "CmProfiler.h" namespace CamelotFramework { Mesh::Mesh(UINT32 numVertices, UINT32 numIndices, const VertexDataDescPtr& vertexDesc, MeshBufferType bufferType, DrawOperationType drawOp, IndexBuffer::IndexType indexType) :mVertexData(nullptr), mIndexData(nullptr), mNumVertices(numVertices), mNumIndices(numIndices), mVertexDesc(vertexDesc), mBufferType(bufferType), mIndexType(indexType), mDefaultSubMesh(0, numIndices, drawOp) { mSubMeshes.reserve(10); } Mesh::Mesh(UINT32 numVertices, UINT32 numIndices, const VertexDataDescPtr& vertexDesc, const MeshDataPtr& initialMeshData, MeshBufferType bufferType, DrawOperationType drawOp, IndexBuffer::IndexType indexType) :mVertexData(nullptr), mIndexData(nullptr), mNumVertices(numVertices), mNumIndices(numIndices), mVertexDesc(vertexDesc), mBufferType(bufferType), mIndexType(indexType), mTempInitialMeshData(initialMeshData), mDefaultSubMesh(0, numIndices, drawOp) { mSubMeshes.reserve(10); } Mesh::Mesh(const MeshDataPtr& initialMeshData, MeshBufferType bufferType, DrawOperationType drawOp) :mVertexData(nullptr), mIndexData(nullptr), mNumVertices(initialMeshData->getNumVertices()), mNumIndices(initialMeshData->getNumIndices()), mBufferType(bufferType), mIndexType(initialMeshData->getIndexType()), mVertexDesc(initialMeshData->getVertexDesc()), mTempInitialMeshData(initialMeshData), mDefaultSubMesh(0, initialMeshData->getNumIndices(), drawOp) { mSubMeshes.reserve(10); } Mesh::Mesh() :mVertexData(nullptr), mIndexData(nullptr), mNumVertices(0), mNumIndices(0), mBufferType(MeshBufferType::Static), mIndexType(IndexBuffer::IT_32BIT), mDefaultSubMesh(0, 0, DOT_TRIANGLE_LIST) { mSubMeshes.reserve(10); } Mesh::~Mesh() { } void Mesh::writeSubresource(UINT32 subresourceIdx, const GpuResourceData& data, bool discardEntireBuffer) { THROW_IF_NOT_CORE_THREAD; if(data.getTypeId() != TID_MeshData) CM_EXCEPT(InvalidParametersException, "Invalid GpuResourceData type. Only MeshData is supported."); if(discardEntireBuffer) { if(mBufferType == MeshBufferType::Static) { LOGWRN("Buffer discard is enabled but buffer was not created as dynamic. Disabling discard."); discardEntireBuffer = false; } } else { if(mBufferType == MeshBufferType::Dynamic) { LOGWRN("Buffer discard is not enabled but buffer was created as dynamic. Enabling discard."); discardEntireBuffer = true; } } const MeshData& meshData = static_cast(data); // Indices UINT32 indexOffset = meshData.getResourceIndexOffset() * meshData.getIndexElementSize(); UINT32 indicesSize = meshData.getIndexBufferSize(); UINT8* srcIdxData = meshData.getIndexData(); if((indexOffset + indicesSize) > mIndexData->indexBuffer->getSizeInBytes()) CM_EXCEPT(InvalidParametersException, "Index buffer values are being written out of valid range."); mIndexData->indexBuffer->writeData(indexOffset, indicesSize, srcIdxData, discardEntireBuffer); // Vertices for(UINT32 i = 0; i <= meshData.getVertexDesc()->getMaxStreamIdx(); i++) { if(!meshData.getVertexDesc()->hasStream(i)) continue; if(i >= mVertexData->getBufferCount()) CM_EXCEPT(InvalidParametersException, "Attempting to write to a vertex stream that doesn't exist on this mesh."); VertexBufferPtr vertexBuffer = mVertexData->getBuffer(i); UINT32 bufferOffset = meshData.getResourceVertexOffset() * meshData.getVertexDesc()->getVertexStride(i); UINT32 bufferSize = meshData.getStreamSize(i); UINT8* srcVertBufferData = meshData.getStreamData(i); if((bufferOffset + bufferSize) > vertexBuffer->getSizeInBytes()) CM_EXCEPT(InvalidParametersException, "Vertex buffer values for stream \"" + toString(i) + "\" are being written out of valid range."); if(vertexBuffer->vertexColorReqRGBFlip()) { UINT8* bufferCopy = (UINT8*)cm_alloc(bufferSize); memcpy(bufferCopy, srcVertBufferData, bufferSize); // TODO Low priority - Attempt to avoid this copy UINT32 vertexStride = meshData.getVertexDesc()->getVertexStride(i); for(INT32 semanticIdx = 0; semanticIdx < VertexBuffer::MAX_SEMANTIC_IDX; semanticIdx++) { if(!meshData.getVertexDesc()->hasElement(VES_COLOR, semanticIdx, i)) continue; UINT8* colorData = bufferCopy + meshData.getElementOffset(VES_COLOR, semanticIdx, i); for(UINT32 j = 0; j < mVertexData->vertexCount; j++) { UINT32* curColor = (UINT32*)colorData; (*curColor) = ((*curColor) & 0xFF00FF00) | ((*curColor >> 16) & 0x000000FF) | ((*curColor << 16) & 0x00FF0000); colorData += vertexStride; } } vertexBuffer->writeData(bufferOffset, bufferSize, bufferCopy, discardEntireBuffer); cm_free(bufferCopy); } else { vertexBuffer->writeData(bufferOffset, bufferSize, srcVertBufferData, discardEntireBuffer); } } } void Mesh::readSubresource(UINT32 subresourceIdx, GpuResourceData& data) { THROW_IF_NOT_CORE_THREAD; if(data.getTypeId() != TID_MeshData) CM_EXCEPT(InvalidParametersException, "Invalid GpuResourceData type. Only MeshData is supported."); IndexBuffer::IndexType indexType = IndexBuffer::IT_32BIT; if(mIndexData) indexType = mIndexData->indexBuffer->getType(); MeshData& meshData = static_cast(data); if(mIndexData) { UINT8* idxData = static_cast(mIndexData->indexBuffer->lock(GBL_READ_ONLY)); UINT32 idxElemSize = mIndexData->indexBuffer->getIndexSize(); UINT32 indexResourceOffset = meshData.getResourceIndexOffset(); UINT8* indices = nullptr; if(indexType == IndexBuffer::IT_16BIT) indices = (UINT8*)meshData.getIndices16(); else indices = (UINT8*)meshData.getIndices32(); UINT32 remainingNumIndices = (UINT32)std::max(0, (INT32)(mNumIndices - indexResourceOffset)); UINT32 numIndicesToCopy = std::min(remainingNumIndices, meshData.getNumIndices()); UINT32 indicesSize = numIndicesToCopy * idxElemSize; if(indicesSize > meshData.getIndexBufferSize()) CM_EXCEPT(InvalidParametersException, "Provided buffer doesn't have enough space to store mesh indices."); idxData += indexResourceOffset * idxElemSize; memcpy(indices, idxData, numIndicesToCopy * idxElemSize); mIndexData->indexBuffer->unlock(); } if(mVertexData) { auto vertexBuffers = mVertexData->getBuffers(); UINT32 streamIdx = 0; for(auto iter = vertexBuffers.begin(); iter != vertexBuffers.end() ; ++iter) { if(streamIdx > meshData.getVertexDesc()->getMaxStreamIdx()) continue; UINT32 vertexResourceOffset = meshData.getResourceVertexOffset(); UINT32 remainingNumVertices = (UINT32)std::max(0, (INT32)(mNumVertices - vertexResourceOffset)); UINT32 numVerticesToCopy = std::min(remainingNumVertices, meshData.getNumVertices()); VertexBufferPtr vertexBuffer = iter->second; UINT32 bufferSize = vertexBuffer->getVertexSize() * numVerticesToCopy; UINT8* vertDataPtr = static_cast(vertexBuffer->lock(GBL_READ_ONLY)) + vertexResourceOffset * meshData.getVertexDesc()->getVertexStride(streamIdx); if(bufferSize > meshData.getStreamSize(streamIdx)) CM_EXCEPT(InvalidParametersException, "Provided buffer doesn't have enough space to store mesh vertices."); UINT8* dest = meshData.getStreamData(streamIdx); memcpy(dest, vertDataPtr, bufferSize); vertexBuffer->unlock(); streamIdx++; } } } MeshDataPtr Mesh::allocateSubresourceBuffer(UINT32 subresourceIdx) const { IndexBuffer::IndexType indexType = IndexBuffer::IT_32BIT; if(mIndexData) indexType = mIndexData->indexBuffer->getType(); MeshDataPtr meshData = cm_shared_ptr(mVertexData->vertexCount, mNumIndices, mVertexDesc, indexType); return meshData; } const AABox& Mesh::getBounds() const { // TODO - Retrieve bounds for entire mesh (need to calculate them during creation) return AABox::BOX_EMPTY; } const AABox& Mesh::getBounds(UINT32 submeshIdx) const { // TODO - Retrieve bounds a specific sub-mesh (need to calculate them during creation) return AABox::BOX_EMPTY; } std::shared_ptr Mesh::getVertexData() const { THROW_IF_NOT_CORE_THREAD; return mVertexData; } std::shared_ptr Mesh::getIndexData() const { THROW_IF_NOT_CORE_THREAD; return mIndexData; } void Mesh::clearSubMeshes() { THROW_IF_CORE_THREAD; mSubMeshes.clear(); } void Mesh::addSubMesh(UINT32 indexOffset, UINT32 indexCount, DrawOperationType drawOp) { if((indexOffset + indexCount) >= mNumIndices) { LOGWRN("Provided sub-mesh references indexes out of range. Sub-mesh range: " + toString(indexOffset) + " .. " + toString(indexOffset + indexCount) + "." \ "Valid range is: 0 .. " + toString(mNumIndices) + ". Ignoring command."); return; } mSubMeshes.push_back(SubMesh(indexOffset, indexCount, drawOp)); } void Mesh::setSubMeshes(const Vector::type& subMeshes) { THROW_IF_CORE_THREAD; for(auto& subMesh : subMeshes) { if((subMesh.indexOffset + subMesh.indexCount) >= mNumIndices) { LOGWRN("Provided sub-mesh references indexes out of range. Sub-mesh range: " + toString(subMesh.indexOffset) + " .. " + toString(subMesh.indexOffset + subMesh.indexCount) + "." \ "Valid range is: 0 .. " + toString(mNumIndices) + ". Ignoring command."); return; } } mSubMeshes = subMeshes; } const SubMesh& Mesh::getSubMesh(UINT32 subMeshIdx) const { THROW_IF_CORE_THREAD; if(mSubMeshes.size() == 0 && subMeshIdx == 0) { return mDefaultSubMesh; } if(subMeshIdx < 0 || subMeshIdx >= mSubMeshes.size()) { CM_EXCEPT(InvalidParametersException, "Invalid sub-mesh index (" + toString(subMeshIdx) + "). Number of sub-meshes available: " + toString((int)mSubMeshes.size())); } return mSubMeshes[subMeshIdx]; } UINT32 Mesh::getNumSubMeshes() const { THROW_IF_CORE_THREAD; if(mSubMeshes.size() > 0) return (UINT32)mSubMeshes.size(); else { if(mDefaultSubMesh.indexCount > 0) return 1; else return 0; } } void Mesh::initialize_internal() { THROW_IF_NOT_CORE_THREAD; mIndexData = std::shared_ptr(cm_new()); mIndexData->indexCount = mNumIndices; mIndexData->indexBuffer = HardwareBufferManager::instance().createIndexBuffer( mIndexType, mIndexData->indexCount, mBufferType == MeshBufferType::Dynamic ? GBU_DYNAMIC : GBU_STATIC); mVertexData = std::shared_ptr(cm_new()); mVertexData->vertexCount = mNumVertices; mVertexData->vertexDeclaration = mVertexDesc->createDeclaration(); for(UINT32 i = 0; i <= mVertexDesc->getMaxStreamIdx(); i++) { if(!mVertexDesc->hasStream(i)) continue; VertexBufferPtr vertexBuffer = HardwareBufferManager::instance().createVertexBuffer( mVertexData->vertexDeclaration->getVertexSize(i), mVertexData->vertexCount, mBufferType == MeshBufferType::Dynamic ? GBU_DYNAMIC : GBU_STATIC); mVertexData->setBuffer(i, vertexBuffer); } // TODO Low priority - DX11 (and maybe OpenGL)? allow an optimization that allows you to set // buffer data upon buffer construction, instead of setting it in a second step like I do here if(mTempInitialMeshData != nullptr) { writeSubresource(0, *mTempInitialMeshData, mBufferType == MeshBufferType::Dynamic); mTempInitialMeshData = nullptr; } Resource::initialize_internal(); } void Mesh::destroy_internal() { THROW_IF_NOT_CORE_THREAD; Resource::destroy_internal(); } HMesh Mesh::dummy() { return MeshManager::instance().getDummyMesh(); } /************************************************************************/ /* SERIALIZATION */ /************************************************************************/ RTTITypeBase* Mesh::getRTTIStatic() { return MeshRTTI::instance(); } RTTITypeBase* Mesh::getRTTI() const { return Mesh::getRTTIStatic(); } /************************************************************************/ /* STATICS */ /************************************************************************/ HMesh Mesh::create(UINT32 numVertices, UINT32 numIndices, const VertexDataDescPtr& vertexDesc, MeshBufferType bufferType, DrawOperationType drawOp, IndexBuffer::IndexType indexType) { MeshPtr meshPtr = MeshManager::instance().create(numVertices, numIndices, vertexDesc, bufferType, drawOp, indexType); return static_resource_cast(Resource::_createResourceHandle(meshPtr)); } HMesh Mesh::create(UINT32 numVertices, UINT32 numIndices, const VertexDataDescPtr& vertexDesc, const MeshDataPtr& initialMeshData, MeshBufferType bufferType, DrawOperationType drawOp, IndexBuffer::IndexType indexType) { MeshPtr meshPtr = MeshManager::instance().create(numVertices, numIndices, vertexDesc, initialMeshData, bufferType, drawOp, indexType); return static_resource_cast(Resource::_createResourceHandle(meshPtr)); } HMesh Mesh::create(const MeshDataPtr& initialMeshData, MeshBufferType bufferType, DrawOperationType drawOp) { MeshPtr meshPtr = MeshManager::instance().create(initialMeshData, bufferType, drawOp); return static_resource_cast(Resource::_createResourceHandle(meshPtr)); } }