Ogre::Item modify mesh Topic is solved

Discussion area about developing with Ogre-Next (2.1, 2.2 and beyond)


Lax
Gnoll
Posts: 675
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 64

Ogre::Item modify mesh

Post by Lax »

Hi @dark_sylinc,

Is it possible to modify vertices of an Ogre::Item Mesh?

I orientated on your DynamicGeometryGameState example. But there a new manual is created. But I have an existing Item (e.g. a sphere) and want to apply a brush, like on terra to transform the positions of the existing vertices. I have not found, how to get the mDynamicVertexBuffer, to work with.

Or must somehow first the Ogre::Item be transformed to a manual?

To you have any example?

Any help is appreciated!

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

jwwalker
Goblin
Posts: 269
Joined: Thu Aug 12, 2021 10:06 pm
Location: San Diego, CA, USA
x 19

Re: Ogre::Item modify mesh

Post by jwwalker »

I don't know as much as dark_sylinc, but I know that a Mesh contains one or more instances of SubMesh, and a SubMesh contains a VertexArrayObject. A VertexArrayObject is immutable, so I think you would need to at least create a new VertexArrayObject. Or, I suppose, you could use SubMesh::importFromV1.

Lax
Gnoll
Posts: 675
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 64

Re: Ogre::Item modify mesh

Post by Lax »

But I need the vertexBuffer, for manipulation, or maybe even add vertices to it. But I would be already satisfied, if I could manipulate the vertexBuffer.

I wonder, how that works with level of detail feature of meshes. These must also have manipulation of the vertexbuffer and even the indexbuffer, as if the level of detail is lower, then less of vertices and indices are used for rendering.

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5482
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1361

Re: Ogre::Item modify mesh

Post by dark_sylinc »

Hi!

I'm going to assume it doesn't have anything slightly esoteric like Skeleton or Pose animations because that complicates things a lot.

I will also assume you don't have live Items when modifying the Mesh (likely it's possible to modify if while there's still live Items, but that depends on the extents of your modifications).

Meshes are created with BT_IMMUTABLE flag by default, and thus you can't change it easily (only solution in that case is to recreate the buffers and VAOs).

But you can force the Mesh to load with BT_DEFAULT via Mesh::setVertexBufferPolicy and/or Mesh::setIndexBufferPolicy.

You can modify existing vertex/index data by range with BufferPacked::upload().
You cannot shrink or grow an existing vertex/index buffer.

However you can "shrink" it by calling subMesh->mVao[vpPass][lod]->setPrimitiveRange( primStart, primCount ). If it was already shrunk, you can enlarge it up to the buffer's original size. This won't save you VRAM, but it will render correctly and GPU will only process the vertex range you ask for (except when running on old Mobile HW).

If this is not enough, you will have to resort to recreating the vertex/index buffer, then the VertexArrayObject, and update the vao pointers in subMesh->mVao[vpPass][lod].

Last but not least, if the mesh was optimized (e.g. QTangents, UVs are half16, etc.) then you'll need to handle that before uploading the data.

If you want to change the vertex format, you will have to recreate the vertex buffer and VAO.

Lax
Gnoll
Posts: 675
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 64

Re: Ogre::Item modify mesh

Post by Lax »

Hi,

thanks for the explanation. Yes, just simple mesh, the most complex will be later a sphere. I would like to create some rudimentary hils on a sphere.

I will keep the policy thing in mind!
For now, thought, I create a more basic scenario: Having a case.mesh (cube), with texture etc. as Ogre::Item (originalItem).
From that, I read all the vertex data via getDetailedMeshInformation2 function.

Then I create a manual and the dynamicVertexBuffer for manipulation.
But after that step, the create manual is crap (just 2 black triangles). Here is the to read out information and set datablock and replace the original item with the manual:

Code: Select all

		MathHelper::getInstance()->getDetailedMeshInformation2(originalItem->getMesh(), this->originalVertexCount, this->originalVertices, this->originalNormals, this->originalTextureCoordinates,
			this->originalIndexCount, this->originalIndices, originalItem->getParentNode()->_getDerivedPositionUpdated(),
			originalItem->getParentNode()->_getDerivedOrientationUpdated(),
			originalItem->getParentNode()->getScale(), isVET_HALF4, isIndices32);

	this->createDynamicMesh(isVET_HALF4, isIndices32);

	// Just one subitem is allowed for now, getNumSubItems will be just 1
	Ogre::Item* itemToModify = this->gameObjectPtr->getSceneManager()->createItem(this->meshToModify, this->gameObjectPtr->isDynamic() ? Ogre::SCENE_DYNAMIC : Ogre::SCENE_STATIC);
	for (size_t i = 0; i < originalItem->getNumSubItems(); i++)
	{
		auto sourceDataBlock = dynamic_cast<Ogre::HlmsPbsDatablock*>(originalItem->getSubItem(i)->getDatablock());
		if (nullptr != sourceDataBlock)
		{
			itemToModify->getSubItem(i)->setDatablock(sourceDataBlock);

			// sourceDataBlock->mShadowConstantBias = 0.0001f;
			// Deactivate fresnel by default, because it looks ugly
			if (sourceDataBlock->getWorkflow() != Ogre::HlmsPbsDatablock::SpecularAsFresnelWorkflow && sourceDataBlock->getWorkflow() != Ogre::HlmsPbsDatablock::MetallicWorkflow)
			{
				sourceDataBlock->setFresnel(Ogre::Vector3(0.01f, 0.01f, 0.01f), false);
			}
		}
	}

	this->gameObjectPtr->getSceneNode()->createChildSceneNode(Ogre::SCENE_DYNAMIC);
	this->gameObjectPtr->getSceneNode()->attachObject(itemToModify);
	this->gameObjectPtr->init(itemToModify);

Here the createDynamicMeshFunction:

Code: Select all

void MeshModifyComponent::createDynamicMesh(bool isVET_HALF4, bool isIndices32)
{
	Ogre::Item* originalItem = this->gameObjectPtr->getMovableObject<Ogre::Item>();

// Transform back from global to local, both: transform and normals
// Get the world transformation matrix of the entity
Ogre::Matrix4 worldMatrix = originalItem->getParentNode()->_getFullTransform();

// Inverse the world matrix to get object space transformation
Ogre::Matrix4 invWorldMatrix = worldMatrix.inverse();

// Get the rotation part of the inverse world matrix for normals
Ogre::Matrix3 invWorldRotation;
invWorldMatrix.extract3x3Matrix(invWorldRotation);

// Transform vertices and normals from world space to object space - do this only ONCE
for (size_t i = 0; i < this->originalVertexCount; ++i)
{
	// Transform each vertex by applying the inverse of the world matrix
	this->originalVertices[i] = invWorldMatrix * this->originalVertices[i];
	this->originalNormals[i] = invWorldRotation * this->originalNormals[i];
	// Normalize the normal after transformation
	this->originalNormals[i].normalise();
}

Ogre::RenderSystem* renderSystem = Ogre::Root::getSingletonPtr()->getRenderSystem();
Ogre::VaoManager* vaoManager = renderSystem->getVaoManager();

// Create manual mesh
this->meshToModify = Ogre::MeshManager::getSingletonPtr()->createManual(this->gameObjectPtr->getName() + "_dynamic", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);

Ogre::SubMesh* subMesh = this->meshToModify->createSubMesh();

// Define vertex elements - use correct formats
Ogre::VertexElement2Vec vertexElements;

if (false == isVET_HALF4)
{
	vertexElements.push_back(Ogre::VertexElement2(Ogre::VET_FLOAT3, Ogre::VES_POSITION));
	vertexElements.push_back(Ogre::VertexElement2(Ogre::VET_FLOAT3, Ogre::VES_NORMAL));
}
else
{
	vertexElements.push_back(Ogre::VertexElement2(Ogre::VET_FLOAT4, Ogre::VES_POSITION));
	vertexElements.push_back(Ogre::VertexElement2(Ogre::VET_FLOAT4, Ogre::VES_NORMAL));
}

vertexElements.push_back(Ogre::VertexElement2(Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES));

// Create interleaved vertex data
size_t vertexSize = 0;
if (isVET_HALF4)
{
	vertexSize = sizeof(float) * 4 + sizeof(float) * 4 + sizeof(float) * 2; // pos(4) + normal(4) + texcoord(2)
}
else
{
	vertexSize = sizeof(float) * 3 + sizeof(float) * 3 + sizeof(float) * 2; // pos(3) + normal(3) + texcoord(2)
}

// Allocate memory for interleaved data - using byte count, not element count
uint8_t* interleavedData = (uint8_t*)OGRE_MALLOC_SIMD(vertexSize * this->originalVertexCount, Ogre::MEMCATEGORY_GEOMETRY);
float* pData = (float*)interleavedData;

// Fill the interleaved data
for (size_t i = 0; i < this->originalVertexCount; ++i)
{
	// Position
	*pData++ = this->originalVertices[i].x;
	*pData++ = this->originalVertices[i].y;
	*pData++ = this->originalVertices[i].z;
	if (isVET_HALF4)
	{
		*pData++ = 1.0f; // w component for VET_FLOAT4
	}

	// Normal
	*pData++ = this->originalNormals[i].x;
	*pData++ = this->originalNormals[i].y;
	*pData++ = this->originalNormals[i].z;
	if (isVET_HALF4)
	{
		*pData++ = 0.0f; // w component for VET_FLOAT4
	}

	// Texture coordinates - no need for a separate count variable
	if (this->originalTextureCoordinates)
	{
		*pData++ = this->originalTextureCoordinates[i].x;
		*pData++ = this->originalTextureCoordinates[i].y;
	}
	else
	{
		// Fallback if texture coordinates aren't available
		*pData++ = 0.0f;
		*pData++ = 0.0f;
	}
}

try
{
	// Create vertex buffer with interleaved data
	this->dynamicVertexBuffer = vaoManager->createVertexBuffer(vertexElements, this->originalVertexCount, Ogre::BT_DYNAMIC_PERSISTENT, interleavedData, false);
}
catch (Ogre::Exception& e)
{
	OGRE_FREE_SIMD(interleavedData, Ogre::MEMCATEGORY_GEOMETRY);
	return;
}

Ogre::IndexBufferPacked::IndexType indexType = Ogre::IndexBufferPacked::IT_32BIT;

if (false == isIndices32)
{
	indexType = Ogre::IndexBufferPacked::IT_16BIT;
}

// Create index buffer with original indices
try
{
	this->dynamicIndexBuffer = vaoManager->createIndexBuffer(indexType, this->originalIndexCount, Ogre::BT_DYNAMIC_PERSISTENT, this->originalIndices, false);
}
catch (Ogre::Exception& e)
{
	OGRE_FREE_SIMD(this->originalIndices, Ogre::MEMCATEGORY_GEOMETRY);
	return;
}

// Create VAO
Ogre::VertexBufferPackedVec vertexBuffers;
vertexBuffers.push_back(this->dynamicVertexBuffer);
Ogre::VertexArrayObject* vao = vaoManager->createVertexArrayObject(vertexBuffers, this->dynamicIndexBuffer, Ogre::OT_TRIANGLE_LIST);

subMesh->mVao[Ogre::VpNormal].push_back(vao);
subMesh->mVao[Ogre::VpShadow].push_back(vao);

// Set bounds (same as originalItem)
Ogre::Aabb originalBounds = originalItem->getMesh()->getAabb();
this->meshToModify->_setBounds(originalBounds, false);
this->meshToModify->_setBoundingSphereRadius(originalItem->getMesh()->getBoundingSphereRadius());
}

The values get from the mesh information function, are correct. I debugged it, but something seems to be totally wrong. I also make global space vertex values to local space.

What is missing here, or is the order wrong?

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5482
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1361

Re: Ogre::Item modify mesh

Post by dark_sylinc »

The mesh is already in local space. You should not have to apply the inverse world transform.

The vertex shader reads the mesh in local space and applies the world transform to turn it into world space. Hence the mesh is always in local space.

Lax
Gnoll
Posts: 675
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 64

Re: Ogre::Item modify mesh

Post by Lax »

Hi @dark_sylinc,

Its harder to get right, as I thought. I try to get like in the Ogre-Next example a dynamic vertex buffer, to work with. So I use the default policy for a cloned mesh:

Code: Select all

Ogre::Item* originalItem = this->gameObjectPtr->getMovableObject<Ogre::Item>();
if (nullptr != originalItem)
{
	Ogre::MeshPtr originalMesh = originalItem->getMesh();
	// Clone the mesh with a new name
	Ogre::MeshPtr clonedMesh = originalMesh->clone(this->gameObjectPtr->getName() + "_Editable");

// Set buffer policies for modification
clonedMesh->setVertexBufferPolicy(Ogre::BT_DEFAULT, false);
clonedMesh->setIndexBufferPolicy(Ogre::BT_DEFAULT, false);


// Recalculate bounds and notify
clonedMesh->load();
clonedMesh->_dirtyState();


for (Ogre::SubMesh* subMesh : clonedMesh->getSubMeshes())
{
	// Normal means here: casual pass, other option is with shadow caster. So it has nothing todo with vertex normals!
	Ogre::VertexArrayObjectArray vaos = subMesh->mVao[Ogre::VpNormal];

	if (false == vaos.empty())
	{
		Ogre::VertexArrayObject* vao = vaos[0];

		this->dynamicVertexBuffer = vao->getBaseVertexBuffer();

		this->dynamicIndexBuffer = vao->getIndexBuffer();
	}
}
}

I debugged the code, and as Soon as the dynamicVertexBuffer is assigned, its still immutable. How can this be fixed?

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5482
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1361

Re: Ogre::Item modify mesh

Post by dark_sylinc »

Hi

clone() overrides the default buffer policies, and you're not providing the 2nd and 3rd optionak arguments.
See its documentation

Lax
Gnoll
Posts: 675
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 64

Re: Ogre::Item modify mesh

Post by Lax »

Hi,

Ah I had no idea. Ok thanks, now At least I get no exception anymore, I now clone with Ogre::BT_DEFAULT.

So I try to modify the mesh vertices and having a brush. Debugging the vertexBuffer I see, that the vertices has just been modified slightly, which is what I want for testing. But the result is horrible. The mesh is crap after a just a small modification. What am I missing here:

Code: Select all

void MeshModifyComponent::modifyMeshWithBrush(const Ogre::Vector3& brushPosition)
{
	std::vector<Ogre::Vector3> newVertices(this->originalVertices, this->originalVertices + this->originalVertexCount);

// Modify vertices using the brush
for (size_t i = 0; i < newVertices.size(); ++i)
{
	Ogre::Vector3& vertex = newVertices[i];

	// Compute brush influence
	Ogre::Real brushIntensity = this->getBrushValue(i);
	Ogre::Real distance = vertex.distance(brushPosition);
	if (distance <= this->brushSize->getInt())
	{
		Ogre::Real intensity = (1.0f - (distance / this->brushSize->getInt())) * brushIntensity;
		vertex += (vertex - brushPosition).normalisedCopy() * intensity * 0.1f;
	}
}

// Upload modified vertices to the dynamic vertex buffer
this->dynamicVertexBuffer->upload(newVertices.data(), 0, newVertices.size());
}

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5482
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1361

Re: Ogre::Item modify mesh

Post by dark_sylinc »

A VertexBuffer contains position, normals, UVs. It seems you're assuming the mesh contains only position.

And you're assuming they're all VET_FLOAT (we often recommend to use "puqs" in OgreMeshTool to optimize the vertex format, which will use 16-bit half floats and similar stuff).

You would either need to handle the conversions, or just ensure your meshes are always unoptimized (-U in OgreMeshTool).

If you want to keep it optimized, VertexBufferDownloadHelper can help you download data from meshes and convert it to float; but we don't have a similar utility for uploading.

Lax
Gnoll
Posts: 675
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 64

Re: Ogre::Item modify mesh

Post by Lax »

Ok, So I read out the mesh now for vertices, normals and texture coordinates.
I test it with a simple cube.

Code: Select all


Ogre::MeshPtr originalMesh = originalItem->getMesh();
// Clone the mesh with a new name
Ogre::MeshPtr clonedMesh = originalMesh->clone(this->gameObjectPtr->getName() + "_Editable", "", Ogre::BT_DEFAULT, Ogre::BT_DEFAULT);

// Set buffer policies for modification
clonedMesh->setVertexBufferPolicy(Ogre::BT_DEFAULT, false);
clonedMesh->setIndexBufferPolicy(Ogre::BT_DEFAULT, false);


// Recalculate bounds and notify
clonedMesh->load();
clonedMesh->_dirtyState();

for (Ogre::SubMesh* subMesh : clonedMesh->getSubMeshes())
{
	// Normal means here: casual pass, other option is with shadow caster. So it has nothing todo with vertex normals!
	Ogre::VertexArrayObjectArray vaos = subMesh->mVao[Ogre::VpNormal];

if (false == vaos.empty())
{
	Ogre::VertexArrayObject* vao = vaos[0];

	this->dynamicVertexBuffer = vao->getBaseVertexBuffer();

	this->dynamicIndexBuffer = vao->getIndexBuffer();
}
}

void MeshModifyComponent::modifyMeshWithBrush(const Ogre::Vector3& brushPosition)
{
	std::vector<float> interleavedData;
	interleavedData.reserve(originalVertexCount * 8); // 8 floats per vertex (3 pos, 3 normal, 2 texcoords)

std::vector<Ogre::Vector3> modifiedVertices(this->originalVertices, this->originalVertices + this->originalVertexCount);
std::vector<Ogre::Vector3> newNormals(originalVertexCount, Ogre::Vector3::ZERO);

// Modify vertices and reset normals in one loop
for (size_t i = 0; i < originalVertexCount; ++i)
{
	Ogre::Vector3& vertex = modifiedVertices[i];

	// Compute brush influence
	Ogre::Real brushIntensity = this->getBrushValue(i);
	Ogre::Real distance = vertex.distance(brushPosition);
	if (distance <= this->brushSize->getInt())
	{
		Ogre::Real intensity = (1.0f - (distance / this->brushSize->getInt())) * brushIntensity;
		vertex += (vertex - brushPosition).normalisedCopy() * intensity * 0.1f;
	}

	// Reset normals (to prepare for accumulation)
	newNormals[i] = Ogre::Vector3::ZERO;
}

// Compute face normals and accumulate
for (size_t i = 0; i < originalIndexCount; i += 3)
{
	size_t i0 = originalIndices[i];
	size_t i1 = originalIndices[i + 1];
	size_t i2 = originalIndices[i + 2];

	Ogre::Vector3& v0 = modifiedVertices[i0];
	Ogre::Vector3& v1 = modifiedVertices[i1];
	Ogre::Vector3& v2 = modifiedVertices[i2];

	Ogre::Vector3 edge1 = v1 - v0;
	Ogre::Vector3 edge2 = v2 - v0;
	Ogre::Vector3 normal = edge1.crossProduct(edge2);
	normal.normalise();

	// Accumulate normals
	newNormals[i0] += normal;
	newNormals[i1] += normal;
	newNormals[i2] += normal;
}

// Normalize accumulated normals and interleave data in a single loop
for (size_t i = 0; i < originalVertexCount; ++i)
{
	newNormals[i].normalise();

	// Add position (3 floats)
	interleavedData.push_back(modifiedVertices[i].x);
	interleavedData.push_back(modifiedVertices[i].y);
	interleavedData.push_back(modifiedVertices[i].z);

	// Add recalculated normal (3 floats)
	interleavedData.push_back(newNormals[i].x);
	interleavedData.push_back(newNormals[i].y);
	interleavedData.push_back(newNormals[i].z);

	// Add texture coordinates (2 floats)
	interleavedData.push_back(this->originalTextureCoordinates[i].x);
	interleavedData.push_back(this->originalTextureCoordinates[i].y);
}


// Upload modified vertices to the dynamic vertex buffer
this->dynamicVertexBuffer->upload(interleavedData.data(), 0, interleavedData.size());
}

But if I want to upload it, I get an exception, because of out of bounds. 192 points do exceed 24 points. So how is that possible? The cube seems just to have 24 vertices and nothing more.

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5482
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1361

Re: Ogre::Item modify mesh

Post by dark_sylinc »

upload() wants number of vertices. You're sending number of floats.

Lax
Gnoll
Posts: 675
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 64

Re: Ogre::Item modify mesh

Post by Lax »

Ok, thanks. I fixed that.

Still the mesh does look like crap. I will investigate further into the topic.
Especially your previous advices with the OgreMeshTool

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62