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.
Table of contents
Snippet
This code creates a sphere mesh with the given name:
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 longitude in the sphere. Defaults to 16.
- nSegments - Number of lines of latitude 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:
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 material.
Creating a sphere with ManualObject
You can also create the mesh with the ManualObject class:
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.
Screen capture shown above is from ManualObject method lit with point light of ColourValue( 1.0f, 0.80f, 0.0f ), textured with MRAMOR6X6.jpg