[2.1] (Solved) Problems with editing a mesh

Problems building or running the engine, queries about how to use features etc.
Post Reply
Dugi
Halfling
Posts: 46
Joined: Tue Jul 23, 2013 2:37 pm
x 7

[2.1] (Solved) Problems with editing a mesh

Post by Dugi »

Hello,

I was trying to code a function to create a copy of a mesh with inverted normals orientation of faces that would be used if the mesh is used somewhere with a negative scale.

I got stuck very early, because it segfaults when cloning the mesh, accessing faulty address 0x20, so it's probably accessing a member variable of a nullptr struct/class. The mesh isn't a nullptr (checked), it's loaded (checked) and it can be replicated easily in a minimal case scenario.

What is enough to cause the segfault:
In Tutorial02 (Variable Framerate), I add this after the line when the item Cube_d.mesh is created:

Code: Select all

Ogre::Mesh* meshOrig = static_cast<Ogre::Mesh*>(Ogre::MeshManager::getSingleton().getResourceByName("Cube_d.mesh").getPointer());
Ogre::Mesh* cloned = meshOrig->clone("Cube_d2.mesh").getPointer();
This is the backtrace:

Code: Select all

Thread 1 "Sample_Tutorial" received signal SIGSEGV, Segmentation fault.
0x00007ffff70ccda4 in memcpy (
    __len=<error reading variable: Cannot access memory at address 0x20>, 
    __src=0x7fffd80dc000, __dest=0x7fffd84dc540)
    at /usr/include/x86_64-linux-gnu/bits/string3.h:53
53	  return __builtin___memcpy_chk (__dest, __src, __len, __bos0 (__dest));
#0  0x00007ffff70ccda4 in memcpy (
    __len=<error reading variable: Cannot access memory at address 0x20>, 
    __src=0x7fffd80dc000, __dest=0x7fffd84dc540)
    at /usr/include/x86_64-linux-gnu/bits/string3.h:53
#1  Ogre::VertexArrayObject::clone (this=0x7fffdbe188c8, 
    vaoManager=0x7fffe017f358, 
    sharedBuffers=sharedBuffers@entry=0x7fffffffd980)
    at blablabla/ogre-2.1/OgreMain/src/Vao/OgreVertexArrayObject.cpp:224
#2  0x00007ffff6e12fbd in Ogre::SubMesh::clone (this=<optimised out>, 
    parentMesh=0x7fffe01f9340)
    at blablabla/ogre-2.1/OgreMain/src/OgreSubMesh2.cpp:340
#3  0x00007ffff6e2668c in Ogre::Mesh::clone (this=this@entry=0x7fffe01f31b0, 
    newName="Cube_d2.mesh", newGroup="")
    at blablabla/ogre-2.1/OgreMain/src/OgreMesh2.cpp:218
#4  0x000000000040a9c9 in Demo::MyGameState::createScene01 (
    this=0x7fffffffdb40)
    at blablabla/ogre-2.1/Samples/2.0/Tutorials/Tutorial02_VariableFramerate/MyGameState.cpp:37
#5  0x000000000040b110 in mainApp ()
    at blablabla/ogre-2.1/Samples/2.0/Tutorials/Tutorial02_VariableFramerate/Tutorial02_VariableFramerate.cpp:49
#6  0x000000000040a37b in main ()
Oh, and I am using Ubuntu 16.04, an nVidia video card and ogre 2.1 (a month old or so).

Just for completeness, this is the whole code I am trying to get working is here (I could not check anything after the clone, so it's kinda useless).
Last edited by Dugi on Fri Sep 02, 2016 10:04 pm, edited 2 times in total.
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5296
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1278
Contact:

Re: [2.1] (Bug?) Segfault when cloning a mesh

Post by dark_sylinc »

getResourceByName will get you the mesh but doesn't ensure it's loaded. Hopefully that's what's happening.

I would grab the mesh like this:

Code: Select all

Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().load( "Cube_d.mesh", ... );
Ogre::MeshPtr cloned = mesh->clone( "Cube_d2.mesh" );
Edit: Nevermind, crashes for me too.
Edit 2: Fixed.
Dugi
Halfling
Posts: 46
Joined: Tue Jul 23, 2013 2:37 pm
x 7

[2.1] Problems with editing a mesh

Post by Dugi »

Thank you. Now I can clone the mesh without problems.

But I encountered another problem afterwards very soon. I can't access the data the VertexBufferPacked has. There are functions to map the data to a CPU accessible memory part, but when I try to use it, it reports that OGRE EXCEPTION(1:InvalidStateException): Only dynamic buffers can be mapped! Use upload instead. However, upload can't be used to access the data, only to replace the data with new data. But I can't get anything from the old data to produce new data. I can upload, but I can't get any source data so that I could upload them. Meshes don't appear to have an option to get dynamic buffers, so mapping seems not to be the way. Textures have methods like blitFromMemory and blitToMemory, but here I can't find the first one, I have an impression that some download() function is missing somewhere.

I can't find an example either, v1 meshes are converted to v2 meshes, but the data is comes from the v1 mesh. Examples with CustomRenderable use data coming from a table. Also, customRenderables are able to use a dynamic mesh, while it appears to be impossible to get a dynamic mesh out of an existing one.

Am I missing something or VertexBufferPacked is missing a method?
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5296
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1278
Contact:

Re: [2.1] Problems with editing a mesh

Post by dark_sylinc »

To download use:

Code: Select all

AsyncTicketPtr asyncTicket = vertexBuffer->readRequest( 0, vertexBuffer->getNumElements() );
const uint8 *vertexData = static_cast<const uint8*>( asyncTicket->map() );
asyncTicket->unmap();
Looking for AsyncTicket or readRequest in our source code should give you plenty of examples if you have doubts.

Note: Although only dynamic buffers can be mapped, you shouldn't read from them either. You'll get garbage. The only valid method to read from GPU is through async tickets.
Dugi
Halfling
Posts: 46
Joined: Tue Jul 23, 2013 2:37 pm
x 7

Re: [2.1] Problems with editing a mesh

Post by Dugi »

Thank you. It works. I have later (after reading your reply) serendipitously found it in VertexArrayObject::clone(), so I was looking for it badly, sorry.

However, I haven't got much further. Attempting to call VertexBufferPacked::upload() throws an exception that Cannot use upload on an immutable buffer! I can't find a way to change the buffer to mutable, so I suppose that it's necessary for an optimalisation or something. It might be possible to replace the whole VAO with a new one, but it would be mostly a copy paste of VertexArrayObject::clone(). The clone() method copies the old object's mutability, which is quite nonsensical, there is no need to clone an immutable object to another immutable object. It makes the meshes' clone() method quite impractical, because it clones a mesh into an identical mesh whose geometry cannot be changed.

Is there some better way to do this or I have to copy the cloning code to make a small change before creating the new VAO?
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5296
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1278
Contact:

Re: [2.1] Problems with editing a mesh

Post by dark_sylinc »

  • Now you can alter the buffer type of the buffer being cloned.
  • You can also control whether the original mesh is loaded as immutable by explicitly loading it yourself:

    Code: Select all

    Ogre::MeshManager::getSingleton().load( "Cube_d.mesh", groupName, vertexBufferType, indexBufferType );
    Note there's also other parameter defaults.
  • You are right that cloning a BT_IMMUTABLE as BT_IMMUTABLE feels pointless; however I cannot foresee the user's intentions. If a user plans to clone the mesh just to get everything copied but then destroy the buffers of the original mesh to overwrite them with whatever he wants (or makes a clone to change skeleton parameters, pose animation parameters, etc); then he will probably want to keep the clone also as BT_IMMUTABLE. It should be pretty rare and esoteric. So allowing to explicitly change the buffer type seemed like the better option instead of assuming the user doesn't want an immutable clone.
Thanks for the feedback.

Cheers.
Dugi
Halfling
Posts: 46
Joined: Tue Jul 23, 2013 2:37 pm
x 7

Re: [2.1] Problems with editing a mesh

Post by Dugi »

Thank you for the change, it helps a lot. Clone to to mutable, change it, clone it back to immutable when done.

However, it appears that there is a little bug. I get an error message that Dynamic buffers can't have a shadow copy! The source seems to indicate that there's no way to remove this shadow copy.

It happens even when I do only this:

Code: Select all

					Ogre::Mesh* orig = Ogre::MeshManager::getSingleton().load(name, "General", Ogre::BT_DYNAMIC_DEFAULT).getPointer();
					Ogre::Mesh* cloned = orig->clone(name + ":flipped", Ogre::BLANKSTRING, Ogre::BT_DYNAMIC_DEFAULT).getPointer();
(the mesh wasn't loaded before)

Is this a bug or I am just failing to understand it?

EDIT: Maybe this topic could be moved to papercuts.
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5296
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1278
Contact:

Re: [2.1] Problems with editing a mesh

Post by dark_sylinc »

Yeah the clone method doesn't handle dynamic buffers well. However from what you're describing you should use default and upload the changes via buffer::upload()
Dynamic buffers are meant to be used for data that change every frame.
Dugi
Halfling
Posts: 46
Joined: Tue Jul 23, 2013 2:37 pm
x 7

Re: [2.1] Problems with editing a mesh

Post by Dugi »

The same thing happens even if I clone an immutable buffer into a dynamic buffer, just anything that clones into a dynamic buffer. However, I didn't realise that cloning into BT_DEFAULT allows using the upload() method but the mesh isn't really dynamic. Now it goes without exceptions.

The changes do seem to have an effect when I grab the data again. I can't see any changes in the game, but that is most likely an error in my code.
EDIT: Yes, I wasn't flipping the right mesh.
EDIT #2: The mesh is changed. At the moment, the change isn't what I wanted to do, but finally I managed to change it.
Dugi
Halfling
Posts: 46
Joined: Tue Jul 23, 2013 2:37 pm
x 7

[2.1] No more problems with editing a mesh

Post by Dugi »

For anyone facing the same issues than me, here is my final code. It flips the orientation of faces of meshes. This is useful if a mesh is to be mirrored, because scaling by -1,1,1 mirrors the geometry but the result is inside out (faces you are looking at are culled, faces at whose backs you are looking are visible).

To flip the faces inside out (creates a new mesh with a different name so that the old one could be used again):

Code: Select all

Ogre::Mesh* meshOrig = static_cast<Ogre::Mesh*>(Ogre::MeshManager::getSingleton().getResourceByName(name).getPointer());
Ogre::Mesh* cloned = meshOrig->clone(name + ":flipped", Ogre::BLANKSTRING, Ogre::BT_DEFAULT, Ogre::BT_DEFAULT).getPointer();
std::set<Ogre::IndexBufferPacked*> buffersEdited;
for (unsigned int i = 0; i < cloned->getNumSubMeshes(); i++) {
	Ogre::SubMesh* sub = cloned->getSubMesh(i);
	for (unsigned int j = 0; j < Ogre::NumVertexPass; j++) for (unsigned int k = 0; k < sub->mVao[j].size(); k++) {
		Ogre::VertexArrayObject* vao = sub->mVao[j][k];
		for (int l = 0; l < cloned->hasIndependentShadowMappingVaos() + 1; l++) {
			Ogre::IndexBufferPacked* buffer = vao->getIndexBuffer();
			if (buffersEdited.find(buffer) != buffersEdited.end()) continue;
			buffersEdited.insert(buffer);
			Ogre::AsyncTicketPtr asyncTicket = buffer->readRequest(0, buffer->getNumElements());
			const uint8_t* vertexData = static_cast<const uint8_t*>(asyncTicket->map());
			void* data = malloc(buffer->getTotalSizeBytes());
			memcpy(data, vertexData, buffer->getTotalSizeBytes());
			asyncTicket->unmap();
			if (buffer->getBytesPerElement() == 2) {
				std::cerr << buffer << " 2 bytes per element\n";
				for (unsigned int i = 0; i < buffer->getTotalSizeBytes(); i += 6) {
					struct Vertices {
								unsigned short int p1;
								unsigned short int p2;
								unsigned short int p3;
					};
					void* start = (void*)((long int)data + i);
					Vertices* verts = reinterpret_cast<Vertices*>(start);
					unsigned short int backup = verts->p2;
					verts->p2 = verts->p3;
					verts->p3 = backup;
				}
			} else { // buffer->getBytesPerElement() == 4
				std::cerr << buffer << " 4 bytes per element\n";
				for (unsigned int i = 0; i < buffer->getTotalSizeBytes(); i += 12) {
					struct Vertices {
						unsigned int p1;
						unsigned int p2;
						unsigned int p3;
					};
					void* start = (void*)((long int)data + i);
					Vertices* verts = reinterpret_cast<Vertices*>(start);
					unsigned int backup = verts->p2;
					verts->p2 = verts->p3;
					verts->p3 = backup;
				}
			}
			buffer->upload(data, 0, buffer->getNumElements());
			free(data);
		}
	}
}
To scale a mesh by -1,1,1 without scaling its parent node (used inside something like the l indexed loop of the previous code, works only on usual pbs meshes because of the simplifying assumptions about vertex buffer data (it verifies it), it's made to be easily changeable to other scaling or editing normals, tangents or texture coordinates):

Code: Select all

for (unsigned int m = 0; m < vao->getVertexBuffers().size(); m++) {
	Ogre::VertexBufferPacked* buffer = vao->getVertexBuffers()[m];
	if (buffersEdited.find(buffer) != buffersEdited.end()) continue;
	buffersEdited.insert(buffer);
	const Ogre::VertexElement2Vec& elems = buffer->getVertexElements();
	if (elems.size() < 4 || elems[0].mSemantic != Ogre::VES_POSITION || elems[1].mSemantic != Ogre::VES_NORMAL
			|| elems[2].mSemantic != Ogre::VES_TEXTURE_COORDINATES || elems[3].mSemantic != Ogre::VES_TANGENT) continue;
	Ogre::AsyncTicketPtr asyncTicket = buffer->readRequest(0, buffer->getNumElements());
	const uint8_t* vertexData = static_cast<const uint8_t*>(asyncTicket->map());
	void* data = malloc(buffer->getTotalSizeBytes());
	memcpy(data, vertexData, buffer->getTotalSizeBytes());
	asyncTicket->unmap();
	for (unsigned int n = 0; n < buffer->getNumElements(); n++) {
		struct Vertices
		{
			float px, py, pz, pw;
			float nx, ny, nz;
			float texx, texy;
			float tx, ty, tz;
		};
		void* start = (void*)((long int)data + buffer->getBytesPerElement() * n);
		Vertices* verts = reinterpret_cast<Vertices*>(start);
		verts->px *= -1; // Flip the tangent
	}
	buffer->upload(data, 0, buffer->getNumElements());
	free(data);
}
Thanks for all the help.
Arkiruthis
Gremlin
Posts: 178
Joined: Fri Dec 24, 2010 7:55 pm
x 10

Re: [2.1] (Solved) Problems with editing a mesh

Post by Arkiruthis »

Dugi: Thanks for this, but sadly with both your example and my own attempts ( here) I simply cannot get past the following assertion failure:

"Cannot use upload on an immutable buffer!" :(

This happens with both an updated v2-1 and v2-1-pso branch.

I have tried:
1. Using the MeshManager to load a mesh with all variations of BT_DEFAULT and BT_DYNAMIC_DEFAULT and BT_DYNAMIC_PERSISTANT to no avail.
2. Cloning using the new clone() version in 2.1 with all variations of BT_DEFAULT and BT_DYNAMIC_DEFAULT and BT_DYNAMIC_PERSISTANT to no avail.

The only difference with your example is that I'm using this at the start instead of your first two lines:

Code: Select all

	Ogre::MeshPtr meshOriginalPtr = Ogre::MeshManager::getSingletonPtr()->load("Cube_d.mesh", Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME);
	Ogre::MeshPtr meshPtr = meshOriginalPtr->clone("test", Ogre::BLANKSTRING, Ogre::BT_DEFAULT, Ogre::BT_DEFAULT);
I know it's probably a facepalm-worthy mistake I'm making. I'm calling this code in CreateScene01() of my test app, but there's no reason this should be a problem?
Arkiruthis
Gremlin
Posts: 178
Joined: Fri Dec 24, 2010 7:55 pm
x 10

Re: [2.1] (Solved) Problems with editing a mesh

Post by Arkiruthis »

Hmm, wierd, it's working now. Changed those lines to have BT_DEFAULT when loading the mesh. (edit - removed BY_DYNAMIC_DEFAULT from clone() as it's ignored internally in OgreMesh2.cpp)

Code: Select all

Ogre::MeshPtr meshOriginalPtr = Ogre::MeshManager::getSingletonPtr()->load("Cube_d.mesh", Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME, Ogre::BT_DEFAULT, Ogre::BT_DEFAULT);
Ogre::MeshPtr meshPtr = meshOriginalPtr->clone("test.mesh", Ogre::BLANKSTRING);
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5296
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1278
Contact:

Re: [2.1] (Solved) Problems with editing a mesh

Post by dark_sylinc »

I took a look and you're right. A recent pull request modified the clone code and inadvertently introduced the bug. It's fixed and working again.
Thanks for the report.
Post Reply