Hello!
I've been using the MovableText created in this thread and it works great!
As a challenge (and perhaps future proofing?) I wanted to update it to not rely on any v1 objects.
Currently it uses the v1::RenderOperation to generate planes for each letter and so on and I would like to swap that out for the Vao method, as in the DynamicMesh sample.
I've swapped out the code in the SetupGeometry function but I'm getting a segfault in the addRenderableVX functions.
I say X because by default it ends up in the V1 renderqueue, I can't seem to find what decides this?
When I set the renderqueuegroup in the constructor to a V2 queue it ends up in addRenderableV2 but still segfaults.
I tested just returning immediately in the setupGeometry function (in other words not mess with any buffers and so on) but it still segfaults.
The callstack simply ends in addRenderableV2 (or addRenderableV1) which I find odd since they just call addRenderable in turn.
I've used Ogre::Item and Ogre::SubItem as examples (since it's almost the same anyway). It still inherits Ogre::MovableObject and Ogre::Renderable.
Adds itself to its mRenderables and adds the generated vao to mVaoPerLod for shadow and normal, like SubItem does with the mesh.
I'm not really sure what I need to post here to diagnose this. Is there some checklist of things I need to fulfill to create a custom V2 MovableObject?
I realize its been three years but thanks a ton for the updated MovableText! If my attempt is in vain I'll just keep using the one posted here.
Here's the updated setupGeometry (note it is not cleaned up at all, I'm sure it can still be improved greatly):
Code: Select all
void MovableText::setupGeometry()
{
assert(ogreFont_);
assert(ogreHlmsDatablock_);
mVaoPerLod[Ogre::VpNormal].clear();
mVaoPerLod[Ogre::VpShadow].clear();
Ogre::RenderSystem* renderSystem = Ogre::Root::getSingletonPtr()->getRenderSystem();
Ogre::VaoManager* vaoManager = renderSystem->getVaoManager();
Ogre::OperationType opType = Ogre::OT_TRIANGLE_LIST;
if (vertices_ != nullptr)
{
OGRE_FREE_SIMD(vertices_, Ogre::MEMCATEGORY_GEOMETRY);
vertices_ = nullptr;
}
if (indices_ != nullptr)
{
OGRE_FREE_SIMD(indices_, Ogre::MEMCATEGORY_GEOMETRY);
indices_ = nullptr;
}
if (vao_ != nullptr)
{
vaoManager->destroyVertexArrayObject(vao_);
vao_ = nullptr;
}
if (vertexBuffer_ != nullptr)
{
vaoManager->destroyVertexBuffer(vertexBuffer_);
vertexBuffer_ = nullptr;
}
if (indexBuffer_ != nullptr)
{
vaoManager->destroyIndexBuffer(indexBuffer_);
indexBuffer_ = nullptr;
}
//Vertex declaration
Ogre::VertexElement2Vec vertexElements;
vertexElements.push_back(Ogre::VertexElement2(Ogre::VET_FLOAT3, Ogre::VES_POSITION));
vertexElements.push_back(Ogre::VertexElement2(Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES));
std::vector<SimpleVertex> verticesVector;
float largestWidth = 0;
float left = UV_MIN;
float top = UV_MAX * normalizedFontSize_;
Ogre::Real spaceWidth = spaceWidth_;
// Derive space width from a capital A
if (spaceWidth <= 0) spaceWidth = ogreFont_->getGlyphAspectRatio('A')*normalizedFontSize_*1.0f;
// for calculation of AABB
Ogre::Vector3 min = Ogre::Vector3(0,0,0);
Ogre::Vector3 max = Ogre::Vector3(1,1,1);
Ogre::Vector3 currPos;
float maxSquaredRadius = 0.0f;
bool first = true;
// Use iterator
Ogre::String::iterator i, iend;
iend = caption_.end();
bool newLine = true;
Ogre::Real len = 0.0f;
Ogre::Real verticalOffset = 0;
switch (verticalAlignment_)
{
case VerticalAlignment::ABOVE:
verticalOffset = -(1.0f/5.0f) * normalizedFontSize_;
break;
case VerticalAlignment::CENTER:
verticalOffset = -(1.0f/2.0f) * normalizedFontSize_;
break;
case VerticalAlignment::BELOW:
verticalOffset = -(4.0f/5.0f) * normalizedFontSize_;
break;
}
// Raise the first line of the caption
top += verticalOffset;
for (i = caption_.begin(); i != iend; ++i)
{
if (*i == '\n') top += verticalOffset*UV_RANGE;
}
for (i = caption_.begin(); i != iend; ++i)
{
if (newLine)
{
len = 0.0f;
for (Ogre::String::iterator j = i; j != iend && *j != '\n'; j++)
{
if (*j == ' ') len += spaceWidth;
else len += ogreFont_->getGlyphAspectRatio(static_cast<unsigned char>(*j))*normalizedFontSize_*UV_RANGE;
}
newLine = false;
}
if (*i == '\n')
{
left = UV_MIN;
top -= normalizedFontSize_*UV_RANGE;
newLine = true;
continue;
}
if (*i == ' ')
{
// Just leave a gap, no tris
left += spaceWidth;
continue;
}
Ogre::Real horiz_height = ogreFont_->getGlyphAspectRatio(static_cast<unsigned char>(*i));
Ogre::Real u1, u2, v1, v2;
Ogre::Font::UVRect utmp;
utmp = ogreFont_->getGlyphTexCoords(static_cast<unsigned char>(*i));
u1 = utmp.left;
u2 = utmp.right;
v1 = utmp.top;
v2 = utmp.bottom;
// each vert is (x, y, z, u, v)
//-------------------------------------------------------------------------------------
// First tri
//
// Upper left
float shiftedLeft = 0;
if(horizontalAlignment_ == HorizontalAlignment::LEFT) shiftedLeft = left;
else if(horizontalAlignment_ == HorizontalAlignment::RIGHT) shiftedLeft = left - len;
else shiftedLeft = left - len/2;
verticesVector.push_back(SimpleVertex(shiftedLeft, top, 0, u1, v1));
// Deal with bounds
if(horizontalAlignment_ == HorizontalAlignment::LEFT) currPos = Ogre::Vector3(left, top, UV_MIN);
else if(horizontalAlignment_ == HorizontalAlignment::RIGHT) currPos = Ogre::Vector3(left - len, top, UV_MIN);
else currPos = Ogre::Vector3(left - len/2, top, UV_MIN);
if (first)
{
min = max = currPos;
maxSquaredRadius = currPos.squaredLength();
first = false;
}
else
{
min.makeFloor(currPos);
max.makeCeil(currPos);
maxSquaredRadius = std::max(maxSquaredRadius, currPos.squaredLength());
}
top -= normalizedFontSize_*UV_RANGE;
// Bottom left
shiftedLeft = 0;
if(horizontalAlignment_ == HorizontalAlignment::LEFT) shiftedLeft = left;
else if(horizontalAlignment_ == HorizontalAlignment::RIGHT) shiftedLeft = left - len;
else shiftedLeft = left - (len / 2);
verticesVector.push_back(SimpleVertex(shiftedLeft, top, 0, u1, v2));
// Deal with bounds
if(horizontalAlignment_ == HorizontalAlignment::LEFT) currPos = Ogre::Vector3(left, top, UV_MIN);
else if(horizontalAlignment_ == HorizontalAlignment::RIGHT) currPos = Ogre::Vector3(left - len, top, UV_MIN);
else currPos = Ogre::Vector3(left - (len/2), top, UV_MIN);
min.makeFloor(currPos);
max.makeCeil(currPos);
maxSquaredRadius = std::max(maxSquaredRadius, currPos.squaredLength());
top += normalizedFontSize_*UV_RANGE;
left += horiz_height * normalizedFontSize_*UV_RANGE;
// Top right
shiftedLeft = 0;
if(horizontalAlignment_ == HorizontalAlignment::LEFT) shiftedLeft = left;
else if(horizontalAlignment_ == HorizontalAlignment::RIGHT) shiftedLeft = left - len;
else shiftedLeft = left - (len / 2);
verticesVector.push_back(SimpleVertex(shiftedLeft, top, 0, u2, v1));
//-------------------------------------------------------------------------------------
// Deal with bounds
if(horizontalAlignment_ == HorizontalAlignment::LEFT) currPos = Ogre::Vector3(left, top, UV_MIN);
else if(horizontalAlignment_ == HorizontalAlignment::RIGHT) currPos = Ogre::Vector3(left - len, top, UV_MIN);
else currPos = Ogre::Vector3(left - (len/2), top, UV_MIN);
min.makeFloor(currPos);
max.makeCeil(currPos);
maxSquaredRadius = std::max(maxSquaredRadius, currPos.squaredLength());
//-------------------------------------------------------------------------------------
// Second tri
//
// Top right (again)
shiftedLeft = 0;
if(horizontalAlignment_ == HorizontalAlignment::LEFT) shiftedLeft = left;
else if(horizontalAlignment_ == HorizontalAlignment::RIGHT) shiftedLeft = left - len;
else shiftedLeft = left - (len / 2);
verticesVector.push_back(SimpleVertex(shiftedLeft, top, 0, u2, v1));
currPos = Ogre::Vector3(left, top, UV_MIN);
min.makeFloor(currPos);
max.makeCeil(currPos);
maxSquaredRadius = std::max(maxSquaredRadius, currPos.squaredLength());
top -= normalizedFontSize_*UV_RANGE;
left -= horiz_height * normalizedFontSize_*UV_RANGE;
shiftedLeft = 0;
if(horizontalAlignment_ == HorizontalAlignment::LEFT) shiftedLeft = left;
else if(horizontalAlignment_ == HorizontalAlignment::RIGHT) shiftedLeft = left - len;
else shiftedLeft = left - (len / 2);
verticesVector.push_back(SimpleVertex(shiftedLeft, top, 0, u1, v2));
currPos = Ogre::Vector3(left, top, UV_MIN);
min.makeFloor(currPos);
max.makeCeil(currPos);
maxSquaredRadius = std::max(maxSquaredRadius, currPos.squaredLength());
left += horiz_height * normalizedFontSize_*UV_RANGE;
// Bottom right
shiftedLeft = 0;
if(horizontalAlignment_ == HorizontalAlignment::LEFT) shiftedLeft = left;
else if(horizontalAlignment_ == HorizontalAlignment::RIGHT) shiftedLeft = left - len;
else shiftedLeft = left - (len / 2);
verticesVector.push_back(SimpleVertex(shiftedLeft, top, 0, u2, v2));
//-------------------------------------------------------------------------------------
currPos = Ogre::Vector3(left, top, UV_MIN);
min.makeFloor(currPos);
max.makeCeil(currPos);
maxSquaredRadius = std::max(maxSquaredRadius, currPos.squaredLength());
// Go back up with top
top += normalizedFontSize_*UV_RANGE;
float currentWidth = (left + UV_MAX)/UV_RANGE;
if (currentWidth > largestWidth) largestWidth = currentWidth;
}
if (verticesVector.size() == 0) return;
size_t vertexCount = static_cast<unsigned int>(verticesVector.size());
vertices_ = reinterpret_cast<SimpleVertex*>(OGRE_MALLOC_SIMD(sizeof(SimpleVertex)*vertexCount, Ogre::MEMCATEGORY_GEOMETRY));
std::copy(verticesVector.begin(), verticesVector.end(), vertices_);
// Create the index and vertex buffers
try
{
//Create the actual vertex buffer.
vertexBuffer_ = vaoManager->createVertexBuffer(vertexElements,
vertexCount,
Ogre::BT_IMMUTABLE,
vertices_,
true);
}
catch(Ogre::Exception &e)
{
OGRE_FREE_SIMD(vertexBuffer_, Ogre::MEMCATEGORY_GEOMETRY);
vertexBuffer_ = nullptr;
throw e;
}
//Now the Vao. We'll just use one vertex buffer source (multi-source not working yet)
Ogre::VertexBufferPackedVec vertexBuffers;
vertexBuffers.push_back(vertexBuffer_);
Ogre::uint16 indexCount = vertexCount;
indices_ = reinterpret_cast<Ogre::uint16*>(OGRE_MALLOC_SIMD(sizeof(Ogre::uint16)*indexCount, Ogre::MEMCATEGORY_GEOMETRY));
for (size_t i = 0; i < indexCount; i++)
{
*indices_++ = i*3 + 0;
*indices_++ = i*3 + 1;
*indices_++ = i*3 + 2;
}
try
{
indexBuffer_ = vaoManager->createIndexBuffer(Ogre::IndexBufferPacked::IT_16BIT,
indexCount,
Ogre::BT_IMMUTABLE,
indices_,
true);
}
catch( Ogre::Exception &e )
{
// When keepAsShadow = true, the memory will be freed when the index buffer is destroyed.
// However if for some weird reason there is an exception raised, the memory will
// not be freed, so it is up to us to do so.
// The reasons for exceptions are very rare. But we're doing this for correctness.
OGRE_FREE_SIMD(indexBuffer_, Ogre::MEMCATEGORY_GEOMETRY);
indexBuffer_ = nullptr;
throw e;
}
vao_ = vaoManager->createVertexArrayObject(vertexBuffers, indexBuffer_, opType);
// update AABB/Sphere radius
Ogre::Aabb aabb = Ogre::Aabb::newFromExtents(min, max);
float radius = Ogre::Math::Sqrt(maxSquaredRadius);
mObjectData.mLocalAabb->setFromAabb(aabb, mObjectData.mIndex);
mObjectData.mWorldAabb->setFromAabb(aabb, mObjectData.mIndex);
mObjectData.mLocalRadius[mObjectData.mIndex] = radius;
mObjectData.mWorldRadius[mObjectData.mIndex] = radius;
//Each Vao pushed to the vector refers to an LOD level.
//Must be in sync with mesh->mLodValues & mesh->mNumLods if you use more than one level
mVaoPerLod[Ogre::VpNormal].push_back(vao_);
//Use the same geometry for shadow casting.
mVaoPerLod[Ogre::VpShadow].push_back(vao_);
if (updateColors_) this->updateColors();
needUpdate_ = false;
this->setDatablock(ogreHlmsDatablock_);
}
EDIT:
I'm dumb, the renderqueueId was being sent in the MovableObject constructor using a default value. I'm pretty sure the problem still remains but at least that answers my question of how the default renderqueue is set.
EDIT2:
Further investigation seems to show that removing "mRenderables.push_back(this)" prevents the segfault.
So is it a terrible idea to use a single class that inherits borth renderable and movable object?
I tried creating two separate objects like Item and SubItem but it still segfaults... It's probably something really simple (just me being bad at C++) but for now I'm a bit stuck.