# Generate Sphere

## Summary

Below is an example demonstrating the use of the geometry pipeline to generate a geometric sphere.

## Example

Generate_Sphere.dsa
```// Define an anonymous function;
// serves as our main loop,
// limits the scope of variables
(function(){

/*********************************************************************/
// Number : A function to convert from meters/yards/feet/inches to centimeters
function convertToCM( nVal, sFromUnit )
{
switch( sFromUnit.lower() ){
case "m":
return nVal * 100;
case "yd":
return nVal * 36.0 * 2.54;
case "ft":
return nVal * 12.0 * 2.54;
case "in":
return nVal * 2.54;
}

return nVal;
};

/*********************************************************************/
// String : A function for finding a unique label in the scene
function getUniqueLabel( sLabel )
{
// Initialize
var sUnqLabel = sLabel;
// If a node by the same label already exists
if( Scene.findNodeByLabel( sUnqLabel ) ){
// Create an array of the label parts
var aLabel = sUnqLabel.split( " " );
// Initialize
var sPreLabel = sUnqLabel;
// Get the last label part
var sLastPart = aLabel[ aLabel.length - 1 ];
// If the last label part is a number enclosed in parens
if( sLastPart.startsWith( "(" ) && sLastPart.endsWith( ")" ) &&
!isNaN( sLastPart.substring( 1, sLastPart.length - 1 ) ) ){
// Get rid of the number
aLabel.pop();
// Reconstruct the Label without the number
sPreLabel = aLabel.join( " " );
}
// Initialize a count
var i = 2;
// Until we cannot find a node with the label
while( Scene.findNodeByLabel( String( "%1 (%2)" ).arg( sPreLabel ).arg( i ) ) ){
// Increment the count
i += 1;
}
// Construct the unique Label
sUnqLabel = String( "%1 (%2)" ).arg( sPreLabel ).arg( i );
}

// Return the unique Label
return sUnqLabel;
};

/*********************************************************************/
// Get the user input
var sOrigin = "Object Center"; // "World Center"
var nSize = 10;
var sUnit = "cm"; // "m", "yd", "ft", "in"
var nSegments = 12;
var nSides = 12;

// Convert the input value to centimeters
nSize = convertToCM( nSize, sUnit );

// Sanity check the input values
if ( nSize <= 0.0 || nSegments < 1 || nSides < 3 )
{
// We are done...
return;
}

// Let the user know we are busy
setBusyCursor();

// Create new node
var oNode = new DzNode();

// Set the node name;
// use a name that is consistent with the create primitive action
oNode.setName( "Sphere" );

// Build the label of the node
var sLabel = String( "Sphere %1x%2 %3%4" )
.arg( nSides )
.arg( nSegments )
.arg( nSize )
.arg( sUnit );
oNode.setLabel( getUniqueLabel( sLabel ) );

// Create a new object
var oObject = new DzObject();

// Set the object name;
// use a name that is consistent with the create primitive action
oObject.name = String( "pSphere(%1)%2_%3" )
.arg( nSize )
.arg( nSides )
.arg( nSegments );

// Create a new polygonal shape
var oFacetShape = new DzFacetShape();

// Set the shape name and label;
// use a name that is consistent with the create primitive action
oFacetShape.name = "Default";
oFacetShape.setLabel( oFacetShape.name );

// Create a new polygonal mesh
var oFacetMesh = new DzFacetMesh();

// Create a new default material
var oMaterial = new DzDefaultMaterial();

// Set the material name and label;
// use a name that is consistent with the create primitive action
oMaterial.name = "Default";
oMaterial.setLabel( oMaterial.name );

// Add the material to the shape

// Begin editing the mesh
oFacetMesh.beginEdit();

// Get the UV map
var oMap = oFacetMesh.getUVs();

// Activate the material; all new geometry will be added to this
oFacetMesh.activateMaterial( oMaterial.name );

// Declare some variables for generating the mesh
var i, j, nNext;
var x, y, z, nAngle, nSinAngle;
var bAtSeam = false;
var vecUVs = new DzVec3( 0, 0, 0 );

var nVerts = nSegments - 1;
var nPoleSegments = nSegments - 2;
var nUvOffset = (nSides - 1) * 2;
var nHalfSize = nSize / 2;

// Pre-size the vertex array; faster than dynamic resizing
oFacetMesh.preSizeVertexArray( nSides * nVerts + 2 );

// Create vertices for the poles
oFacetMesh.addVertex( 0.0, 0.0, 0.0 );
oFacetMesh.addVertex( 0.0, nSize, 0.0 );

// Create UVs for the poles
var nOffset = 0.5 / nSides;
for( i = 0; i < nSides; i += 1 ){
vecUVs.x = i / nSides + nOffset;
vecUVs.y = 0;
oMap.appendPnt2Vec( vecUVs );
vecUVs.y = 1;
oMap.appendPnt2Vec( vecUVs );
}

// Create the vertices and UVs for the sides
for( i = 0; i < nSides; i += 1 ){
nAngle = ((Math.PI * 2) * i) / nSides;
x = Math.sin( nAngle ) * nHalfSize;
z = Math.cos( nAngle ) * nHalfSize;
vecUVs.x = i / nSides;
for( j = 0; j < nVerts; j += 1 ){
nAngle = Math.PI * (j + 1) / nSegments;
y = -Math.cos( nAngle ) * nHalfSize + nHalfSize;
nSinAngle = Math.sin( nAngle );
oFacetMesh.addVertex( x * nSinAngle, y, z * nSinAngle );
vecUVs.y = (j + 1) / nSegments;
oMap.appendPnt2Vec( vecUVs );
}
}

// Create UVs for the seam
var nSeam = oMap.getNumValues();
vecUVs.x = 1;
for( i = 0; i < nVerts; i += 1 ){
vecUVs.y = (i + 1) / nSegments;
oMap.appendPnt2Vec( vecUVs );
}

// Pre-size the facet array; faster than dynamic resizing
oFacetMesh.preSizeFacets( nSides * (nPoleSegments + 2) );

var aVertexIndices = new Array( 4 );
var aUvIndices = new Array( 4 );

// Create the faces
for( i = 0; i < nSides; i += 1 ){
nNext = i + 1;
if( nNext >= nSides ){
bAtSeam = true;
nNext = 0;
}

aVertexIndices[0] = 0;
aUvIndices[0] = i * 2;

aVertexIndices[1] = nNext * nVerts + 2;
aUvIndices[1] = bAtSeam ? nSeam : aVertexIndices[1] + nUvOffset;

aVertexIndices[2] = i * nVerts + 2;
aUvIndices[2] = aVertexIndices[2] + nUvOffset;

aVertexIndices[3] = -1;
aUvIndices[3] = -1;

oFacetMesh.addFacet( aVertexIndices, aUvIndices );

for( j = 0; j < nPoleSegments; j += 1 ){
aVertexIndices[0] = i * nVerts + 2 + j;
aUvIndices[0] = aVertexIndices[0] + nUvOffset;

aVertexIndices[1] = nNext * nVerts + 2 + j;
aUvIndices[1] = bAtSeam ? nSeam + j : aVertexIndices[1] + nUvOffset;

aVertexIndices[2] = nNext * nVerts + 3 + j;
aUvIndices[2] = bAtSeam ? nSeam + j + 1 : aVertexIndices[2] + nUvOffset;

aVertexIndices[3] = i * nVerts + 3 + j;
aUvIndices[3] = aVertexIndices[3] + nUvOffset;

oFacetMesh.addFacet( aVertexIndices, aUvIndices );
}

aVertexIndices[0] = 1;
aUvIndices[0] = i * 2 + 1;

aVertexIndices[1] = i * nVerts + nVerts + 1;
aUvIndices[1] = aVertexIndices[1] + nUvOffset;

aVertexIndices[2] = nNext * nVerts + nVerts + 1;
aUvIndices[2] = bAtSeam ? nSeam + nPoleSegments : aVertexIndices[2] + nUvOffset;

aVertexIndices[3] = -1;
aUvIndices[3] = -1;

oFacetMesh.addFacet( aVertexIndices, aUvIndices );
}

// Finish editing the mesh
oFacetMesh.finishEdit();

// Set the mesh for the shape
oFacetShape.setFacetMesh( oFacetMesh );

// Add the shape to the object

// Add the object to the node
oNode.setObject( oObject );

// Get the local bounding box
var boxLocal = oNode.getLocalBoundingBox();
var vecMax = boxLocal.max;
var vecMin = boxLocal.min;

// If the user chose the object center for the origin
if( sOrigin == "Object Center" ){
// Get the middle of the height of the box
var nMid = (vecMax.y + vecMin.y) * 0.5;

// Set the origin; default and current
var vecOrigin = new DzVec3(0, nMid, 0);
oNode.setOrigin( vecOrigin, true );
oNode.setOrigin( vecOrigin );
}

// If the height of the bounding box is less than
// 1 unit (1cm) tall, set it to be 1 unit tall
if( vecMax.y < 1 ){
vecMax.y = 1;
}

// Set the end point; default and current
var vecEndPoint = new DzVec3( 0, vecMax.y, 0 );
oNode.setEndPoint( vecEndPoint, true );
oNode.setEndPoint( vecEndPoint );

// Get the presentation for the node
var oPresentation = oNode.getPresentation();

// If the node did not have a presentation,
// create one and assign it to the node
if( !oPresentation ){
oPresentation = new DzPresentation();
oNode.setPresentation( oPresentation );
}

// Set the type of node
oPresentation.type = "Prop";

// Add the node to the scene