Skip to main content

History: ManualSphereMeshes

Preview of version: 11

You could always generate a sphere in your favorite modeller, but given the nature of some of the Ogre exporters, it's not always so easy.

The following code snippet will make a sphere, making full use of hardware vertex and index buffers for performance.

Original credit for this code goes to mcspy0312 from a post of his on the forums, but it looks like he got it from elsewhere too. His initial code was for a "sky sphere," with the faces pointing inwards. I modified it so that the faces had an outwards orientation and fixed a bug in the texture coordinate code.

C# version is also available in MOGRE ManualSphereMeshes.

If you have a screenshot of a sphere, please upload and add it.

Snippet

This code creates a sphere mesh with the given name:

Copy to clipboard
void createSphere(const std::string& strName, const float r, const int nRings = 16, const int nSegments = 16) { MeshPtr pSphere = MeshManager::getSingleton().createManual(strName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); SubMesh *pSphereVertex = pSphere->createSubMesh(); pSphere->sharedVertexData = new VertexData(); VertexData* vertexData = pSphere->sharedVertexData; // define the vertex format VertexDeclaration* vertexDecl = vertexData->vertexDeclaration; size_t currOffset = 0; // positions vertexDecl->addElement(0, currOffset, VET_FLOAT3, VES_POSITION); currOffset += VertexElement::getTypeSize(VET_FLOAT3); // normals vertexDecl->addElement(0, currOffset, VET_FLOAT3, VES_NORMAL); currOffset += VertexElement::getTypeSize(VET_FLOAT3); // two dimensional texture coordinates vertexDecl->addElement(0, currOffset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0); currOffset += VertexElement::getTypeSize(VET_FLOAT2); // allocate the vertex buffer vertexData->vertexCount = (nRings + 1) * (nSegments+1); HardwareVertexBufferSharedPtr vBuf = HardwareBufferManager::getSingleton().createVertexBuffer(vertexDecl->getVertexSize(0), vertexData->vertexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false); VertexBufferBinding* binding = vertexData->vertexBufferBinding; binding->setBinding(0, vBuf); float* pVertex = static_cast<float*>(vBuf->lock(HardwareBuffer::HBL_DISCARD)); // allocate index buffer pSphereVertex->indexData->indexCount = 6 * nRings * (nSegments + 1); pSphereVertex->indexData->indexBuffer = HardwareBufferManager::getSingleton().createIndexBuffer(HardwareIndexBuffer::IT_16BIT, pSphereVertex->indexData->indexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false); HardwareIndexBufferSharedPtr iBuf = pSphereVertex->indexData->indexBuffer; unsigned short* pIndices = static_cast<unsigned short*>(iBuf->lock(HardwareBuffer::HBL_DISCARD)); float fDeltaRingAngle = (Math::PI / nRings); float fDeltaSegAngle = (2 * Math::PI / nSegments); unsigned short wVerticeIndex = 0 ; // Generate the group of rings for the sphere for( int ring = 0; ring <= nRings; ring++ ) { float r0 = r * sinf (ring * fDeltaRingAngle); float y0 = r * cosf (ring * fDeltaRingAngle); // Generate the group of segments for the current ring for(int seg = 0; seg <= nSegments; seg++) { float x0 = r0 * sinf(seg * fDeltaSegAngle); float z0 = r0 * cosf(seg * fDeltaSegAngle); // Add one vertex to the strip which makes up the sphere *pVertex++ = x0; *pVertex++ = y0; *pVertex++ = z0; Vector3 vNormal = Vector3(x0, y0, z0).normalisedCopy(); *pVertex++ = vNormal.x; *pVertex++ = vNormal.y; *pVertex++ = vNormal.z; *pVertex++ = (float) seg / (float) nSegments; *pVertex++ = (float) ring / (float) nRings; if (ring != nRings) { // each vertex (except the last) has six indices pointing to it *pIndices++ = wVerticeIndex + nSegments + 1; *pIndices++ = wVerticeIndex; *pIndices++ = wVerticeIndex + nSegments; *pIndices++ = wVerticeIndex + nSegments + 1; *pIndices++ = wVerticeIndex + 1; *pIndices++ = wVerticeIndex; wVerticeIndex ++; } }; // end for seg } // end for ring // Unlock vBuf->unlock(); iBuf->unlock(); // Generate face list pSphereVertex->useSharedVertices = true; // the original code was missing this line: pSphere->_setBounds( AxisAlignedBox( Vector3(-r, -r, -r), Vector3(r, r, r) ), false ); pSphere->_setBoundingSphereRadius(r); // this line makes clear the mesh is loaded (avoids memory leaks) pSphere->load(); }

The parameters are

  • strName - The Mesh's name. You'll use this to access it.
  • r - The sphere's radius.
  • nRings - Number of lines of {LEX()}longitude{LEX} in the sphere. Defaults to 16.
  • nSegments - Number of lines of {LEX()}latitude{LEX} in the sphere. Also defaults to 16.

Example

Here's an example of how you would use this function and attach the new mesh to a SceneNode:

Copy to clipboard
createSphere("mySphereMesh", 10, 64, 64); Entity* sphereEntity = sceneMgr->createEntity("mySphereEntity", "mySphereMesh"); SceneNode* sphereNode = sceneMgr->getRootSceneNode()->createChildSceneNode(); sphereEntity->setMaterialName("material_name_goes_here"); sphereNode->attachObject(sphereEntity);

Don't forget to replace "material_name_goes_here" with the name of your {LEX()}material{LEX}.

Creating a sphere with ManualObject

You can also create the {LEX()}mesh{LEX} with the ManualObject class:

Copy to clipboard
void createSphere(const std::string& strName, const float r, const int nRings = 16, const int nSegments = 16) { ManualObject * manual = sceneMgr->createManualObject(strName); manual->begin("BaseWhiteNoLighting", RenderOperation::OT_TRIANGLE_LIST); float fDeltaRingAngle = (Math::PI / nRings); float fDeltaSegAngle = (2 * Math::PI / nSegments); unsigned short wVerticeIndex = 0 ; // Generate the group of rings for the sphere for( int ring = 0; ring <= nRings; ring++ ) { float r0 = r * sinf (ring * fDeltaRingAngle); float y0 = r * cosf (ring * fDeltaRingAngle); // Generate the group of segments for the current ring for(int seg = 0; seg <= nSegments; seg++) { float x0 = r0 * sinf(seg * fDeltaSegAngle); float z0 = r0 * cosf(seg * fDeltaSegAngle); // Add one vertex to the strip which makes up the sphere manual->position( x0, y0, z0); manual->normal(Vector3(x0, y0, z0).normalisedCopy()); manual->textureCoord((float) seg / (float) nSegments, (float) ring / (float) nRings); if (ring != nRings) { // each vertex (except the last) has six indicies pointing to it manual->index(wVerticeIndex + nSegments + 1); manual->index(wVerticeIndex); manual->index(wVerticeIndex + nSegments); manual->index(wVerticeIndex + nSegments + 1); manual->index(wVerticeIndex + 1); manual->index(wVerticeIndex); wVerticeIndex ++; } }; // end for seg } // end for ring manual->end(); MeshPtr mesh = manual->convertToMesh(name); mesh->_setBounds( AxisAlignedBox( Vector3(-r, -r, -r), Vector3(r, r, r) ), false ); mesh->_setBoundingSphereRadius(r); unsigned short src, dest; if (!mesh->suggestTangentVectorBuildParams(VES_TANGENT, src, dest)) { mesh->buildTangentVectors(VES_TANGENT, src, dest); } }

You can use the above example to include it in your code.

ManualObjectSphere
ManualObjectSphere

Screen capture shown above is from ManualObject method lit with point light of ColourValue( 1.0f, 0.80f, 0.0f ), textured with MRAMOR6X6.jpg

History

Information Version
Fri 10 of Jul, 2020 21:44 GMT-0000 sercero 13
Fri 10 of Jul, 2020 18:43 GMT-0000 sercero 12
Fri 10 of Jul, 2020 18:36 GMT-0000 sercero 11
Wed 25 of Jan, 2012 14:32 GMT-0000 CommonSurname added image of ManualObject sphere 10
Mon 28 of Jun, 2010 21:46 GMT-0000 jacmoe 9
Mon 28 of Jun, 2010 21:44 GMT-0000 jacmoe 8
Mon 28 of Jun, 2010 21:43 GMT-0000 jacmoe 7
Sat 02 of Jan, 2010 03:01 GMT-0000 jacmoe 6
Tue 22 of Dec, 2009 01:09 GMT-0000 jacmoe 5
Tue 22 of Dec, 2009 01:09 GMT-0000 jacmoe 4
Tue 22 of Dec, 2009 01:09 GMT-0000 jacmoe 3
Tue 22 of Dec, 2009 01:08 GMT-0000 jacmoe 2
Mon 09 of Mar, 2009 21:45 GMT-0000 OgreWikiBot Robot: Cosmetic changes 1