MFloatPointArray ropeGenerator::createHalfRope( int pointsCount, float radius ) { MFloatPointArray points; MPoint baseVector( 1,0,0 ); baseVector = baseVector * radius; points.append( MFloatPoint( baseVector.x, baseVector.y, baseVector.z, 1.0 ) ); float fbaseAngle = 180.0 / float( pointsCount ); for (int d = 1; d < pointsCount; d++) { if (d == 1) { MAngle baseAngle((fbaseAngle * 0.25), MAngle::kDegrees ); MVector vVector( baseVector ); vVector = vVector.rotateBy( MVector::kYaxis, baseAngle.asRadians() ); points.append( MFloatPoint( vVector.x, vVector.y, vVector.z, 1.0 ) ); } MAngle baseAngle((fbaseAngle * float( d ) ), MAngle::kDegrees ); MVector vVector( baseVector ); vVector = vVector.rotateBy( MVector::kYaxis, baseAngle.asRadians() ); points.append( MFloatPoint( vVector.x, vVector.y, vVector.z, 1.0 ) ); if ( d == pointsCount - 1 ) { MAngle baseAngle((fbaseAngle * ( d + 0.75 )), MAngle::kDegrees ); MVector vVector( baseVector ); vVector = vVector.rotateBy( MVector::kYaxis, baseAngle.asRadians() ); points.append( MFloatPoint( vVector.x, vVector.y, vVector.z, 1.0 ) ); } } return points; }
void SoftBodyNode::createHelperMesh(MFnMesh &mayaMesh, std::vector<int> &triIndices, std::vector<float> &triVertices, MSpace::Space space) { MFloatPointArray ptArray; mayaMesh.getPoints(ptArray, space); // append vertex locations (x, y, z) into "flattened array" for(int i = 0; i < ptArray.length(); i++) { MFloatPoint pt; pt = ptArray[i]; pt.cartesianize(); triVertices.push_back(pt.x); triVertices.push_back(pt.y); triVertices.push_back(pt.z); } std::cout << std::endl; // create vector of triangle indices MIntArray tCounts; MIntArray tVerts; mayaMesh.getTriangles(tCounts, tVerts); triIndices.resize(tVerts.length()); for(int i = 0; i < tVerts.length(); i ++) { triIndices[i] = tVerts[i]; } }
MObject animCube::createMesh(const MTime& time, MObject& outData, MStatus& stat) { int numVertices, frame; float cubeSize; MFloatPointArray points; MFnMesh meshFS; // Scale the cube on the frame number, wrap every 10 frames. frame = (int)time.as( MTime::kFilm ); if (frame == 0) frame = 1; cubeSize = 0.5f * (float)( frame % 10); const int numFaces = 6; numVertices = 8; const int numFaceConnects = 24; MFloatPoint vtx_1( -cubeSize, -cubeSize, -cubeSize ); MFloatPoint vtx_2( cubeSize, -cubeSize, -cubeSize ); MFloatPoint vtx_3( cubeSize, -cubeSize, cubeSize ); MFloatPoint vtx_4( -cubeSize, -cubeSize, cubeSize ); MFloatPoint vtx_5( -cubeSize, cubeSize, -cubeSize ); MFloatPoint vtx_6( -cubeSize, cubeSize, cubeSize ); MFloatPoint vtx_7( cubeSize, cubeSize, cubeSize ); MFloatPoint vtx_8( cubeSize, cubeSize, -cubeSize ); points.append( vtx_1 ); points.append( vtx_2 ); points.append( vtx_3 ); points.append( vtx_4 ); points.append( vtx_5 ); points.append( vtx_6 ); points.append( vtx_7 ); points.append( vtx_8 ); // Set up an array containing the number of vertices // for each of the 6 cube faces (4 verticies per face) // int face_counts[numFaces] = { 4, 4, 4, 4, 4, 4 }; MIntArray faceCounts( face_counts, numFaces ); // Set up and array to assign vertices from points to each face // int face_connects[ numFaceConnects ] = { 0, 1, 2, 3, 4, 5, 6, 7, 3, 2, 6, 5, 0, 3, 5, 4, 0, 4, 7, 1, 1, 7, 6, 2 }; MIntArray faceConnects( face_connects, numFaceConnects ); MObject newMesh = meshFS.create(numVertices, numFaces, points, faceCounts, faceConnects, outData, &stat); return newMesh; }
MStatus metro_model_translator::create_shape( const m2033::mesh_ptr m ) { MFloatPointArray v; MVectorArray norm; MIntArray p; MIntArray idx; MFnTransform transform_fn; MObject transform_obj = transform_fn.create( MObject::kNullObj ); transform_fn.setName( m->get_name().c_str() ); m2033::mesh::vertices mv = m->get_vertices(); m2033::mesh::indices mi = m->get_indices(); m2033::mesh::texcoords mt = m->get_tex_coords(); m2033::mesh::normals mn = m->get_normals(); for( unsigned i = 0; i < mv.size(); i++ ) { v.append( -mv[i].x, mv[i].y, mv[i].z ); norm.append( MVector( -mn[i].x, mn[i].y, mn[i].z ) ); } for( unsigned i = 0; i < mi.size() / 3; i++ ) { idx.append( mi[i*3+2] ); idx.append( mi[i*3+1] ); idx.append( mi[i*3] ); p.append( 3 ); } MFloatArray u_values, v_values; for( unsigned i = 0; i < mt.size(); i++ ) { u_values.append( mt[i].x ); v_values.append( -mt[i].y ); } MFnMesh meshFn; MObject mesh = meshFn.create( v.length(), p.length(), v, p, idx, u_values, v_values, transform_obj ); MString name = m->get_name().c_str(); meshFn.setName( name + MString("_shape") ); MStatus s = meshFn.assignUVs( p, idx, 0 ); if( !s ) { return s; } s = meshFn.unlockVertexNormals( idx ); if( !s ) { return s; } meshFn.setVertexNormals( norm, idx ); MObject mat = create_material( m->get_texture_name(), &s ); if( !s ) { return s; } MFnSet mat_fn(mat); mat_fn.addMember(mesh); return MS::kSuccess; }
static void makeCubes(std::vector<cube> &cubes, MString &name, MStatus *stat) { MFnMesh fnMesh; MObject result; MFloatPointArray points; MIntArray faceCounts; MIntArray faceConnects; int index_offset = 0; for (std::vector<cube>::iterator cit = cubes.begin(); cit != cubes.end(); ++cit) { point3 diag = cit->diagonal(); float scale = diag.x; point3 pos = cit->start + (diag * .5f); MFloatVector mpos(pos.x, pos.y, pos.z); addCube(scale, mpos, index_offset * (8), points, faceCounts, faceConnects); index_offset += 1; } unsigned int vtx_cnt = points.length(); unsigned int face_cnt = faceCounts.length(); MObject newMesh = fnMesh.create( /* numVertices */ vtx_cnt, /* numFaces */ face_cnt, points, faceCounts, faceConnects, MObject::kNullObj, stat); /* Harden all edges. */ int n_edges = fnMesh.numEdges(stat); for (int i = 0; i < n_edges; ++i) { fnMesh.setEdgeSmoothing(i, false); } fnMesh.cleanupEdgeSmoothing(); /* Must be called after editing edges. */ fnMesh.updateSurface(); /* Assign Shader. */ MSelectionList sel_list; if (!MFAIL(sel_list.add("initialShadingGroup"))) { MObject set_obj; sel_list.getDependNode(0, set_obj); MFnSet set(set_obj); set.addMember(newMesh); } /* Give it a swanky name. */ MFnDagNode parent(fnMesh.parent(0)); name = parent.setName("polyMengerSponge", false, stat); }
MStatus testNucleusNode::compute(const MPlug &plug, MDataBlock &data) { MStatus stat; if ( plug == nextState ) { //get the value of the currentTime MTime currTime = data.inputValue(currentTime).asTime(); MObject inputData; //pull on start state or current state depending on the current time. if(currTime.value() <= 0.0) { MArrayDataHandle multiDataHandle = data.inputArrayValue(startState); multiDataHandle.jumpToElement(0); inputData =multiDataHandle.inputValue().data(); } else { MArrayDataHandle multiDataHandle = data.inputArrayValue(currentState); multiDataHandle.jumpToElement(0); inputData =multiDataHandle.inputValue().data(); } MFnNObjectData inputNData(inputData); MnCloth * nObj = NULL; inputNData.getObjectPtr(nObj); MFloatPointArray points; nObj->getPositions(points); unsigned int ii; for(ii=0;ii<points.length();ii++) { points[ii].y = (float) sin(points[ii].x + currTime.value()*4.0f*(3.1415f/180.0f)); } nObj->setPositions(points); delete nObj; data.setClean(plug); } else if ( plug == currentState ) { data.setClean(plug); } else if (plug == startState) { data.setClean(plug); } else { stat = MS::kUnknownParameter; } return stat; }
MObject AniMesh::readFrame(const MTime& time,MObject& outData,MStatus& stat) { MFloatPointArray points; MFnMesh meshFS; int frame = (int)time.as( MTime::kFilm ); if (frame == 0) frame = 1; vector<size_t> face_v; vector<double> points_v; char cfilename[256]; sprintf(cfilename, "%s%d.vrml",import_prefix.c_str(),frame); //sprintf(cfilename, "%s%d.vrml",import_prefix.c_str(),0); string filename = string(cfilename); fstream fp; fp.open(filename,ios::in); if (fp) { ImportVrml2 (filename, face_v, points_v); }else{ sprintf(cfilename, "%s%d.vrml",import_prefix.c_str(),0); string filename = string(cfilename); ImportVrml2(filename,face_v,points_v); } size_t numVertices = points_v.size()/3; size_t numFaces = face_v.size()/3; for(vector<double>::const_iterator it = points_v.begin();it != points_v.end();it+=3) { MFloatPoint vtx(*it,*(it+1),*(it+2)); points.append(vtx); } vector<int> face_count; for(int i=0;i<numFaces;i++) { face_count.push_back(3); } MIntArray faceCounts(&face_count[0],numFaces); vector<int> face_connects; face_connects.resize(face_v.size()); for(int i=0;i<face_v.size();++i) { face_connects[i] = face_v[i]; } MIntArray faceConnects( &face_connects[0], face_connects.size() ); MObject newMesh=meshFS.create(numVertices, numFaces,points, faceCounts, faceConnects,outData,&stat); return newMesh; }
bool kgLocator::getPoints( MFloatPointArray &pts ) { MFloatPoint pt; pt.x = -0.5f; pt.y = 0.0f; pt.z = 0.0f; pts.append( pt ); pt.x = 0.5f; pt.y = 0.0f; pt.z = 0.0f; pts.append( pt ); pt.x = 0.0f; pt.y = 0.0f; pt.z = -0.5f; pts.append( pt ); pt.x = 0.0f; pt.y = 0.0f; pt.z = 0.5f; pts.append( pt ); assert( pts.length() == 4 ); return true; }
void ropeGenerator::createCriclePoints( int pointsCount, MMatrix bMatrix, MFloatPointArray &points, float radius ) { MPoint baseVector2( radius,0,0 ); baseVector2 = baseVector2 * bMatrix; points.append( MFloatPoint( baseVector2.x, baseVector2.y, baseVector2.z, 1.0 ) ); float baseAngle = 360.0f / float( pointsCount ); for (int d = 1; d < pointsCount; d++ ) { MVector vVector( radius,0,0 ); vVector = vVector.rotateBy( MVector::kYaxis, MAngle( baseAngle * float( d ),MAngle::kDegrees).asRadians() ); MPoint point( vVector.x, vVector.y, vVector.z ); point = point * bMatrix; points.append( MFloatPoint( point.x, point.y, point.z, 1.0 )); } }
void MayaGeoAttribute::transferValueFromMaya(MPlug &plug, MDataBlock &data){ MDataHandle dataHandle = data.inputValue(plug); MFnMesh meshFn(dataHandle.asMesh()); MFloatPointArray mayaPoints; meshFn.getPoints(mayaPoints); // collect points std::vector<Imath::V3f> coralPoints; for(int i = 0; i < mayaPoints.length(); ++i){ MFloatPoint* mayaPoint = &mayaPoints[i]; coralPoints.push_back(Imath::V3f(mayaPoint->x, mayaPoint->y, mayaPoint->z)); } // collect faces int numPolys = meshFn.numPolygons(); std::vector<std::vector<int> > coralFaces(numPolys); for(int polyId = 0; polyId < numPolys; ++polyId){ MIntArray mayaVertexList; meshFn.getPolygonVertices(polyId, mayaVertexList); int polyPoints = mayaVertexList.length(); std::vector<int> coralFace(polyPoints); for(int i = 0; i < polyPoints; ++i){ int pointId = mayaVertexList[i]; coralFace[i] = pointId; } coralFaces[polyId] = coralFace; } // create coral geo coral::Geo *coralGeo = outValue(); if(coralGeo->hasSameTopology(coralFaces)){ coralGeo->setPoints(coralPoints); } else{ coralGeo->build(coralPoints, coralFaces); } valueChanged(); }
void MayaGeoAttribute::transferValueToMaya(MPlug &plug, MDataBlock &data){ coral::Geo *coralGeo = value(); const std::vector<Imath::V3f> &coralPoints = coralGeo->points(); // transfer points MFloatPointArray mayaPoints; for(int i = 0; i < coralPoints.size(); ++i){ const Imath::V3f *coralPoint = &coralPoints[i]; mayaPoints.append(MFloatPoint(coralPoint->x, coralPoint->y, coralPoint->z)); } // transfer faces MIntArray mayaFaceCount; MIntArray mayaFaceVertices; const std::vector<std::vector<int> > coralFaces = coralGeo->rawFaces(); for(int polyId = 0; polyId < coralFaces.size(); ++polyId){ const std::vector<int> *coralFace = &coralFaces[polyId]; int faceVertexCount = coralFace->size(); mayaFaceCount.append(faceVertexCount); for(int i = 0; i < faceVertexCount; ++i){ mayaFaceVertices.append(coralFace->at(i)); } } // create maya mesh MDataHandle dataHandle = data.outputValue(plug); MFnMeshData dataCreator; MObject newOutputData = dataCreator.create(); MFnMesh newMesh; newMesh.create(mayaPoints.length(), coralFaces.size(), mayaPoints, mayaFaceCount, mayaFaceVertices, newOutputData); dataHandle.set(newOutputData); }
void store_in_hds(HDS &hds, MFloatPointArray &points, MIntArray &nFV, MIntArray &F) { size_t nV = points.length(); hds.V.setDims(3, nV); for (size_t k=0; k<nV; k++) { hds.V[3*k+0] = points[k](0); hds.V[3*k+1] = points[k](1); hds.V[3*k+2] = points[k](2); } hds.nFV.setDims(1,nFV.length()); for (size_t k=0; k<nFV.length(); k++) hds.nFV[k] = nFV[k]; hds.tip.setDims(1,F.length()); for (size_t k=0; k<F.length(); k++) hds.tip[k] = F[k]; }
MBoundingBox BasicLocator::boundingBox() const { MBoundingBox bbox; MFloatPointArray points; points.clear(); points.setSizeIncrement(4); points.append(-1.0, 0.0, 0.0); points.append(1.0, 0.0, 0.0); points.append(0.0, 0.0, 1.0); points.append(0.0, 0.0, -1.0); for (unsigned int i = 0; i < points.length(); i++) bbox.expand(points[i]); return bbox; }
void load_from_hds(HDS &hds, MFloatPointArray &points, MIntArray &nFV, MIntArray &F) { size_t nV = hds.nV(); size_t nF = hds.nF(); size_t nIHE = hds.nIHE(); points.setLength(nV); for (size_t k=0; k<nV; k++) { points[k](0) = hds.V[3*k+0]; points[k](1) = hds.V[3*k+1]; points[k](2) = hds.V[3*k+2]; } nFV.setLength(nF); for (size_t k=0; k<nF; k++) nFV[k] = hds.nFV[k]; F.setLength(nIHE); for (size_t k=0; k<nIHE; k++) F[k] = hds.tip[k]; }
void OsdMeshData::updateGeometry(const MHWRender::MVertexBuffer *points, const MHWRender::MVertexBuffer *normals) { // Update coarse vertex if (!_positionBuffer) return; if (!_normalBuffer) return; int nCoarsePoints = _pointArray.length(); OpenSubdiv::OsdCpuVertexBuffer *cpuPos = dynamic_cast<OpenSubdiv::OsdCpuVertexBuffer*>(_positionBuffer); OpenSubdiv::OsdCpuVertexBuffer *cpuNormal = dynamic_cast<OpenSubdiv::OsdCpuVertexBuffer*>(_normalBuffer); if (cpuPos) { // I know, this is very inefficient... float *d_pos = cpuPos->GetCpuBuffer(); float *d_normal = cpuNormal->GetCpuBuffer(); glBindBuffer(GL_ARRAY_BUFFER, *(GLuint*)points->resourceHandle()); glGetBufferSubData(GL_ARRAY_BUFFER, 0, nCoarsePoints*3*sizeof(float), d_pos); glBindBuffer(GL_ARRAY_BUFFER, *(GLuint*)normals->resourceHandle()); glGetBufferSubData(GL_ARRAY_BUFFER, 0, nCoarsePoints*3*sizeof(float), d_normal); glBindBuffer(GL_ARRAY_BUFFER, 0); } else { glBindBuffer(GL_COPY_READ_BUFFER, *(GLuint*)points->resourceHandle()); glBindBuffer(GL_COPY_WRITE_BUFFER, _positionBuffer->GetGpuBuffer()); glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, nCoarsePoints*3*sizeof(float)); glBindBuffer(GL_COPY_READ_BUFFER, *(GLuint*)normals->resourceHandle()); glBindBuffer(GL_COPY_WRITE_BUFFER, _normalBuffer->GetGpuBuffer()); glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, nCoarsePoints*3*sizeof(float)); glBindBuffer(GL_COPY_READ_BUFFER, 0); glBindBuffer(GL_COPY_WRITE_BUFFER, 0); } _osdmesh->Subdivide(_positionBuffer, NULL); _osdmesh->Subdivide(_normalBuffer, NULL); _needsUpdate = false; }
void ropeGenerator::createRopesRings( int ropesCount, MMatrix bMatrix, MFloatPointArray &points, int pointsCount, float ropeStrength, float radius ) { MAngle angle( (180.0/ ropesCount ), MAngle::kDegrees ); float distanceToMoveRope = cos( angle.asRadians() ); float singleRopeRadius = sin( angle.asRadians() ); float baseAngle = 360.0f / float( ropesCount ); for ( int d = 1; d < ropesCount + 1; d++) { MFloatPointArray ropePoints( createHalfRope( pointsCount, singleRopeRadius ) ); for ( int ropP = 0; ropP < ropePoints.length(); ropP++) { MFloatPoint ropPoint( ropePoints[ropP] ); MVector ropV( ropPoint.x, ropPoint.y, ropPoint.z * ropeStrength ); ropV = ropV + MVector( 0,0,-distanceToMoveRope ); ropV = ropV.rotateBy( MVector::kYaxis, MAngle( baseAngle * float( d ), MAngle::kDegrees).asRadians() ); MPoint ropFinalPoint( ropV * radius ); ropFinalPoint = ropFinalPoint * bMatrix; points.append( MFloatPoint( ropFinalPoint.x, ropFinalPoint.y, ropFinalPoint.z ) ); } } }
void write_to_TCCNodeData(TCCData &tcc, MFloatPointArray &V, TCCNodeData &nd) { size_t nV = V.length(); nd.V.setDims(3, nV); for (size_t k=0; k<nV; k++) { nd.V[3*k+0] = V[k](0); nd.V[3*k+1] = V[k](1); nd.V[3*k+2] = V[k](2); } // vertex data nd.pole.setDims(1,tcc.pole.length()); for (size_t k=0; k<tcc.pole.length(); k++) nd.pole[k] = tcc.pole[k]==1; nd.corner.setDims(1,tcc.corner.length()); for (size_t k=0; k<tcc.corner.length(); k++) nd.corner[k] = tcc.corner[k]; // halfedge data nd.T.setDims(1,tcc.T.length()); for (size_t k=0; k<tcc.T.length(); k++) nd.T[k] = tcc.T[k]; nd.itv.setDims(1,tcc.itv.length()); for (size_t k=0; k<tcc.itv.length(); k++) nd.itv[k] = tcc.itv[k]; nd.eqc.setDims(1,tcc.eqc.length()); for (size_t k=0; k<tcc.eqc.length(); k++) nd.eqc[k] = tcc.eqc[k]; // err is not needed! nd.nFV.setDims(1,tcc.nFV.length()); for (size_t k=0; k<tcc.nFV.length(); k++) nd.nFV[k] = tcc.nFV[k]; nd.tip.setDims(1,tcc.F.length()); for (size_t k=0; k<tcc.F.length(); k++) nd.tip[k] = tcc.F[k]; nd.selHE.setDims(1,tcc.selHE.length()); for (size_t k=0; k<tcc.selHE.length(); k++) nd.selHE[k] = tcc.selHE[k]; }
bool ToMayaMeshConverter::doConversion( IECore::ConstObjectPtr from, MObject &to, IECore::ConstCompoundObjectPtr operands ) const { MStatus s; IECore::ConstMeshPrimitivePtr mesh = IECore::runTimeCast<const IECore::MeshPrimitive>( from ); assert( mesh ); if ( !mesh->arePrimitiveVariablesValid() ) { return false; } MFloatPointArray vertexArray; MIntArray polygonCounts; MIntArray polygonConnects; MFnMesh fnMesh; int numVertices = 0; IECore::PrimitiveVariableMap::const_iterator it = mesh->variables.find("P"); if ( it != mesh->variables.end() ) { /// \todo Employ some M*Array converters to simplify this IECore::ConstV3fVectorDataPtr p = IECore::runTimeCast<const IECore::V3fVectorData>(it->second.data); if (p) { numVertices = p->readable().size(); vertexArray.setLength( numVertices ); for (int i = 0; i < numVertices; i++) { vertexArray[i] = IECore::convert<MFloatPoint, Imath::V3f>( p->readable()[i] ); } } else { IECore::ConstV3dVectorDataPtr p = IECore::runTimeCast<const IECore::V3dVectorData>(it->second.data); if (p) { numVertices = p->readable().size(); vertexArray.setLength( numVertices ); for (int i = 0; i < numVertices; i++) { vertexArray[i] = IECore::convert<MFloatPoint, Imath::V3d>( p->readable()[i] ); } } else { // "P" is not convertible to an array of "points" return false; } } } IECore::ConstIntVectorDataPtr verticesPerFace = mesh->verticesPerFace(); assert( verticesPerFace ); int numPolygons = verticesPerFace->readable().size(); polygonCounts.setLength( numPolygons ); for (int i = 0; i < numPolygons; i++) { polygonCounts[i] = verticesPerFace->readable()[i]; } IECore::ConstIntVectorDataPtr vertexIds = mesh->vertexIds(); assert( vertexIds ); int numPolygonConnects = vertexIds->readable().size(); polygonConnects.setLength( numPolygonConnects ); for (int i = 0; i < numPolygonConnects; i++) { polygonConnects[i] = vertexIds->readable()[i]; } MObject mObj = fnMesh.create( numVertices, numPolygons, vertexArray, polygonCounts, polygonConnects, to, &s ); if (!s) { return false; } it = mesh->variables.find("N"); if ( it != mesh->variables.end() ) { if (it->second.interpolation == IECore::PrimitiveVariable::FaceVarying ) { /// \todo Employ some M*Array converters to simplify this MVectorArray vertexNormalsArray; IECore::ConstV3fVectorDataPtr n = IECore::runTimeCast<const IECore::V3fVectorData>(it->second.data); if (n) { int numVertexNormals = n->readable().size(); vertexNormalsArray.setLength( numVertexNormals ); for (int i = 0; i < numVertexNormals; i++) { vertexNormalsArray[i] = IECore::convert<MVector, Imath::V3f>( n->readable()[i] ); } } else { IECore::ConstV3dVectorDataPtr n = IECore::runTimeCast<const IECore::V3dVectorData>(it->second.data); if (n) { int numVertexNormals = n->readable().size(); vertexNormalsArray.setLength( numVertexNormals ); for (int i = 0; i < numVertexNormals; i++) { vertexNormalsArray[i] = IECore::convert<MVector, Imath::V3d>( n->readable()[i] ); } } else { IECore::msg( IECore::Msg::Warning, "ToMayaMeshConverter::doConversion", boost::format( "PrimitiveVariable \"N\" has unsupported type \"%s\"." ) % it->second.data->typeName() ); } } if ( vertexNormalsArray.length() ) { MStatus status; MItMeshPolygon itPolygon( mObj, &status ); if( status != MS::kSuccess ) { IECore::msg( IECore::Msg::Warning, "ToMayaMeshConverter::doConversion", "Failed to create mesh iterator" ); } unsigned v = 0; MIntArray vertexIds; MIntArray faceIds; for ( ; !itPolygon.isDone(); itPolygon.next() ) { for ( v=0; v < itPolygon.polygonVertexCount(); ++v ) { faceIds.append( itPolygon.index() ); vertexIds.append( itPolygon.vertexIndex( v ) ); } } if( !fnMesh.setFaceVertexNormals( vertexNormalsArray, faceIds, vertexIds ) ) { IECore::msg( IECore::Msg::Warning, "ToMayaMeshConverter::doConversion", "Setting normals failed" ); } } } else { IECore::msg( IECore::Msg::Warning, "ToMayaMeshConverter::doConversion", "PrimitiveVariable \"N\" has unsupported interpolation (expected FaceVarying)." ); } } bool haveDefaultUVs = false; IECore::PrimitiveVariableMap::const_iterator sIt = mesh->variables.find( "s" ); IECore::RefCountedPtr sDataRef = ( sIt == mesh->variables.end() ) ? 0 : static_cast<IECore::RefCountedPtr>( sIt->second.data ); /// Add named UV sets std::set< std::string > uvSets; for ( it = mesh->variables.begin(); it != mesh->variables.end(); ++it ) { const std::string &sName = it->first; size_t suffixOffset = sName.rfind( "_s" ); if ( ( suffixOffset != std::string::npos) && ( suffixOffset == sName.length() - 2 ) ) { std::string uvSetNameStr = sName.substr( 0, suffixOffset ); if ( uvSetNameStr.size() ) { MString uvSetName = uvSetNameStr.c_str(); std::string tName = uvSetNameStr + "_t"; std::string stIdName = uvSetNameStr + "Indices"; addUVSet( fnMesh, polygonCounts, mesh, sName, tName, stIdName, &uvSetName ); uvSets.insert( uvSetNameStr ); if ( sDataRef == static_cast<IECore::RefCountedPtr>( it->second.data ) ) { haveDefaultUVs = true; } } } } /// Add default UV set if it isn't just a reference to a named set if ( !haveDefaultUVs ) { addUVSet( fnMesh, polygonCounts, mesh, "s", "t", "stIndices" ); } // We do the search again, but looking for primvars ending "_t", so we can catch cases where either "UVSETNAME_s" or "UVSETNAME_t" is present, but not both, taking care // not to attempt adding any duplicate sets for ( it = mesh->variables.begin(); it != mesh->variables.end(); ++it ) { const std::string &tName = it->first; size_t suffixOffset = tName.rfind( "_t" ); if ( ( suffixOffset != std::string::npos) && ( suffixOffset == tName.length() - 2 ) ) { std::string uvSetNameStr = tName.substr( 0, suffixOffset ); if ( uvSetNameStr.size() && uvSets.find( uvSetNameStr ) == uvSets.end() ) { MString uvSetName = uvSetNameStr.c_str(); std::string sName = uvSetNameStr + "_s"; std::string stIdName = uvSetNameStr + "Indices"; addUVSet( fnMesh, polygonCounts, mesh, sName, tName, stIdName, &uvSetName ); uvSets.insert( uvSetNameStr ); } } } /// If we're making a mesh node (rather than a mesh data) then make sure it belongs /// to the default shading group and add the ieMeshInterpolation attribute. MObject oMesh = fnMesh.object(); if( oMesh.apiType()==MFn::kMesh ) { assignDefaultShadingGroup( oMesh ); setMeshInterpolationAttribute( oMesh, mesh->interpolation() ); } /// \todo Other primvars, e.g. vertex color ("Cs") return true; }
// the arrays being passed in are assumed to be empty void MayaMeshWriter::fillTopology( std::vector<float> & oPoints, std::vector<Alembic::Util::int32_t> & oFacePoints, std::vector<Alembic::Util::int32_t> & oPointCounts) { MStatus status = MS::kSuccess; MFnMesh lMesh( mDagPath, &status ); if ( !status ) { MGlobal::displayError( "MFnMesh() failed for MayaMeshWriter" ); } MFloatPointArray pts; lMesh.getPoints(pts); if (pts.length() < 3 && pts.length() > 0) { MString err = lMesh.fullPathName() + " is not a valid mesh, because it only has "; err += pts.length(); err += " points."; MGlobal::displayError(err); return; } unsigned int numPolys = lMesh.numPolygons(); if (numPolys == 0) { MGlobal::displayWarning(lMesh.fullPathName() + " has no polygons."); return; } unsigned int i; int j; oPoints.resize(pts.length() * 3); // repack the float for (i = 0; i < pts.length(); i++) { size_t local = i * 3; oPoints[local] = pts[i].x; oPoints[local+1] = pts[i].y; oPoints[local+2] = pts[i].z; } /* oPoints - oFacePoints - vertex list oPointCounts - number of points per polygon */ MIntArray faceArray; for (i = 0; i < numPolys; i++) { lMesh.getPolygonVertices(i, faceArray); if (faceArray.length() < 3) { MGlobal::displayWarning("Skipping degenerate polygon"); continue; } // write backwards cause polygons in Maya are in a different order // from Renderman (clockwise vs counter-clockwise?) int faceArrayLength = faceArray.length() - 1; for (j = faceArrayLength; j > -1; j--) { oFacePoints.push_back(faceArray[j]); } oPointCounts.push_back(faceArray.length()); } }
// h�mta all n�dv�ndig data och l�gger det i ett MeshData-objekt, som senare anv�nds vid exportering. bool Exporter::ExtractMeshData(MFnMesh &mesh, UINT index) { MeshData mesh_data; MFloatPointArray points; MFloatVectorArray normals; MSpace::Space world_space = MSpace::kTransform; // DAG-path mesh_data.mesh_path = mesh.dagPath(); // namn och id mesh_data.name = mesh.name(); mesh_data.id = index; std::string name = mesh.partialPathName().asChar(); if (!strcmp(name.substr(0, 5).c_str(), "Blend")){ return true; } // triangulera meshen innan man h�mtar punkterna MString command = "polyTriangulate -ch 1 " + mesh_data.name; if (!MGlobal::executeCommand(command)) { return false; } // h�mta icke-indexerade vertexpunkter if (!mesh.getPoints(points, world_space)) { return false; } for (int i = 0; i < points.length(); i++){ point temppoints = { points[i].x, points[i].y, points[i].z }; vec3 temppurepoints = { points[i].x, points[i].y, points[i].z }; mesh_data.points.push_back(temppoints); mesh_data.purepoints.push_back(temppurepoints); } // h�mta icke-indexerade normaler if (!mesh.getNormals(normals, world_space)) { return false; } for (int i = 0; i < normals.length(); i++){ vec3 tempnormals = { normals[i].x, normals[i].y, normals[i].z }; mesh_data.normals.push_back(tempnormals); } //variabler f�r att mellanlagra uvdata och tangenter/bitangenter MStringArray uvSets; mesh.getUVSetNames(uvSets); uvSet tempUVSet; MFloatArray Us; MFloatArray Vs; vec2 UVs; // iterera �ver uvsets och ta ut koordinater, tangenter och bitangenter for (int i = 0; i < uvSets.length(); i++) { MString currentSet = uvSets[i]; mesh.getUVs(Us, Vs, ¤tSet); for (int a = 0; a < Us.length(); a++){ UVs.u = Us[a]; UVs.v = Vs[a]; //1-Vs in order to get correct UV angles tempUVSet.UVs.push_back(UVs); } mesh.getTangents(tempUVSet.tangents, world_space, ¤tSet); mesh.getBinormals(tempUVSet.binormals, world_space, ¤tSet); mesh_data.uvSets.push_back(tempUVSet); } //itererar �ver trianglar och returnerar ID:n f�r associerade vertiser, normaler och uvset MItMeshPolygon itFaces(mesh.dagPath()); while (!itFaces.isDone()) { face tempface; // printf("%d", itFaces.vertexIndex(0)); // printf(" %d", itFaces.vertexIndex(1)); // printf(" %d\n", itFaces.vertexIndex(2)); int vc = itFaces.polygonVertexCount(); for (int i = 0; i < vc; ++i) { tempface.verts[i].pointID = itFaces.vertexIndex(i); tempface.verts[i].normalID = itFaces.normalIndex(i); for (int k = 0; k < uvSets.length(); ++k) { int temptexCoordsID; itFaces.getUVIndex(i, temptexCoordsID, &uvSets[k]); tempface.verts[i].texCoordsID.push_back(temptexCoordsID); } } mesh_data.faces.push_back(tempface); itFaces.next(); } // l�gg till mesh_data i scen-datan scene_.meshes.push_back(mesh_data); return true; }
MStatus sseDeformer::compute(const MPlug& plug, MDataBlock& data) { MStatus status; if (plug.attribute() != outputGeom) { printf("Ignoring requested plug\n"); return status; } unsigned int index = plug.logicalIndex(); MObject thisNode = this->thisMObject(); // get input value MPlug inPlug(thisNode,input); inPlug.selectAncestorLogicalIndex(index,input); MDataHandle hInput = data.inputValue(inPlug, &status); MCheckStatus(status, "ERROR getting input mesh\n"); // get the input geometry MDataHandle inputData = hInput.child(inputGeom); if (inputData.type() != MFnData::kMesh) { printf("Incorrect input geometry type\n"); return MStatus::kFailure; } MObject iSurf = inputData.asMesh() ; MFnMesh inMesh; inMesh.setObject( iSurf ) ; MDataHandle outputData = data.outputValue(plug); outputData.copy(inputData); if (outputData.type() != MFnData::kMesh) { printf("Incorrect output mesh type\n"); return MStatus::kFailure; } MObject oSurf = outputData.asMesh() ; if(oSurf.isNull()) { printf("Output surface is NULL\n"); return MStatus::kFailure; } MFnMesh outMesh; outMesh.setObject( oSurf ) ; MCheckStatus(status, "ERROR setting points\n"); // get all points at once for demo purposes. Really should get points from the current group using iterator MFloatPointArray pts; outMesh.getPoints(pts); int nPoints = pts.length(); MDataHandle envData = data.inputValue(envelope, &status); float env = envData.asFloat(); MDataHandle sseData = data.inputValue(sseEnabled, &status); bool sseEnabled = (bool) sseData.asBool(); // NOTE: Using MTimer and possibly other classes disables // autovectorization with Intel <=10.1 compiler on OSX and Linux!! // Must compile this function with -fno-exceptions on OSX and // Linux to guarantee autovectorization is done. Use -fvec_report2 // to check for vectorization status messages with Intel compiler. MTimer timer; timer.beginTimer(); if(sseEnabled) { // Innter loop will autovectorize. Around 3x faster than the // loop below it. It would be faster if first element was // guaranteed to be aligned on 16 byte boundary. for(int i=0; i<nPoints; i++) { float* ptPtr = &pts[i].x; for(int j=0; j<4; j++) { ptPtr[j] = env * (cosf(ptPtr[j]) * sinf(ptPtr[j]) * tanf(ptPtr[j])); } } } else { // This inner loop will not autovectorize. for(int i=0; i<nPoints; i++) { MFloatPoint& pt = pts[i]; for(int j=0; j<3; j++) { pt[j] = env * (cosf(pt[j]) * sinf(pt[j]) * tanf(pt[j])); } } } timer.endTimer(); if(sseEnabled) { printf("SSE enabled, runtime %f\n", timer.elapsedTime()); } else { printf("SSE disabled, runtime %f\n", timer.elapsedTime()); } outMesh.setPoints(pts); return status; }
MStatus meshOpFty::doLightningSplit(MFnMesh& meshFn) // // Description: // Performs the kSplitLightning operation on the selected mesh // and components. It may not split all the selected components. // { unsigned int i, j; // These are the input arrays to the split function. The following // algorithm fills them in with the arguments for a continuous // split that goes through some of the selected faces. // MIntArray placements; MIntArray edgeIDs; MFloatArray edgeFactors; MFloatPointArray internalPoints; // The following array is going to be used to determine which faces // have been split. Since the split function can only split faces // which are adjacent to the earlier face, we may not split // all the faces // bool* faceTouched = new bool[fComponentIDs.length()]; for (i = 0; i < fComponentIDs.length(); ++i) faceTouched[i] = false; // We need a starting point. For this example, the first face in // the component list is picked. Also get a polygon iterator // to this face. // MItMeshPolygon itPoly(fMesh); for (; !itPoly.isDone(); itPoly.next()) { if (fComponentIDs[0] == itPoly.index()) break; } if (itPoly.isDone()) { // Should never happen. // delete [] faceTouched; return MS::kFailure; } // In this example, edge0 is called the starting edge and // edge1 is called the destination edge. This algorithm will split // each face from the starting edge to the destination edge // while going through two inner points inside each face. // int edge0, edge1; MPoint innerVert0, innerVert1; int nextFaceIndex = 0; // We need a starting edge. For this example, the first edge in the // edge list is used. // MIntArray edgeList; itPoly.getEdges(edgeList); edge0 = edgeList[0]; bool done = false; while (!done) { // Set this face as touched so that we don't try to split it twice // faceTouched[nextFaceIndex] = true; // Get the current face's center. It is used later in the // algorithm to calculate inner vertices. // MPoint faceCenter = itPoly.center(); // Iterate through the connected faces to find an untouched, // selected face and get the ID of the shared edge. That face // will become the next face to be split. // MIntArray faceList; itPoly.getConnectedFaces(faceList); nextFaceIndex = -1; for (i = 0; i < fComponentIDs.length(); ++i) { for (j = 0; j < faceList.length(); ++j) { if (fComponentIDs[i] == faceList[j] && !faceTouched[i]) { nextFaceIndex = i; break; } } if (nextFaceIndex != -1) break; } if (nextFaceIndex == -1) { // There is no selected and untouched face adjacent to this // face, so this algorithm is done. Pick the first edge that // is not the starting edge as the destination edge. // done = true; edge1 = -1; for (i = 0; i < edgeList.length(); ++i) { if (edgeList[i] != edge0) { edge1 = edgeList[i]; break; } } if (edge1 == -1) { // This should not happen, since there should be more than // one edge for each face // delete [] faceTouched; return MS::kFailure; } } else { // The next step is to find out which edge is shared between // the two faces and use it as the destination edge. To do // that, we need to iterate through the faces and get the // next face's list of edges. // itPoly.reset(); for (; !itPoly.isDone(); itPoly.next()) { if (fComponentIDs[nextFaceIndex] == itPoly.index()) break; } if (itPoly.isDone()) { // Should never happen. // delete [] faceTouched; return MS::kFailure; } // Look for a common edge ID in the two faces edge lists // MIntArray nextFaceEdgeList; itPoly.getEdges(nextFaceEdgeList); edge1 = -1; for (i = 0; i < edgeList.length(); ++i) { for (j = 0; j < nextFaceEdgeList.length(); ++j) { if (edgeList[i] == nextFaceEdgeList[j]) { edge1 = edgeList[i]; break; } } if (edge1 != -1) break; } if (edge1 == -1) { // Should never happen. // delete [] faceTouched; return MS::kFailure; } // Save the edge list for the next iteration // edgeList = nextFaceEdgeList; } // Calculate the two inner points that the split will go through. // For this example, the midpoints between the center and the two // farthest vertices of the edges are used. // // Find the 3D positions of the edges' vertices // MPoint edge0vert0, edge0vert1, edge1vert0, edge1vert1; MItMeshEdge itEdge(fMesh, MObject::kNullObj ); for (; !itEdge.isDone(); itEdge.next()) { if (itEdge.index() == edge0) { edge0vert0 = itEdge.point(0); edge0vert1 = itEdge.point(1); } if (itEdge.index() == edge1) { edge1vert0 = itEdge.point(0); edge1vert1 = itEdge.point(1); } } // Figure out which are the farthest from each other // double distMax = edge0vert0.distanceTo(edge1vert0); MPoint max0, max1; max0 = edge0vert0; max1 = edge1vert0; double newDist = edge0vert1.distanceTo(edge1vert0); if (newDist > distMax) { max0 = edge0vert1; max1 = edge1vert0; distMax = newDist; } newDist = edge0vert0.distanceTo(edge1vert1); if (newDist > distMax) { max0 = edge0vert0; max1 = edge1vert1; distMax = newDist; } newDist = edge0vert1.distanceTo(edge1vert1); if (newDist > distMax) { max0 = edge0vert1; max1 = edge1vert1; } // Calculate the two inner points // innerVert0 = (faceCenter + max0) / 2.0; innerVert1 = (faceCenter + max1) / 2.0; // Add this split's information to the input arrays. If this is // the last split, also add the destination edge's split information. // placements.append((int) MFnMesh::kOnEdge); placements.append((int) MFnMesh::kInternalPoint); placements.append((int) MFnMesh::kInternalPoint); if (done) placements.append((int) MFnMesh::kOnEdge); edgeIDs.append(edge0); if (done) edgeIDs.append(edge1); edgeFactors.append(0.5f); if (done) edgeFactors.append(0.5f); MFloatPoint point1((float)innerVert0[0], (float)innerVert0[1], (float)innerVert0[2], (float)innerVert0[3]); MFloatPoint point2((float)innerVert1[0], (float)innerVert1[1], (float)innerVert1[2], (float)innerVert1[3]); internalPoints.append(point1); internalPoints.append(point2); // For the next iteration, the current destination // edge becomes the start edge. // edge0 = edge1; } // Release the dynamically-allocated memory and do the actual split // delete [] faceTouched; return meshFn.split(placements, edgeIDs, edgeFactors, internalPoints); }
bool preFrame( const MObject hairSystem, const double curTime, void **privateData ) { MStatus status; // If you need want to perform any preprocessing on your collision // objects pre-frame, do it here. One option for storing the pre- // processed data is on a typed attribute on the hairSystem node. // That data could be fetched and updated here. // // In our example, we'll just compute a bounding box here and NOT use // attribute storage. That is an exercise for the reader. // MFnDependencyNode fnHairSystem( hairSystem, &status ); CHECK_MSTATUS_AND_RETURN( status, false ); fprintf( stderr, "preFrame: calling hairSystem node=`%s', time=%g\n", fnHairSystem.name().asChar(), curTime ); MObjectArray cols; MIntArray logIdxs; CHECK_MSTATUS_AND_RETURN( MHairSystem::getCollisionObject( hairSystem, cols, logIdxs ), false ); int nobj = cols.length(); // Allocate private data. // This allows us to pre-process data on a pre-frame basis to avoid // calculating it per hair inside the collide() call. As noted earlier // we could allocate this in preFrame() and hang it off the hairSystem // node via a dynamic attribute. // Instead we'll allocate it here. // COLLISION_INFO *collisionInfo = (COLLISION_INFO *) malloc( sizeof( COLLISION_INFO ) ); collisionInfo->objs = (COLLISION_OBJ *) malloc( nobj * sizeof( COLLISION_OBJ ) ); collisionInfo->numObjs = nobj; // Create the private data that we'll make available to the collide // method. The data should actually be stored in a way that it can // be cleaned up (such as storing the pointer on the hairSystem node // using a dynamic attribute). As it stands right now, there is a // memory leak with this plug-in because the memory we're allocating // for the private data is never cleaned up. // // Note that when using the dynamic attribute approach, it is still // wise to set *privateData because this avoids the need to look up // the plug inside the collide() routine which is a high-traffic // method. // *privateData = (void *) collisionInfo; // Loop through the collision objects and pre-process, storing the // results in the collisionInfo structure. // int obj; for ( obj = 0; obj < nobj; ++obj ) { // Get the ith collision geometry we are connected to. // MObject colObj = cols[obj]; // Get the DAG path for the collision object so we can transform // the vertices to world space. // MFnDagNode fnDagNode( colObj, &status ); CHECK_MSTATUS_AND_RETURN( status, false ); MDagPath path; status = fnDagNode.getPath( path ); CHECK_MSTATUS_AND_RETURN( status, false ); MFnMesh fnMesh( path, &status ); if ( MS::kSuccess != status ) { fprintf( stderr, "%s:%d: collide was not passed a valid mesh shape\n", __FILE__, __LINE__ ); return( false ); } // Get the vertices of the object transformed to world space. // MFloatPointArray verts; status = fnMesh.getPoints( verts, MSpace::kWorld ); CHECK_MSTATUS_AND_RETURN( status, false ); // Compute the bounding box for the collision object. // As this is a quick and dirty demo, we'll just support collisions // between hair and the bbox. // double minx, miny, minz, maxx, maxy, maxz, x, y, z; minx = maxx = verts[0].x; miny = maxy = verts[0].y; minz = maxz = verts[0].z; int nv = verts.length(); int i; for ( i = 1; i < nv; ++i ) { x = verts[i].x; y = verts[i].y; z = verts[i].z; if ( x < minx ) { minx = x; } if ( y < miny ) { miny = y; } if ( z < minz ) { minz = z; } if ( x > maxx ) { maxx = x; } if ( y > maxy ) { maxy = y; } if ( z > maxz ) { maxz = z; } } // Store this precomputed informantion into our private data // structure. // collisionInfo->objs[obj].numVerts = nv; collisionInfo->objs[obj].minx = minx; collisionInfo->objs[obj].miny = miny; collisionInfo->objs[obj].minz = minz; collisionInfo->objs[obj].maxx = maxx; collisionInfo->objs[obj].maxy = maxy; collisionInfo->objs[obj].maxz = maxz; fprintf( stderr, "Inside preFrameInit, bbox=%g %g %g %g %g %g\n", minx,miny,minz,maxx,maxy,maxz); } return( true ); }
//---------------------------------------------------------------------------------------------------------------------- void OceanNode::createGrid(int _resolution, double _time, double _choppiness, MObject& _outputData, MStatus &_status){ int numTris = (_resolution-1)*(_resolution-1)*2; MFloatPointArray vertices; MIntArray numFaceVertices; MIntArray faceVertices; int tris[numTris*3]; int width = 500; int depth = 500; // calculate the deltas for the x,z values of our point float wStep=(float)width/(float)_resolution; float dStep=(float)depth/(float)_resolution; // now we assume that the grid is centered at 0,0,0 so we make // it flow from -w/2 -d/2 float xPos=-((float)width/2.0); float zPos=-((float)depth/2.0); // now loop from top left to bottom right and generate points m_ocean->update(_time); float2* heights = m_ocean->getHeights(); float2* chopXArray = m_ocean->getChopX(); float2* chopZArray = m_ocean->getChopZ(); // Sourced form Jon Macey's NGL library for(int z=0; z<_resolution; z++){ for(int x=0; x<_resolution; x++){ // Divide the values we get out of the FFT by 50000 to get them in a suitable range float height = heights[z * _resolution + x].x/50000.0; float chopX = _choppiness * chopXArray[z * _resolution + x].x/50000.0; float chopZ= _choppiness * chopZArray[z * _resolution + x].x/50000.0; int sign = 1.0; if ((x+z) % 2 != 0){ sign = -1.0; } vertices.append((xPos + (chopX * sign)), height * sign, (zPos + (chopZ * sign))); // calculate the new position zPos+=dStep; } // now increment to next z row xPos+=wStep; // we need to re-set the xpos for new row zPos=-((float)depth/2.0); } // Array for num vertices in each face for (int i=0; i<numTris; i++){ numFaceVertices.append(3); } // Assign vertices to each face int fidx = 0; for (int i=0; i<(_resolution-1); i++){ for (int j=0; j<(_resolution-1); j++){ tris[fidx*3+0] = (i+1)*_resolution+j; tris[fidx*3+1] = i*_resolution+j+1; tris[fidx*3+2] = i*_resolution+j; fidx++; tris[fidx*3+0] = (i+1)*_resolution+j; tris[fidx*3+1] = (i+1)*_resolution+j+1; tris[fidx*3+2] = i*_resolution+j+1; fidx++; } } for (uint i=0; i<sizeof(tris)/sizeof(int); i++){ faceVertices.append(tris[i]); } MFnMesh grid; grid.create(vertices.length(), numTris, vertices, numFaceVertices, faceVertices, _outputData, &_status); }
collision_shape_t::pointer collisionShapeNode::createCollisionShape(const MObject& node) { collision_shape_t::pointer collision_shape = 0; MObject thisObject(thisMObject()); MPlug plgType(thisObject, ia_type); int type; plgType.getValue(type); switch(type) { case 0: { //convex hull { if(node.hasFn(MFn::kMesh)) { MDagPath dagPath; MDagPath::getAPathTo(node, dagPath); MFnMesh fnMesh(dagPath); MFloatPointArray mpoints; MFloatVectorArray mnormals; MIntArray mtrianglecounts; MIntArray mtrianglevertices; fnMesh.getPoints(mpoints, MSpace::kObject); fnMesh.getNormals(mnormals, MSpace::kObject); fnMesh.getTriangles(mtrianglecounts, mtrianglevertices); std::vector<vec3f> vertices(mpoints.length()); std::vector<vec3f> normals(mpoints.length()); std::vector<unsigned int> indices(mtrianglevertices.length()); btAlignedObjectArray<btVector3> btVerts; //mb for(size_t i = 0; i < vertices.size(); ++i) { vertices[i] = vec3f(mpoints[i].x, mpoints[i].y, mpoints[i].z); normals[i] = vec3f(mnormals[i].x, mnormals[i].y, mnormals[i].z); #if UPDATE_SHAPE //future collision margin adjust btVerts.push_back(btVector3(mpoints[i].x, mpoints[i].y, mpoints[i].z)); //mb #endif } for(size_t i = 0; i < indices.size(); ++i) { indices[i] = mtrianglevertices[i]; } #if UPDATE_SHAPE //future collision margin adjust btAlignedObjectArray<btVector3> planeEquations; btGeometryUtil::getPlaneEquationsFromVertices(btVerts, planeEquations); btAlignedObjectArray<btVector3> shiftedPlaneEquations; for (int p=0;p<planeEquations.size();p++) { btVector3 plane = planeEquations[p]; plane[3] += collisionShapeNode::collisionMarginOffset; shiftedPlaneEquations.push_back(plane); } btAlignedObjectArray<btVector3> shiftedVertices; btGeometryUtil::getVerticesFromPlaneEquations(shiftedPlaneEquations, shiftedVertices); std::vector<vec3f> shiftedVerticesVec3f(shiftedVertices.size()); for(size_t i = 0; i < shiftedVertices.size(); ++i) { shiftedVerticesVec3f[i] = vec3f(shiftedVertices[i].getX(), shiftedVertices[i].getY(), shiftedVertices[i].getZ()); //std::cout << "orig verts: " << vertices[i][0] << " " << vertices[i][1] << " " << vertices[i][2] << std::endl; //std::cout << "shft verts: " << shiftedVertices[i].getX() << " " << shiftedVertices[i].getY() << " " << shiftedVertices[i].getZ() << std::endl; //std::cout << std::endl; } collision_shape = solver_t::create_convex_hull_shape(&(shiftedVerticesVec3f[0]), shiftedVerticesVec3f.size(), &(normals[0]), &(indices[0]), indices.size()); #endif #if UPDATE_SHAPE == 0 collision_shape = solver_t::create_convex_hull_shape(&(vertices[0]), vertices.size(), &(normals[0]), &(indices[0]), indices.size()); //original #endif } } } break; case 1: { //mesh { if(node.hasFn(MFn::kMesh)) { MDagPath dagPath; MDagPath::getAPathTo(node, dagPath); MFnMesh fnMesh(dagPath); MFloatPointArray mpoints; MFloatVectorArray mnormals; MIntArray mtrianglecounts; MIntArray mtrianglevertices; fnMesh.getPoints(mpoints, MSpace::kObject); fnMesh.getNormals(mnormals, MSpace::kObject); fnMesh.getTriangles(mtrianglecounts, mtrianglevertices); std::vector<vec3f> vertices(mpoints.length()); std::vector<vec3f> normals(mpoints.length()); std::vector<unsigned int> indices(mtrianglevertices.length()); for(size_t i = 0; i < vertices.size(); ++i) { vertices[i] = vec3f(mpoints[i].x, mpoints[i].y, mpoints[i].z); normals[i] = vec3f(mnormals[i].x, mnormals[i].y, mnormals[i].z); } for(size_t i = 0; i < indices.size(); ++i) { indices[i] = mtrianglevertices[i]; } bool dynamicMesh = true; collision_shape = solver_t::create_mesh_shape(&(vertices[0]), vertices.size(), &(normals[0]), &(indices[0]), indices.size(),dynamicMesh); } } } break; case 2: //cylinder break; case 3: //capsule break; case 7: //btBvhTriangleMeshShape { if(node.hasFn(MFn::kMesh)) { MDagPath dagPath; MDagPath::getAPathTo(node, dagPath); MFnMesh fnMesh(dagPath); MFloatPointArray mpoints; MFloatVectorArray mnormals; MIntArray mtrianglecounts; MIntArray mtrianglevertices; fnMesh.getPoints(mpoints, MSpace::kObject); fnMesh.getNormals(mnormals, MSpace::kObject); fnMesh.getTriangles(mtrianglecounts, mtrianglevertices); std::vector<vec3f> vertices(mpoints.length()); std::vector<vec3f> normals(mpoints.length()); std::vector<unsigned int> indices(mtrianglevertices.length()); for(size_t i = 0; i < vertices.size(); ++i) { vertices[i] = vec3f(mpoints[i].x, mpoints[i].y, mpoints[i].z); normals[i] = vec3f(mnormals[i].x, mnormals[i].y, mnormals[i].z); } for(size_t i = 0; i < indices.size(); ++i) { indices[i] = mtrianglevertices[i]; } bool dynamicMesh = false; collision_shape = solver_t::create_mesh_shape(&(vertices[0]), vertices.size(), &(normals[0]), &(indices[0]), indices.size(),dynamicMesh); } } break; case 8: //hacd convex decomposition { { if(node.hasFn(MFn::kMesh)) { MDagPath dagPath; MDagPath::getAPathTo(node, dagPath); MFnMesh fnMesh(dagPath); MFloatPointArray mpoints; MFloatVectorArray mnormals; MIntArray mtrianglecounts; MIntArray mtrianglevertices; fnMesh.getPoints(mpoints, MSpace::kObject); fnMesh.getNormals(mnormals, MSpace::kObject); fnMesh.getTriangles(mtrianglecounts, mtrianglevertices); std::vector<vec3f> vertices(mpoints.length()); std::vector<vec3f> normals(mpoints.length()); std::vector<unsigned int> indices(mtrianglevertices.length()); for(size_t i = 0; i < vertices.size(); ++i) { vertices[i] = vec3f(mpoints[i].x, mpoints[i].y, mpoints[i].z); normals[i] = vec3f(mnormals[i].x, mnormals[i].y, mnormals[i].z); } for(size_t i = 0; i < indices.size(); ++i) { indices[i] = mtrianglevertices[i]; } bool dynamicMesh = false; collision_shape = solver_t::create_hacd_shape(&(vertices[0]), vertices.size(), &(normals[0]), &(indices[0]), indices.size(),dynamicMesh); } } } break; default: { } } return collision_shape; }
// // Recursively traverse a mesh by processing each face, and the neighbouring faces along it's edges. // The initial invocation of this routine provides the basis for the new first vertex/edge // and faces. // // The result of this routine is an array of values that map the old CV indices to the new ones. Along // the a new list of reindexed CVs is built, along with a list of poly counts and connetions. These // can be used to build a new mesh with the reordering specfied by the seed face and vertices. // // // Inputs: // path : Path to the object being traversed // faceIdx : Current face being traversed // v0, v1 : Veretices that define the direction of travel along the face // faceTraversal : An array booleans to track which faces have been // : traversed, controls the recursion // origVertices : The vertices from the original mesh. The could be obtained // : from the path, but are passed in for efficiency // // Outputs: // cvMapping : Mapping of the existing vertices to their new indices // : the fist values in the final array will be the intial v0, v1 // cvMappingInverse : The inverse of the cvMapping // : the value of items v0 and v1 will be 0 and 1 respectively // newPolygonCounts : Vertex counts for each of the new faces // newPolygonConnects : Connections, specified in terms of new CV indices // newVertices : The orginal vertices resorted based on the reindexing // // MStatus meshMapUtils::traverseFace( MDagPath& path, int faceIdx, int v0, int v1, MIntArray& faceTraversal, MIntArray& cvMapping, MIntArray& cvMappingInverse, MIntArray& newPolygonCounts, MIntArray& newPolygonConnects, MFloatPointArray& origVertices, MFloatPointArray& newVertices ) { int vtxCnt = -1; int dir = 0; int dummy; // For setIndex calls MStatus stat = MStatus::kSuccess; MFnMesh theMesh( path, &stat ); MItMeshPolygon polyIt( path ); MItMeshEdge edgeIt( path ); if( stat != MStatus::kSuccess ) { MGlobal::displayError( " theMesh.getPoint failed"); return stat; } // // Skip over any faces already processed, this is not a failure // if( faceTraversal[faceIdx] ) { return MStatus::kSuccess; } // // get the vertex/edge information and sort it based on the user seed // MIntArray vtxOrig; MIntArray edgeOrig; polyIt.setIndex( faceIdx, dummy ); polyIt.getEdges( edgeOrig ); polyIt.getVertices( vtxOrig ); vtxCnt = vtxOrig.length(); // // the sorted V/E info // MIntArray vtxSorted( vtxCnt ); MIntArray edgeSorted( vtxCnt ); // // Build a new array ordered with v0, then v1, first figure out the // starting point, and direction // int v0Idx = -1; int i; for( i = 0; i < vtxCnt; i++ ) { if( vtxOrig[i] == v0 ) { // We've found v0, now find in what direction we need to travel to find v1 v0Idx = i; if( vtxOrig[IDX(i+1, vtxCnt)] == v1 ) { dir = 1; } else if( vtxOrig[IDX(i-1, vtxCnt)] == v1 ) { dir = -1; } break; } } if (dir == 0) { MGlobal::displayError("Selected vertices are not adjacent"); return MS::kFailure; } // Now sort the vertex/edge arrays for( i = 0; i < vtxCnt; i++ ) { vtxSorted[i] = vtxOrig[IDX( v0Idx + i * dir, vtxCnt )]; if( dir == 1 ) { edgeSorted[i] = edgeOrig[IDX( v0Idx + i * dir, vtxCnt )]; } else { edgeSorted[i] = edgeOrig[IDX( v0Idx - 1 + i * dir, vtxCnt )]; } } // Add any new CVs to the vertex array being constructed for ( i = 0; i < vtxCnt; i++ ) { MPoint pos; int index = vtxSorted[i]; if( cvMapping[index] == -1 ) { if( stat != MStatus::kSuccess ) { MGlobal::displayError( " theMesh.getPoint failed"); return stat; } // Added the new CV, and mark it as transferred newVertices.append( origVertices[index] ); // Store the mapping from the old CV indices to the new ones cvMapping[index] = newVertices.length()-1; cvMappingInverse[newVertices.length()-1] = index; } } // // Add the new face count // newPolygonCounts.append( vtxCnt ); // // Add the new polyConnects for ( i = 0; i < vtxCnt; i++ ) { newPolygonConnects.append( cvMapping[vtxSorted[i]] ); } // Mark this face as complete faceTraversal[faceIdx] = true; // // Now recurse over the edges of this face // for( i = 0; i < (int)edgeSorted.length(); i++ ) { int nextEdge = edgeSorted[i]; int2 nextEdgeVtx; stat = theMesh.getEdgeVertices(nextEdge, nextEdgeVtx ); // // Find the vertex, in the sorted array, that starts the next edge int baseIdx = -1; bool swap = false; int j; for( j = 0; j < (int)vtxSorted.length(); j++ ) { if( vtxSorted[j] == nextEdgeVtx[0] ) { baseIdx = j; break; } } assert( baseIdx != -1 ); // // Now look forward and backward in the vertex array to find the // edge's other point, this indicates the edges direction. This // is needed to guide the next recursion level, and keep the // normals pointed consistenly // if( vtxSorted[IDX(baseIdx+1, vtxCnt)] == nextEdgeVtx[1] ) { // Nothing } else if ( vtxSorted[IDX(baseIdx-1, vtxCnt)] == nextEdgeVtx[1] ) { swap = true; } MIntArray connectedFaces; edgeIt.setIndex( nextEdge, dummy ); edgeIt.getConnectedFaces( connectedFaces ); // A single face is simply the current one. Recurse over the others if( connectedFaces.length() > 1 ) { int nextFace; if( connectedFaces[0] == faceIdx ) { nextFace = connectedFaces[1]; } else { nextFace = connectedFaces[0]; } int nextVtx0 = -1; int nextVtx1 = -1; if ( !swap ) { nextVtx0 = nextEdgeVtx[1]; nextVtx1 = nextEdgeVtx[0]; } else { nextVtx0 = nextEdgeVtx[0]; nextVtx1 = nextEdgeVtx[1]; } stat = traverseFace( path, nextFace, nextVtx0, nextVtx1, faceTraversal, cvMapping, cvMappingInverse, newPolygonCounts, newPolygonConnects, origVertices, newVertices ); // Break out of edge loop on error if( stat != MStatus::kSuccess ) { break; } } } return stat; }
MObject ClothSimMayaPlugin::createMesh(const MTime& time, MObject& outData, MStatus& stat) { double t = time.as(MTime::kSeconds); if (t <= 1.0 / 24 && m_prevTime > 1.0/24) { m_simMesh.reset(0); } int nx = 60; int ny = 60; if (!m_simMesh.get()) { Eigen::VectorXf v((nx+1) * (ny+1) * 3); Eigen::VectorXf x((nx + 1) * (ny + 1) * 3); Eigen::VectorXf uv((nx + 1) * (ny + 1) * 2); for (int i = 0; i <= nx; ++i) { for (int j = 0; j <= ny; ++j) { int base = i + (nx+1) * j; uv[2 * base + 0] = (float)i / nx - 0.5f; uv[2 * base + 1] = (float)j / ny - 0.5f; x[3 * base + 0] = uv[2 * base + 0]; x[3 * base + 1] = 0; x[3 * base + 2] = uv[2 * base + 1]; v[3 * base + 0] = v[3 * base + 1] = v[3 * base + 2] = 0; } } std::vector<int> triangleInds; for (int i = 0; i < nx; ++i) { for (int j = 0; j < ny; ++j) { int base = i + (nx + 1) * j; triangleInds.push_back(base + 0); triangleInds.push_back(base + 1); triangleInds.push_back(base + (nx + 1)); triangleInds.push_back(base + 1); triangleInds.push_back(base + (nx + 2)); triangleInds.push_back(base + (nx + 1)); } } m_simMesh.reset( new ClothMesh<float>( x, v, uv, triangleInds, 0.01f, 1000000.0f, 1000000.0f, 0.01f, 1000.0f, 1000.0f, 1.0f ) ); } std::vector<int> constraintIndices; std::vector< Eigen::Matrix3f > constraintMatrices; Eigen::VectorXf constraintVelocityDeltas(m_simMesh->x().size()); constraintVelocityDeltas.setConstant(0); for (int i = 0; i <= nx; ++i) { for (int j = 0; j <= ny; ++j) { int idx = i + (nx + 1) * j; float x = (float)i / nx - 0.5f; float y = (float)j / ny - 0.5f; if (x * x + y * y < 0.3 * 0.3) { constraintIndices.push_back(idx); constraintMatrices.push_back(Eigen::Matrix3f::Zero()); } } } if (t > m_prevTime) { ConstrainedCGSolver<float> solver( constraintIndices, constraintMatrices, constraintVelocityDeltas, 0.01f, 400 ); GravityField<float> g( m_simMesh->m(), Eigen::Vector3f( 0,-9.8f, 0 ) ); std::vector< ForceField<float>* > forceFields; forceFields.push_back( &g ); try { std::cerr << "advance" << std::endl; m_simMesh->advance(forceFields, float(t - m_prevTime)*0.5f, solver); m_simMesh->advance(forceFields, float(t - m_prevTime)*0.5f, solver); std::cerr << "done" << std::endl; } catch (const std::exception &e) { std::cerr << e.what() << std::endl; stat = MStatus::kFailure; return MObject(); } catch (...) { std::cerr << "unknown exception" << std::endl; stat = MStatus::kFailure; return MObject(); } } m_prevTime = t; MFloatPointArray points; for (int i = 0; i < m_simMesh->x().size(); i += 3) { MFloatPoint p(m_simMesh->x()[i], m_simMesh->x()[i + 1], m_simMesh->x()[i + 2]); points.append(p); } MFnMesh meshFS; MIntArray faceCounts((int)m_simMesh->triangleIndices().size()/3, 3); MIntArray faceConnects; for (unsigned i = 0; i < m_simMesh->triangleIndices().size(); ++i) { faceConnects.append(m_simMesh->triangleIndices()[i]); } MObject newMesh = meshFS.create((int)m_simMesh->x().size() / 3, (int)m_simMesh->triangleIndices().size() / 3, points, faceCounts, faceConnects, outData, &stat); return newMesh; }
void readPoly(double iFrame, MFnMesh & ioMesh, MObject & iParent, PolyMeshAndFriends & iNode, bool iInitialized) { Alembic::AbcGeom::IPolyMeshSchema schema = iNode.mMesh.getSchema(); Alembic::AbcGeom::MeshTopologyVariance ttype = schema.getTopologyVariance(); Alembic::AbcCoreAbstract::index_t index, ceilIndex; double alpha = getWeightAndIndex(iFrame, schema.getTimeSampling(), schema.getNumSamples(), index, ceilIndex); MFloatPointArray pointArray; Alembic::Abc::P3fArraySamplePtr ceilPoints; // we can just read the points if (ttype != Alembic::AbcGeom::kHeterogenousTopology && iInitialized) { Alembic::Abc::P3fArraySamplePtr points = schema.getPositionsProperty( ).getValue(Alembic::Abc::ISampleSelector(index)); if (alpha != 0.0) { ceilPoints = schema.getPositionsProperty().getValue( Alembic::Abc::ISampleSelector(ceilIndex) ); } fillPoints(pointArray, points, ceilPoints, alpha); if(pointArray.length() > 0) { ioMesh.setPoints(pointArray, MSpace::kObject); } setColorsAndUVs(iFrame, ioMesh, schema.getUVsParam(), iNode.mV2s, iNode.mC3s, iNode.mC4s, !iInitialized); if (schema.getNormalsParam().getNumSamples() > 1) { setPolyNormals(iFrame, ioMesh, schema.getNormalsParam()); } return; } // we need to read the topology Alembic::AbcGeom::IPolyMeshSchema::Sample samp; schema.get(samp, Alembic::Abc::ISampleSelector(index)); if (alpha != 0.0 && ttype != Alembic::AbcGeom::kHeterogenousTopology) { ceilPoints = schema.getPositionsProperty().getValue( Alembic::Abc::ISampleSelector(ceilIndex) ); } fillPoints(pointArray, samp.getPositions(), ceilPoints, alpha); fillTopology(ioMesh, iParent, pointArray, samp.getFaceIndices(), samp.getFaceCounts()); setPolyNormals(iFrame, ioMesh, schema.getNormalsParam()); setColorsAndUVs(iFrame, ioMesh, schema.getUVsParam(), iNode.mV2s, iNode.mC3s, iNode.mC4s, !iInitialized); }
MStatus intersectCmd::doIt(const MArgList& args) // Description: // Determine if the ray from the spotlight intersects the mesh. // If it does, display the intersection points. { MStatus stat = MStatus::kSuccess; if (args.length() != 2) { MGlobal::displayError("Need 2 items!"); return MStatus::kFailure; } MSelectionList activeList; int i; for ( i = 0; i < 2; i++) { MString strCurrSelection; stat = args.get(i, strCurrSelection); if (MStatus::kSuccess == stat) activeList.add(strCurrSelection); } MItSelectionList iter(activeList); MFnSpotLight fnLight; MFnMesh fnMesh; MFnDagNode dagNod; MFnDependencyNode fnDN; float fX = 0; float fY = 0; float fZ = 0; for ( ; !iter.isDone(); iter.next() ) { MObject tempObjectParent, tempObjectChild; iter.getDependNode(tempObjectParent); if (tempObjectParent.apiType() == MFn::kTransform) { dagNod.setObject(tempObjectParent); tempObjectChild = dagNod.child(0, &stat); } // check what type of object is selected if (tempObjectChild.apiType() == MFn::kSpotLight) { MDagPath pathToLight; MERR_CHK(MDagPath::getAPathTo(tempObjectParent, pathToLight), "Couldn't get a path to the spotlight"); MERR_CHK(fnLight.setObject(pathToLight), "Failure on assigning light"); stat = fnDN.setObject(tempObjectParent); MPlug pTempPlug = fnDN.findPlug("translateX", &stat); if (MStatus::kSuccess == stat) { pTempPlug.getValue(fX); } pTempPlug = fnDN.findPlug("translateY", &stat); if (MStatus::kSuccess == stat) { pTempPlug.getValue(fY); } pTempPlug = fnDN.findPlug("translateZ", &stat); if (MStatus::kSuccess == stat) { pTempPlug.getValue(fZ); } } else if (tempObjectChild.apiType() == MFn::kMesh) { MDagPath pathToMesh; MERR_CHK(MDagPath::getAPathTo(tempObjectChild, pathToMesh), "Couldn't get a path to the spotlight"); MERR_CHK(fnMesh.setObject(pathToMesh), "Failure on assigning light"); } else { MGlobal::displayError("Need a spotlight and a mesh"); return MStatus::kFailure; } } MFloatPoint fpSource(fX, fY, fZ); MFloatVector fvRayDir = fnLight.lightDirection(0, MSpace::kWorld, &stat); MFloatPoint hitPoint; MMeshIsectAccelParams mmAccelParams = fnMesh.autoUniformGridParams(); float fHitRayParam, fHitBary1, fHitBary2; int nHitFace, nHitTriangle; // a large positive number is used here for the maxParam parameter bool bAnyIntersection = fnMesh.anyIntersection(fpSource, fvRayDir, NULL, NULL, false, MSpace::kWorld, (float)9999, false, &mmAccelParams, hitPoint, &fHitRayParam, &nHitFace, &nHitTriangle, &fHitBary1, &fHitBary2, (float)1e-6, &stat); if (! bAnyIntersection) { MGlobal::displayInfo("There were no intersection points detected"); return stat; } MFloatPointArray hitPoints; MFloatArray faHitRayParams; MIntArray iaHitFaces; MIntArray iaHitTriangles; MFloatArray faHitBary1; MFloatArray faHitBary2; bool bAllIntersections = fnMesh.allIntersections(fpSource, fvRayDir, NULL, NULL, false, MSpace::kWorld, 9999, false, NULL, false, hitPoints, &faHitRayParams, &iaHitFaces, &iaHitTriangles, &faHitBary1, &faHitBary2, 0.000001f, &stat); if (! bAllIntersections) { MGlobal::displayInfo("Error getting all intersections"); return stat; } // check how many intersections are found unsigned int nNumberHitPoints = hitPoints.length(); if (! nNumberHitPoints) { MGlobal::displayInfo("No hit points detected"); return MStatus::kSuccess; } // Intersection exists; display intersections as spheres MString strCommandString = "string $strBall[] = `polySphere -r 0.5`;"; strCommandString += "$strBallName = $strBall[0];"; float x = 0; float y = 0; float z = 0; for (i = 0; i < (int)nNumberHitPoints; i++) { // get the points x = hitPoints[i][0]; y = hitPoints[i][1]; z = hitPoints[i][2]; // execute some MEL to create a small sphere strCommandString += "setAttr ($strBallName + \".tx\") "; strCommandString += x; strCommandString += ";"; strCommandString += "setAttr ($strBallName + \".ty\") "; strCommandString += y; strCommandString += ";"; strCommandString += "setAttr ($strBallName + \".tz\") "; strCommandString += z; strCommandString += ";"; MGlobal::executeCommand(strCommandString); } return stat; }
MObject blindDataMesh::createMesh(long seed, MObject& outData, MStatus& stat) { MFloatPointArray vertices; MIntArray faceDegrees; MIntArray faceVertices; int i, j; srand(seed); float planeSize = 20.0f; float planeOffset = planeSize / 2.0f; float planeDim = 0.5f; int numDivisions = (int) (planeSize / planeDim); // int numVertices = (numDivisions + 1) * (numDivisions + 1); // int numEdge = (2 * numDivisions) * (numDivisions + 1); int numFaces = numDivisions * numDivisions; // Set up an array containing the vertex positions for the plane. The // vertices are placed equi-distant on the X-Z plane to form a square // grid that has a side length of "planeSize". // // The Y-coordinate of each vertex is the average of the neighbors already // calculated, if there are any, with a small random offset added. Because // of the way the vertices are calculated, the whole plane will look like // it is streaked in a diagonal direction with mountains and depressions. // for (i = 0; i < (numDivisions + 1); ++i) { for (j = 0; j < (numDivisions + 1); ++j) { float height; if (i == 0 && j == 0) { height = ((rand() % 101) / 100.0f - 0.5f); } else if (i == 0) { float previousHeight = vertices[j - 1][1]; height = previousHeight + ((rand() % 101) / 100.0f - 0.5f); } else if (j == 0) { float previousHeight = vertices[(i-1)*(numDivisions + 1)][1]; height = previousHeight + ((rand() % 101) / 100.0f - 0.5f); } else { float previousHeight = vertices[(i-1)*(numDivisions + 1) + j][1]; float previousHeight2 = vertices[i*(numDivisions + 1) + j - 1][1]; height = (previousHeight + previousHeight2) / 2.0f + ((rand() % 101) / 100.0f - 0.5f); } MFloatPoint vtx( i * planeDim - planeOffset, height, j * planeDim - planeOffset ); vertices.append(vtx); } } // Set up an array containing the number of vertices // for each of the plane's faces // for (i = 0; i < numFaces; ++i) { faceDegrees.append(4); } // Set up an array to assign the vertices for each face // for (i = 0; i < numDivisions; ++i) { for (j = 0; j < numDivisions; ++j) { faceVertices.append(i*(numDivisions+1) + j); faceVertices.append(i*(numDivisions+1) + j + 1); faceVertices.append((i+1)*(numDivisions+1) + j + 1); faceVertices.append((i+1)*(numDivisions+1) + j); } } MFnMesh meshFn; MObject newMesh = meshFn.create(vertices.length(), numFaces, vertices, faceDegrees, faceVertices, outData, &stat); return newMesh; }