// // Description // // Transforms the given components. This method is used by // the move, rotate, and scale tools in component mode. // The bounding box has to be updated here, so do the normals and // any other attributes that depend on vertex positions. // // Arguments // mat - matrix to transform the components by // componentList - list of components to be transformed, // or an empty list to indicate the whole surface // cachingMode - how to use the supplied pointCache // pointCache - if non-null, save or restore points from this list base // on the cachingMode // public override void transformUsing(MMatrix mat, MObjectArray componentList, MVertexCachingMode cachingMode, MPointArray pointCache) { apiMeshGeom geomPtr = meshGeom(); bool savePoints = (cachingMode == MVertexCachingMode.kSavePoints); int i = 0, j = 0; uint len = componentList.length; if (cachingMode == MVertexCachingMode.kRestorePoints) { // restore the points based on the data provided in the pointCache attribute // uint cacheLen = pointCache.length; if (len > 0) { // traverse the component list // for ( i = 0; i < len && j < cacheLen; i++ ) { MObject comp = componentList[i]; MFnSingleIndexedComponent fnComp = new MFnSingleIndexedComponent( comp ); int elemCount = fnComp.elementCount; for ( int idx=0; idx<elemCount && j < cacheLen; idx++, ++j ) { int elemIndex = fnComp.element( idx ); geomPtr.vertices[elemIndex] = pointCache[j]; } } } else { // if the component list is of zero-length, it indicates that we // should transform the entire surface // len = geomPtr.vertices.length; for ( int idx = 0; idx < len && j < cacheLen; ++idx, ++j ) { geomPtr.vertices[idx] = pointCache[j]; } } } else { // Transform the surface vertices with the matrix. // If savePoints is true, save the points to the pointCache. // if (len > 0) { // Traverse the componentList // for ( i=0; i<len; i++ ) { MObject comp = componentList[i]; MFnSingleIndexedComponent fnComp = new MFnSingleIndexedComponent( comp ); uint elemCount = (uint)fnComp.elementCount; if (savePoints && 0 == i) { pointCache.sizeIncrement = elemCount; } for ( int idx=0; idx<elemCount; idx++ ) { int elemIndex = fnComp.element( (int)idx ); if (savePoints) { pointCache.append(geomPtr.vertices[elemIndex]); } geomPtr.vertices[elemIndex].multiplyEqual( mat ); geomPtr.normals[idx] = geomPtr.normals[idx].transformAsNormal( mat ); } } } else { // If the component list is of zero-length, it indicates that we // should transform the entire surface // len = geomPtr.vertices.length; if (savePoints) { pointCache.sizeIncrement = len; } for ( int idx = 0; idx < len; ++idx ) { if (savePoints) { pointCache.append(geomPtr.vertices[idx]); } geomPtr.vertices[idx].multiplyEqual( mat ); geomPtr.normals[idx] = geomPtr.normals[idx].transformAsNormal( mat ); } } } // Retrieve the value of the cached surface attribute. // We will set the new geometry data into the cached surface attribute // // Access the datablock directly. This code has to be efficient // and so we bypass the compute mechanism completely. // NOTE: In general we should always go though compute for getting // and setting attributes. // MDataBlock datablock = _forceCache(); MDataHandle cachedHandle = datablock.outputValue( cachedSurface ); apiMeshData cached = cachedHandle.asPluginData as apiMeshData; MDataHandle dHandle = datablock.outputValue( mControlPoints ); // If there is history then calculate the tweaks necessary for // setting the final positions of the vertices. // if ( hasHistory() && (null != cached) ) { // Since the shape has history, we need to store the tweaks (deltas) // between the input shape and the tweaked shape in the control points // attribute. // buildControlPoints( datablock, (int)geomPtr.vertices.length ); MArrayDataHandle cpHandle = new MArrayDataHandle( dHandle ); // Loop through the component list and transform each vertex. // for ( i=0; i<len; i++ ) { MObject comp = componentList[i]; MFnSingleIndexedComponent fnComp = new MFnSingleIndexedComponent( comp ); int elemCount = fnComp.elementCount; for ( int idx=0; idx<elemCount; idx++ ) { int elemIndex = fnComp.element( idx ); cpHandle.jumpToElement( (uint)elemIndex ); MDataHandle pntHandle = cpHandle.outputValue(); double[] pnt = pntHandle.Double3; MPoint oldPnt = cached.fGeometry.vertices[elemIndex]; MPoint newPnt = geomPtr.vertices[elemIndex]; MVector offset = newPnt.minus( oldPnt ); pnt[0] += offset[0]; pnt[1] += offset[1]; pnt[2] += offset[2]; pntHandle.Double3 = pnt; } } } // Copy outputSurface to cachedSurface // if ( null == cached ) { MGlobal.displayInfo("NULL cachedSurface data found"); } else { cached.fGeometry = geomPtr; } MPlug pCPs = new MPlug( thisMObject(), mControlPoints ); pCPs.setValue(dHandle); // Moving vertices will likely change the bounding box. // computeBoundingBox( datablock ); // Tell Maya the bounding box for this object has changed // and thus "boundingBox()" needs to be called. // childChanged( MChildChanged.kBoundingBoxChanged ); }
// // Description // // Transforms the given components. This method is used by // the move, rotate, and scale tools in component mode when the // tweaks for the shape are stored on a separate tweak node. // The bounding box has to be updated here, so do the normals and // any other attributes that depend on vertex positions. // // Arguments // mat - matrix to transform the components by // componentList - list of components to be transformed, // or an empty list to indicate the whole surface // cachingMode - how to use the supplied pointCache // pointCache - if non-null, save or restore points from this list base // on the cachingMode // handle - handle to the attribute on the tweak node where the // tweaks should be stored // public override void tweakUsing( MMatrix mat, MObjectArray componentList, MVertexCachingMode cachingMode, MPointArray pointCache, MArrayDataHandle handle) { apiMeshGeom geomPtr = meshGeom(); bool savePoints = (cachingMode == MVertexCachingMode.kSavePoints); bool updatePoints = (cachingMode == MVertexCachingMode.kUpdatePoints); MArrayDataBuilder builder = handle.builder(); MPoint delta = new MPoint(); MPoint currPt = new MPoint(); MPoint newPt = new MPoint(); int i=0; uint len = componentList.length; int cacheIndex = 0; uint cacheLen = (null != pointCache) ? pointCache.length : 0; if (cachingMode == MVertexCachingMode.kRestorePoints) { // restore points from the pointCache // if (len > 0) { // traverse the component list // for ( i=0; i<len; i++ ) { MObject comp = componentList[i]; MFnSingleIndexedComponent fnComp = new MFnSingleIndexedComponent( comp ); int elemCount = fnComp.elementCount; for ( int idx=0; idx<elemCount && cacheIndex < cacheLen; idx++, cacheIndex++) { int elemIndex = fnComp.element( idx ); MDataHandle hdl = builder.addElement((uint)elemIndex); double[] pt = hdl.Double3; MPoint cachePt = pointCache[cacheIndex]; pt[0] += cachePt.x; pt[1] += cachePt.y; pt[2] += cachePt.z; hdl.Double3 = pt; } } } else { // if the component list is of zero-length, it indicates that we // should transform the entire surface // len = geomPtr.vertices.length; for ( uint idx = 0; idx < len && idx < cacheLen; ++idx ) { MDataHandle hdl = builder.addElement(idx); double[] pt = hdl.Double3; MPoint cachePt = pointCache[cacheIndex]; pt[0] += cachePt.x; pt[1] += cachePt.y; pt[2] += cachePt.z; hdl.Double3 = pt; } } } else { // Tweak the points. If savePoints is true, also save the tweaks in the // pointCache. If updatePoints is true, add the new tweaks to the existing // data in the pointCache. // if (len > 0) { for ( i=0; i<len; i++ ) { MObject comp = componentList[i]; MFnSingleIndexedComponent fnComp = new MFnSingleIndexedComponent( comp ); int elemCount = fnComp.elementCount; if (savePoints) { pointCache.sizeIncrement = (uint)elemCount; } for ( int idx=0; idx<elemCount; idx++ ) { int elemIndex = fnComp.element( idx ); MDataHandle hdl = builder.addElement((uint)elemIndex); double[] pt = hdl.Double3; currPt = newPt = geomPtr.vertices[elemIndex]; newPt.multiplyEqual( mat ); delta.x = newPt.x - currPt.x; delta.y = newPt.y - currPt.y; delta.z = newPt.z - currPt.z; pt[0] += delta.x; pt[1] += delta.y; pt[2] += delta.z; hdl.Double3 = pt; if (savePoints) { // store the points in the pointCache for undo // pointCache.append(delta*(-1.0)); } else if (updatePoints && cacheIndex < cacheLen) { MPoint cachePt = pointCache[cacheIndex]; cachePt[0] -= delta.x; cachePt[1] -= delta.y; cachePt[2] -= delta.z; cacheIndex++; } } } } else { // if the component list is of zero-length, it indicates that we // should transform the entire surface // len = geomPtr.vertices.length; if (savePoints) { pointCache.sizeIncrement = len; } for ( int idx = 0; idx < len; ++idx ) { MDataHandle hdl = builder.addElement((uint)idx); double[] pt = hdl.Double3; currPt = newPt = geomPtr.vertices[idx]; newPt.multiplyEqual( mat ); delta.x = newPt.x - currPt.x; delta.y = newPt.y - currPt.y; delta.z = newPt.z - currPt.z; pt[0] += delta.x; pt[1] += delta.y; pt[2] += delta.z; hdl.Double3 = pt; if (savePoints) { // store the points in the pointCache for undo // pointCache.append(delta*-1.0); } else if (updatePoints && idx < cacheLen) { MPoint cachePt = pointCache[idx]; cachePt[0] -= delta.x; cachePt[1] -= delta.y; cachePt[2] -= delta.z; } } } } // Set the builder into the handle. // handle.set(builder); // Tell Maya the bounding box for this object has changed // and thus "boundingBox()" needs to be called. // childChanged( MChildChanged.kBoundingBoxChanged ); }
public override void tweakUsing( MMatrix mat, MObjectArray componentList, MVertexCachingMode cachingMode, MPointArray pointCache, MArrayDataHandle handle ) // // Description // // Transforms the given components. This method is used by // the move, rotate, and scale tools in component mode when the // tweaks for the shape are stored on a separate tweak node. // The bounding box has to be updated here, so do the normals and // any other attributes that depend on vertex positions. // // Arguments // mat - matrix to transform the components by // componentList - list of components to be transformed, // or an empty list to indicate the whole surface // cachingMode - how to use the supplied pointCache // pointCache - if non-null, save or restore points from this list base // on the cachingMode // handle - handle to the attribute on the tweak node where the // tweaks should be stored // { apiMeshGeom geomPtr = meshGeom(); bool savePoints = (cachingMode == MVertexCachingMode.kSavePoints); bool updatePoints = (cachingMode == MVertexCachingMode.kUpdatePoints); MArrayDataBuilder builder = handle.builder(); MPoint delta = new MPoint(); MPoint currPt = new MPoint(); MPoint newPt = new MPoint(); int i=0; uint len = componentList.length; int cacheIndex = 0; uint cacheLen = (null != pointCache) ? pointCache.length : 0; if (cachingMode == MVertexCachingMode.kRestorePoints) { // restore points from the pointCache // if (len > 0) { // traverse the component list // for ( i=0; i<len; i++ ) { MObject comp = componentList[i]; MFnSingleIndexedComponent fnComp = new MFnSingleIndexedComponent( comp ); int elemCount = fnComp.elementCount; for ( int idx=0; idx<elemCount && cacheIndex < cacheLen; idx++, cacheIndex++) { int elemIndex = fnComp.element( idx ); MDataHandle hdl = builder.addElement((uint)elemIndex); double[] pt = hdl.Double3; MPoint cachePt = pointCache[cacheIndex]; pt[0] += cachePt.x; pt[1] += cachePt.y; pt[2] += cachePt.z; hdl.Double3 = pt; } } } else { // if the component list is of zero-length, it indicates that we // should transform the entire surface // len = geomPtr.vertices.length; for ( uint idx = 0; idx < len && idx < cacheLen; ++idx ) { MDataHandle hdl = builder.addElement(idx); double[] pt = hdl.Double3; MPoint cachePt = pointCache[cacheIndex]; pt[0] += cachePt.x; pt[1] += cachePt.y; pt[2] += cachePt.z; hdl.Double3 = pt; } } } else { // Tweak the points. If savePoints is true, also save the tweaks in the // pointCache. If updatePoints is true, add the new tweaks to the existing // data in the pointCache. // if (len > 0) { for ( i=0; i<len; i++ ) { MObject comp = componentList[i]; MFnSingleIndexedComponent fnComp = new MFnSingleIndexedComponent( comp ); int elemCount = fnComp.elementCount; if (savePoints) { pointCache.sizeIncrement = (uint)elemCount; } for ( int idx=0; idx<elemCount; idx++ ) { int elemIndex = fnComp.element( idx ); MDataHandle hdl = builder.addElement((uint)elemIndex); double[] pt = hdl.Double3; currPt = newPt = geomPtr.vertices[elemIndex]; newPt.multiplyEqual( mat ); delta.x = newPt.x - currPt.x; delta.y = newPt.y - currPt.y; delta.z = newPt.z - currPt.z; pt[0] += delta.x; pt[1] += delta.y; pt[2] += delta.z; hdl.Double3 = pt; if (savePoints) { // store the points in the pointCache for undo // pointCache.append(delta*(-1.0)); } else if (updatePoints && cacheIndex < cacheLen) { MPoint cachePt = pointCache[cacheIndex]; cachePt[0] -= delta.x; cachePt[1] -= delta.y; cachePt[2] -= delta.z; cacheIndex++; } } } } else { // if the component list is of zero-length, it indicates that we // should transform the entire surface // len = geomPtr.vertices.length; if (savePoints) { pointCache.sizeIncrement = len; } for ( int idx = 0; idx < len; ++idx ) { MDataHandle hdl = builder.addElement((uint)idx); double[] pt = hdl.Double3; currPt = newPt = geomPtr.vertices[idx]; newPt.multiplyEqual( mat ); delta.x = newPt.x - currPt.x; delta.y = newPt.y - currPt.y; delta.z = newPt.z - currPt.z; pt[0] += delta.x; pt[1] += delta.y; pt[2] += delta.z; hdl.Double3 = pt; if (savePoints) { // store the points in the pointCache for undo // pointCache.append(delta*-1.0); } else if (updatePoints && idx < cacheLen) { MPoint cachePt = pointCache[idx]; cachePt[0] -= delta.x; cachePt[1] -= delta.y; cachePt[2] -= delta.z; } } } } // Set the builder into the handle. // handle.set(builder); // Tell Maya the bounding box for this object has changed // and thus "boundingBox()" needs to be called. // childChanged( MChildChanged.kBoundingBoxChanged ); }
// // Description // // Create circles of vertices starting with // the top pole ending with the bottom pole // public void buildSphere(double rad, int div, MPointArray vertices, MIntArray counts, MIntArray connects, MVectorArray normals, apiMeshGeomUV uvs) { double u = -Math.PI / 2.0; double v = -Math.PI; double u_delta = Math.PI / ((double)div); double v_delta = 2 * Math.PI / ((double)div); MPoint topPole = new MPoint( 0.0, rad, 0.0 ); MPoint botPole = new MPoint( 0.0, -rad, 0.0 ); // Build the vertex and normal table // vertices.append( botPole ); normals.append( botPole.minus(MPoint.origin) ); int i; for ( i=0; i<(div-1); i++ ) { u += u_delta; v = -Math.PI; for ( int j=0; j<div; j++ ) { double x = rad * Math.Cos(u) * Math.Cos(v); double y = rad * Math.Sin(u); double z = rad * Math.Cos(u) * Math.Sin(v) ; MPoint pnt = new MPoint( x, y, z ); vertices.append( pnt ); normals.append( pnt.minus(MPoint.origin) ); v += v_delta; } } vertices.append( topPole ); normals.append( topPole.minus(MPoint.origin) ); // Create the connectivity lists // int vid = 1; int numV = 0; for ( i=0; i<div; i++ ) { for ( int j=0; j<div; j++ ) { if ( i==0 ) { counts.append( 3 ); connects.append( 0 ); connects.append( j+vid ); connects.append( (j==(div-1)) ? vid : j+vid+1 ); } else if ( i==(div-1) ) { counts.append( 3 ); connects.append( j+vid+1-div ); connects.append( vid+1 ); connects.append( j==(div-1) ? vid+1-div : j+vid+2-div ); } else { counts.append( 4 ); connects.append( j + vid+1-div ); connects.append( j + vid+1 ); connects.append( j == (div-1) ? vid+1 : j+vid+2 ); connects.append( j == (div-1) ? vid+1-div : j+vid+2-div ); } numV++; } vid = numV; } // TODO: Define UVs for sphere ... // }
// // Description // // Constructs a cube // public void buildCube(double cube_size, MPointArray pa, MIntArray faceCounts, MIntArray faceConnects, MVectorArray normals, apiMeshGeomUV uvs) { const int num_faces = 6; const int num_face_connects = 24; const double normal_value = 0.5775; const int uv_count = 14; pa.clear(); faceCounts.clear(); faceConnects.clear(); uvs.reset(); pa.append( new MPoint( -cube_size, -cube_size, -cube_size ) ); pa.append( new MPoint( cube_size, -cube_size, -cube_size ) ); pa.append( new MPoint( cube_size, -cube_size, cube_size ) ); pa.append( new MPoint( -cube_size, -cube_size, cube_size ) ); pa.append( new MPoint( -cube_size, cube_size, -cube_size ) ); pa.append( new MPoint( -cube_size, cube_size, cube_size ) ); pa.append( new MPoint( cube_size, cube_size, cube_size ) ); pa.append( new MPoint( cube_size, cube_size, -cube_size ) ); normals.append( new MVector( -normal_value, -normal_value, -normal_value ) ); normals.append( new MVector( normal_value, -normal_value, -normal_value ) ); normals.append( new MVector( normal_value, -normal_value, normal_value ) ); normals.append( new MVector( -normal_value, -normal_value, normal_value ) ); normals.append( new MVector( -normal_value, normal_value, -normal_value ) ); normals.append( new MVector( -normal_value, normal_value, normal_value ) ); normals.append( new MVector( normal_value, normal_value, normal_value ) ); normals.append( new MVector( normal_value, normal_value, -normal_value ) ); // Define the UVs for the cube. // float[] uv_pts = new float[uv_count*2] { 0.375f, 0.0f, 0.625f, 0.0f, 0.625f, 0.25f, 0.375f, 0.25f, 0.625f, 0.5f, 0.375f, 0.5f, 0.625f, 0.75f, 0.375f, 0.75f, 0.625f, 1.0f, 0.375f, 1.0f, 0.875f, 0.0f, 0.875f, 0.25f, 0.125f, 0.0f, 0.125f, 0.25f }; // UV Face Vertex Id. // int[] uv_fvid = new int[num_face_connects]{ 0, 1, 2, 3, 3, 2, 4, 5, 5, 4, 6, 7, 7, 6, 8, 9, 1, 10, 11, 2, 12, 0, 3, 13 }; int i; for ( i = 0; i < uv_count; i ++ ) { uvs.append_uv( uv_pts[i*2], uv_pts[i*2 + 1] ); } for ( i = 0; i < num_face_connects; i ++ ) { uvs.faceVertexIndex.append( uv_fvid[i] ); } // Set up an array containing the number of vertices // for each of the 6 cube faces (4 vertices per face) // int[] face_counts = new int[num_faces]{ 4, 4, 4, 4, 4, 4 }; for ( i=0; i<num_faces; i++ ) { faceCounts.append( face_counts[i] ); } // Set up and array to assign vertices from pa to each face // int[] face_connects = new int[ num_face_connects ]{ 0, 1, 2, 3, 4, 5, 6, 7, 3, 2, 6, 5, 0, 3, 5, 4, 0, 4, 7, 1, 1, 7, 6, 2 }; for ( i=0; i<num_face_connects; i++ ) { faceConnects.append( face_connects[i] ); } }
// // Description // // Create circles of vertices starting with // the top pole ending with the bottom pole // public void buildSphere(double rad, int div, MPointArray vertices, MIntArray counts, MIntArray connects, MVectorArray normals, apiMeshGeomUV uvs) { double u = -Math.PI / 2.0; double v = -Math.PI; double u_delta = Math.PI / ((double)div); double v_delta = 2 * Math.PI / ((double)div); MPoint topPole = new MPoint(0.0, rad, 0.0); MPoint botPole = new MPoint(0.0, -rad, 0.0); // Build the vertex and normal table // vertices.append(botPole); normals.append(botPole.minus(MPoint.origin)); int i; for (i = 0; i < (div - 1); i++) { u += u_delta; v = -Math.PI; for (int j = 0; j < div; j++) { double x = rad * Math.Cos(u) * Math.Cos(v); double y = rad * Math.Sin(u); double z = rad * Math.Cos(u) * Math.Sin(v); MPoint pnt = new MPoint(x, y, z); vertices.append(pnt); normals.append(pnt.minus(MPoint.origin)); v += v_delta; } } vertices.append(topPole); normals.append(topPole.minus(MPoint.origin)); // Create the connectivity lists // int vid = 1; int numV = 0; for (i = 0; i < div; i++) { for (int j = 0; j < div; j++) { if (i == 0) { counts.append(3); connects.append(0); connects.append(j + vid); connects.append((j == (div - 1)) ? vid : j + vid + 1); } else if (i == (div - 1)) { counts.append(3); connects.append(j + vid + 1 - div); connects.append(vid + 1); connects.append(j == (div - 1) ? vid + 1 - div : j + vid + 2 - div); } else { counts.append(4); connects.append(j + vid + 1 - div); connects.append(j + vid + 1); connects.append(j == (div - 1) ? vid + 1 : j + vid + 2); connects.append(j == (div - 1) ? vid + 1 - div : j + vid + 2 - div); } numV++; } vid = numV; } // TODO: Define UVs for sphere ... // }
// // Description // // Constructs a cube // public void buildCube(double cube_size, MPointArray pa, MIntArray faceCounts, MIntArray faceConnects, MVectorArray normals, apiMeshGeomUV uvs) { const int num_faces = 6; const int num_face_connects = 24; const double normal_value = 0.5775; const int uv_count = 14; pa.clear(); faceCounts.clear(); faceConnects.clear(); uvs.reset(); pa.append(new MPoint(-cube_size, -cube_size, -cube_size)); pa.append(new MPoint(cube_size, -cube_size, -cube_size)); pa.append(new MPoint(cube_size, -cube_size, cube_size)); pa.append(new MPoint(-cube_size, -cube_size, cube_size)); pa.append(new MPoint(-cube_size, cube_size, -cube_size)); pa.append(new MPoint(-cube_size, cube_size, cube_size)); pa.append(new MPoint(cube_size, cube_size, cube_size)); pa.append(new MPoint(cube_size, cube_size, -cube_size)); normals.append(new MVector(-normal_value, -normal_value, -normal_value)); normals.append(new MVector(normal_value, -normal_value, -normal_value)); normals.append(new MVector(normal_value, -normal_value, normal_value)); normals.append(new MVector(-normal_value, -normal_value, normal_value)); normals.append(new MVector(-normal_value, normal_value, -normal_value)); normals.append(new MVector(-normal_value, normal_value, normal_value)); normals.append(new MVector(normal_value, normal_value, normal_value)); normals.append(new MVector(normal_value, normal_value, -normal_value)); // Define the UVs for the cube. // float[] uv_pts = new float[uv_count * 2] { 0.375f, 0.0f, 0.625f, 0.0f, 0.625f, 0.25f, 0.375f, 0.25f, 0.625f, 0.5f, 0.375f, 0.5f, 0.625f, 0.75f, 0.375f, 0.75f, 0.625f, 1.0f, 0.375f, 1.0f, 0.875f, 0.0f, 0.875f, 0.25f, 0.125f, 0.0f, 0.125f, 0.25f }; // UV Face Vertex Id. // int[] uv_fvid = new int[num_face_connects] { 0, 1, 2, 3, 3, 2, 4, 5, 5, 4, 6, 7, 7, 6, 8, 9, 1, 10, 11, 2, 12, 0, 3, 13 }; int i; for (i = 0; i < uv_count; i++) { uvs.append_uv(uv_pts[i * 2], uv_pts[i * 2 + 1]); } for (i = 0; i < num_face_connects; i++) { uvs.faceVertexIndex.append(uv_fvid[i]); } // Set up an array containing the number of vertices // for each of the 6 cube faces (4 vertices per face) // int[] face_counts = new int[num_faces] { 4, 4, 4, 4, 4, 4 }; for (i = 0; i < num_faces; i++) { faceCounts.append(face_counts[i]); } // Set up and array to assign vertices from pa to each face // int[] face_connects = new int[num_face_connects] { 0, 1, 2, 3, 4, 5, 6, 7, 3, 2, 6, 5, 0, 3, 5, 4, 0, 4, 7, 1, 1, 7, 6, 2 }; for (i = 0; i < num_face_connects; i++) { faceConnects.append(face_connects[i]); } }