Ejemplo n.º 1
0
	MSelectionList convertMIntArrToMselList( MIntArray& intArr, MDagPath& dPath, MFn::Type kCompType )
	{
		MFnSingleIndexedComponent singleIndexCompFn;
			// create MObjet componenet by sigleIndexComponent
		MObject components = singleIndexCompFn.create(kCompType);
		singleIndexCompFn.addElements( intArr );

		MSelectionList compSelList;
		compSelList.add( dPath, components );
		return compSelList;
	}
Ejemplo n.º 2
0
//---------------------------------------------
void	IV_makeSelection(void* data)
//---------------------------------------------
{

	
	BPT_InsertVtx* nodePtr = (BPT_InsertVtx*) data;
	MEventMessage::removeCallback(nodePtr->eID);

	MDagPath meshPath;
	nodePtr->getMeshPath(meshPath);

	if( !(meshPath.apiType() == MFn::kInvalid) && nodePtr->validIndices.length() != 0)	//zur Sicherheit, sollte aber eigentlich nicht mueueglich sein
	{
		MFnSingleIndexedComponent compFn;
		
		MFn::Type type = MFn::kInvalid;
		if(nodePtr->validIndices[0] == 1)
			type = MFn::kMeshEdgeComponent;
		else if(nodePtr->validIndices[0] == 2)
			type = MFn::kMeshPolygonComponent;
		else if(nodePtr->validIndices[0] == 3)
			type = MFn::kMeshVertComponent;
		
		assert(type != MFn::kInvalid);
		//flag wieder entfernen
		nodePtr->validIndices.remove(0);

		MSelectionList compList, current;
		
		MObject comps = compFn.create(type);
		compFn.addElements(nodePtr->validIndices);
		
		compList.add(meshPath,comps);
		compList.add(nodePtr->thisMObject());
	

		

	//	MGlobal::getActiveSelectionList(current);
		
		MGlobal::setActiveSelectionList(compList, MGlobal::kAddToList);
	//	MGlobal::setActiveSelectionList(current,MGlobal::kAddToList);

	
	}
}
Ejemplo n.º 3
0
MStatus splitUV::pruneUVs( MIntArray& validUVIndices )
//
// Description:
//
//		This method will remove any invalid UVIds from the component list and UVId array.
//		The benefit of this is to reduce the amount of extra processing that the node would
//		have to perform. It will result in less iterations through the mesh as there are
//		less UVs to search for.
//
{
	MStatus status;

	unsigned i;
	MIntArray validUVIds;

	for( i = 0; i < validUVIndices.length(); i++ )
	{
		int uvIndex = validUVIndices[i];
		validUVIds.append( fSelUVs[uvIndex] );
	}

	// Replace the local int array of UVIds
	//
	fSelUVs.clear();
	fSelUVs = validUVIds;

	// Build the list of valid components
	//
	MFnSingleIndexedComponent compFn;
	compFn.create( MFn::kMeshMapComponent, &status );
	MCheckStatus( status, "compFn.create( MFn::kMeshMapComponent )" );
	status = compFn.addElements( validUVIds );
	MCheckStatus( status, "compFn.addElements( validUVIds )" );
	MObject component = compFn.object();

	// Replace the component list
	//
	MFnComponentListData compListFn;
	compListFn.create();
	status = compListFn.add( component );
	MCheckStatus( status, "compListFn.add( component )" );

	fComponentList = compListFn.object();

	return status;
}
Ejemplo n.º 4
0
	// ouput the MselectList of VERTEX Component  
	MSelectionList getVtxSelList( MDagPath& inDagPath, const int inCompArray[], int inCountArray )
	{
			// vtx process
			//
		MSelectionList vtxSelList;
			// Create a MFnSingleIndexedComponent object of type kMeshVertComponent.
		MFnSingleIndexedComponent singleIndexCompFn;
		MObject components = singleIndexCompFn.create(MFn::kMeshVertComponent);
		MIntArray elementArray( inCompArray, inCountArray);

			// Add the element indices
		singleIndexCompFn.addElements(elementArray);

			// Add the element to the selection list.
		vtxSelList.add( inDagPath, components );

		return vtxSelList;
	}
Ejemplo n.º 5
0
// ====================================
// Helper Functions
// ====================================
// Create component groups
void
createComp(MFnMeshData &dataCreator, MFn::Type compType, unsigned compId, MIntArray &compList) {

  MStatus returnStatus;

  MFnSingleIndexedComponent comp;
  MObject compObj = comp.create(compType,&returnStatus);
  MWARNERR(returnStatus, "cannot create MFnSingleIndexedComponent");

  returnStatus = comp.addElements(compList);
  MWARNERR(returnStatus, "Error in addElements() for MFnSingleIndexedComponent");

  returnStatus = dataCreator.addObjectGroup(compId);
  MWARNERR(returnStatus, "Error in addObjectGroup()");

  returnStatus = dataCreator.setObjectGroupComponent(compId, compObj);
  MWARNERR(returnStatus, "Error in setObjectGroupComponent()");
}
static
bool
_AssignMaterialFaceSet(
        const MObject& shadingEngine,
        const MDagPath& shapeDagPath,
        const VtIntArray& faceIndices)
{
    MStatus status;

    // Create component object using single indexed
    // components, i.e. face indices.
    MFnSingleIndexedComponent compFn;
    MObject faceComp = compFn.create(MFn::kMeshPolygonComponent, &status);
    if (!status) {
        TF_RUNTIME_ERROR("Failed to create face component.");
        return false;
    }

    MIntArray mFaces;
    TF_FOR_ALL(fIdxIt, faceIndices) {
        mFaces.append(*fIdxIt);
    }
MStatus SelectRingToolCmd2::redoIt()
{	
	//MGlobal::displayInfo( "redoIt" );

	MItMeshPolygon polyIter( selEdgeObject );
	MItMeshEdge edgeIter( selEdgeObject, selEdgeComp );

	//MFnSingleIndexedComponent si( selEdgeComp );
	//MGlobal::displayInfo( MString("doing stuff on ") + selEdgeObject.fullPathName() + " " + si.element(0) );
	
	MFnSingleIndexedComponent indexedCompFn;
	MObject newComponent;		
	MSelectionList newSel;
	MIntArray edges;
	MIntArray faces;
	unsigned int i;
	const int initEdgeIndex = edgeIter.index();
	int initFaceIndex;
	int prevIndex, lastIndex;
	int faceIndex, edgeIndex;
	int newFaceIndex, newEdgeIndex;
	int nInitEdgeVisits;
	
	MIntArray remainFaces;
	MIntArray remainEdges;
	
	if( selType == RING )
	{
		nInitEdgeVisits = 0;
		
		// Push the face+edge combo on both sides 
		// of the current edge onto the stack
		edgeIter.getConnectedFaces( faces );
		if( faces.length() > 1 )
		{
			remainFaces.append( faces[1] );
			remainEdges.append( initEdgeIndex );
		}
		initFaceIndex = faces[0];
		remainFaces.append( initFaceIndex );
		remainEdges.append( initEdgeIndex );
		
		while( remainFaces.length() )
		{
			// Pop the face and edge indices
			lastIndex = remainFaces.length() - 1;
			faceIndex = remainFaces[ lastIndex ];
			edgeIndex = remainEdges[ lastIndex ];
			remainFaces.remove( lastIndex );
			remainEdges.remove( lastIndex );
			
			// If the current edge the initial edge
			if( faceIndex == initFaceIndex && 
				edgeIndex == initEdgeIndex )
			{
				// Reached back to the initial edge, so the loop is closed
				if( ++nInitEdgeVisits == 2 )
					break;
			}
			
			// Add edge to the new selection list
			if( selEdges )
			{
				newComponent = indexedCompFn.create( MFn::kMeshEdgeComponent );
				indexedCompFn.addElement( edgeIndex );
				newSel.add( selEdgeObject, newComponent );
			}
			
			// Add vertices to the new selection list
			if( selVertices )
			{
				newComponent = indexedCompFn.create( MFn::kMeshVertComponent );
				edgeIter.setIndex( edgeIndex, prevIndex );
				indexedCompFn.addElement( edgeIter.index(0) );
				indexedCompFn.addElement( edgeIter.index(1) );
				newSel.add( selEdgeObject, newComponent );
			}
			
			if( faceIndex != -1 ) // Not a boundary edge
			{
				// Add face to the new selection list
				if( selFaces )
				{
					newComponent = indexedCompFn.create( MFn::kMeshPolygonComponent );
					indexedCompFn.addElement( faceIndex );
					newSel.add( selEdgeObject, newComponent );		
				}
				
				// Get edge opposite the current edge
				//
				newEdgeIndex = navigateFace( selEdgeObject, faceIndex, 
											 edgeIndex, OPPOSITE );
				
				// Get the face on the other side of the opposite edge
				//
				edgeIter.setIndex( newEdgeIndex, prevIndex );
				edgeIter.getConnectedFaces( faces );
				
				newFaceIndex = -1; // Initialize to a boundary edge
				if( faces.length() > 1 )
					newFaceIndex = (faceIndex == faces[0]) ? 
									faces[1] : faces[0];
									
				// Push face and edge onto list
				remainFaces.append( newFaceIndex );
				remainEdges.append( newEdgeIndex );
			}
		}
	}
	else // Ring
	{
		int reflEdgeIndex;
		int newReflEdgeIndex;
		int initReflEdgeIndex;
		MIntArray remainReflEdges;
		
		nInitEdgeVisits = 0;
		
		// Push the face+edge+reflect combo on both sides 
		// of the current edge onto the stack
		//edgeIter.setIndex( initEdgeIndex, prevIndex );
		edgeIter.getConnectedFaces( faces );
		initFaceIndex = faces[0];
		for( i=0; i < 2; i++ )
		{
			remainFaces.append( initFaceIndex );
			remainEdges.append( initEdgeIndex );
			remainReflEdges.append(
				navigateFace( selEdgeObject, initFaceIndex, initEdgeIndex, 
							(i == 0) ? PREVIOUS : NEXT ) );
		}				
		initReflEdgeIndex = remainReflEdges[1];
				
		while( remainFaces.length() )
		{
			// Pop the face, edge, and reflEdge indices
			lastIndex = remainFaces.length() - 1;
			faceIndex = remainFaces[ lastIndex ];
			edgeIndex = remainEdges[ lastIndex ];
			reflEdgeIndex = remainReflEdges[ lastIndex ];
			remainFaces.remove( lastIndex );
			remainEdges.remove( lastIndex );
			remainReflEdges.remove( lastIndex );
									
			//MGlobal::displayInfo( MString("Face: ") + faceIndex + " edge: " + edgeIndex + " reflEdgeIndex: " + reflEdgeIndex );
		
			// If the current edge the initial edge
			if( faceIndex == initFaceIndex && 
				edgeIndex == initEdgeIndex &&
				reflEdgeIndex == initReflEdgeIndex )
			{
				// Reached back to the initial edge, so the ring is closed
				if( ++nInitEdgeVisits == 2 )
					break;
			}
										
			// Add edge to the new selection list
			if( selEdges )
			{
				newComponent = indexedCompFn.create( MFn::kMeshEdgeComponent );
				indexedCompFn.addElement( edgeIndex );
				newSel.add( selEdgeObject, newComponent );
			}
			
			// Add vertices to the new selection list
			if( selVertices )
			{
				newComponent = indexedCompFn.create( MFn::kMeshVertComponent );
				edgeIter.setIndex( edgeIndex, prevIndex );
				indexedCompFn.addElement( edgeIter.index(0) );
				indexedCompFn.addElement( edgeIter.index(1) );
				newSel.add( selEdgeObject, newComponent );
			}
			
			// Add face to the new selection list
			if( selFaces )
			{
				newComponent = indexedCompFn.create( MFn::kMeshPolygonComponent );
				indexedCompFn.addElement( faceIndex );
				newSel.add( selEdgeObject, newComponent );		
			}
			
			// Get the face on opposite side of reflEdge
			edgeIter.setIndex( reflEdgeIndex, prevIndex );
			edgeIter.getConnectedFaces( faces );

			// Only if there are two faces can their be an opposite face
			if( faces.length() > 1 )
			{
				newFaceIndex = (faceIndex == faces[0]) ?
								faces[1] : faces[0];			

				int edgePrev = navigateFace( selEdgeObject, newFaceIndex, reflEdgeIndex, PREVIOUS );
				int edgeNext = navigateFace( selEdgeObject, newFaceIndex, reflEdgeIndex, NEXT );
				
				// Determine which of the edges touches the original edge
				//
				edgeIter.setIndex( edgeIndex, prevIndex );
				if( edgeIter.connectedToEdge( edgePrev ) )
				{
					newEdgeIndex = edgePrev;
					newReflEdgeIndex = navigateFace( selEdgeObject, newFaceIndex, newEdgeIndex, PREVIOUS );
				}
				else
				{
					newEdgeIndex = edgeNext;
					newReflEdgeIndex = navigateFace( selEdgeObject, newFaceIndex, newEdgeIndex, NEXT );
				}
				
				// Push face, edge, and reflEdge onto list
				remainFaces.append( newFaceIndex );
				remainEdges.append( newEdgeIndex );
				remainReflEdges.append( newReflEdgeIndex );
			}
			
		}
	}	
		
	// Set the active selection to the one previous to edge loop selection
	MGlobal::setActiveSelectionList( prevSel, MGlobal::kReplaceList );

	// Update this selection based on the list adjustment setting
	MGlobal::selectCommand( newSel, listAdjust );

	return MS::kSuccess;	
}
Ejemplo n.º 8
0
bool ProceduralHolderUI::select( MSelectInfo &selectInfo, MSelectionList &selectionList, MPointArray &worldSpaceSelectPts ) const
{
	MStatus s;

	// early out if we're not selectable. we always allow components to be selected if we're highlighted,
	// but we don't allow ourselves to be selected as a whole unless meshes are in the selection mask.
	// it's not ideal that we act like a mesh, but it's at least consistent with the drawing mask we use.
	if( selectInfo.displayStatus() != M3dView::kHilite )
	{
		MSelectionMask meshMask( MSelectionMask::kSelectMeshes );
		if( !selectInfo.selectable( meshMask ) )
		{
			return false;
		}
	}

	// early out if we have no scene to draw
	ProceduralHolder *proceduralHolder = static_cast<ProceduralHolder *>( surfaceShape() );
	IECoreGL::ConstScenePtr scene = proceduralHolder->scene();
	if( !scene )
	{
		return false;
	}
	
	// we want to perform the selection using an IECoreGL::Selector, so we
	// can avoid the performance penalty associated with using GL_SELECT mode.
	// that means we don't really want to call view.beginSelect(), but we have to
	// call it just to get the projection matrix for our own selection, because as far
	// as i can tell, there is no other way of getting it reliably.
	
	M3dView view = selectInfo.view();
	view.beginSelect();
	Imath::M44d projectionMatrix;
	glGetDoublev( GL_PROJECTION_MATRIX, projectionMatrix.getValue() );
	view.endSelect();
	
	view.beginGL();
	
		glMatrixMode( GL_PROJECTION );
		glLoadMatrixd( projectionMatrix.getValue() );
		
		IECoreGL::Selector::Mode selectionMode = IECoreGL::Selector::IDRender;
		if( selectInfo.displayStatus() == M3dView::kHilite && !selectInfo.singleSelection() )
		{
			selectionMode = IECoreGL::Selector::OcclusionQuery;
		}
		
		std::vector<IECoreGL::HitRecord> hits;
		{
			IECoreGL::Selector selector( Imath::Box2f( Imath::V2f( 0 ), Imath::V2f( 1 ) ), selectionMode, hits );
				
			IECoreGL::State::bindBaseState();
			selector.baseState()->bind();
			scene->render( selector.baseState() );
		
			if( selectInfo.displayStatus() != M3dView::kHilite )
			{
				// we're not in component selection mode. we'd like to be able to select the procedural
				// object using the bounding box so we draw it too.
				MPlug pDrawBound( proceduralHolder->thisMObject(), ProceduralHolder::aDrawBound );
				bool drawBound = true;
				pDrawBound.getValue( drawBound );
				if( drawBound )
				{
					IECoreGL::BoxPrimitive::renderWireframe( IECore::convert<Imath::Box3f>( proceduralHolder->boundingBox() ) );
				}
			}
		}
						
	view.endGL();
	
	if( !hits.size() )
	{
		return false;
	}

	// iterate over the hits, converting them into components and also finding
	// the closest one.
	MIntArray componentIndices;
	float depthMin = std::numeric_limits<float>::max();
	int depthMinIndex = -1;
	for( int i=0, e = hits.size(); i < e; i++ )
	{		
		if( hits[i].depthMin < depthMin )
		{
			depthMin = hits[i].depthMin;
			depthMinIndex = componentIndices.length();
		}
		
		ProceduralHolder::ComponentsMap::const_iterator compIt = proceduralHolder->m_componentsMap.find( hits[i].name.value() );
		assert( compIt != proceduralHolder->m_componentsMap.end() );
		componentIndices.append( compIt->second.first );		
	}
	
	assert( depthMinIndex >= 0 );

	// figure out the world space location of the closest hit
	
	MDagPath camera;
	view.getCamera( camera );
	MFnCamera fnCamera( camera.node() );
	float near = fnCamera.nearClippingPlane();
	float far = fnCamera.farClippingPlane();
	
	float z = -1;
	if( fnCamera.isOrtho() )
	{
		z = Imath::lerp( near, far, depthMin );
	}
	else
	{
		// perspective camera - depth isn't linear so linearise to get z
		float a = far / ( far - near );
		float b = far * near / ( near - far );
		z = b / ( depthMin - a );
	}	
	
	MPoint localRayOrigin;
	MVector localRayDirection;
	selectInfo.getLocalRay( localRayOrigin, localRayDirection );
	MMatrix localToCamera = selectInfo.selectPath().inclusiveMatrix() * camera.inclusiveMatrix().inverse();	
	MPoint cameraRayOrigin = localRayOrigin * localToCamera;
	MVector cameraRayDirection = localRayDirection * localToCamera;
	
	MPoint cameraIntersectionPoint = cameraRayOrigin + cameraRayDirection * ( -( z - near ) / cameraRayDirection.z );
	MPoint worldIntersectionPoint = cameraIntersectionPoint * camera.inclusiveMatrix();
	
	// turn the processed hits into appropriate changes to the current selection
				
	if( selectInfo.displayStatus() == M3dView::kHilite )
	{
		// selecting components
		MFnSingleIndexedComponent fnComponent;
		MObject component = fnComponent.create( MFn::kMeshPolygonComponent, &s ); assert( s );
	
		if( selectInfo.singleSelection() )
		{
			fnComponent.addElement( componentIndices[depthMinIndex] );
		}
		else
		{
			fnComponent.addElements( componentIndices );
		}
		
		MSelectionList items;
		items.add( selectInfo.multiPath(), component );
		
		selectInfo.addSelection(
			items, worldIntersectionPoint,
			selectionList, worldSpaceSelectPts,
			MSelectionMask::kSelectMeshFaces,
			true
		);		
	}
	else
	{
		// selecting objects
		MSelectionList item;
		item.add( selectInfo.selectPath() );

		selectInfo.addSelection(
			item, worldIntersectionPoint,
			selectionList, worldSpaceSelectPts,
			MSelectionMask::kSelectMeshes,
			false
		);
	}
	
	return true;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
MStatus CVstSelectCoincidentFacesCmd::DoSelect()
{
	MSelectionList meshList;
	GetSpecifiedMeshes( meshList );

	MSelectionList coincidentList;

	MDagPath mDagPath;
	MObject cObj;

	MPointArray points;
	MIntArray iIndexes;
	MIntArray jIndexes;

	uint iCount;
	bool addI;
	bool same;
	bool foundVertex;

	double tolerance( MPoint_kTol );
	if ( m_undo.ArgDatabase().isFlagSet( kOptTolerance ) )
	{
		MDistance optTolerance;
		m_undo.ArgDatabase().getFlagArgument( kOptTolerance, 0U, optTolerance );
		tolerance = optTolerance.as( MDistance::internalUnit() );
	}

	for ( MItSelectionList sIt( meshList ); !sIt.isDone(); sIt.next() )
	{
		if ( !sIt.getDagPath( mDagPath, cObj ) )
			continue;

		MFnSingleIndexedComponent sFn;
		MObject sObj( sFn.create( MFn::kMeshPolygonComponent ) );

		MFnMesh meshFn( mDagPath );
		meshFn.getPoints( points );

		if ( !sIt.hasComponents() )
		{
			const uint nFaces( meshFn.numPolygons() );
			for ( uint i( 0U ); i != nFaces; ++i )
			{
				meshFn.getPolygonVertices( i, iIndexes );
				iCount = iIndexes.length();

				addI = false;

				for ( uint j( i + 1 ); j < nFaces; ++j )
				{
					meshFn.getPolygonVertices( j, jIndexes );
					if ( jIndexes.length() == iCount )
					{
						same = true;

						for ( uint k( 0U ); k != iCount; ++k )
						{
							foundVertex = false;

							const MPoint &kPoint( points[ iIndexes[ k ] ] );

							for ( uint l( 0U ); l < iCount; ++l )
							{
								if ( kPoint.isEquivalent( points[ jIndexes[ l ] ], tolerance ) )
								{
									foundVertex = true;
									break;
								}
							}

							if ( !foundVertex )
							{
								same = false;
								break;
							}
						}

						if ( same )
						{
							addI = true;
							sFn.addElement( j );
						}
					}
				}

				if ( addI )
				{
					sFn.addElement( i );
				}
			}
		}
		else
		{
			MFnSingleIndexedComponent cFn( cObj );
			MIntArray cA;
			MFnSingleIndexedComponent( cObj ).getElements( cA );

			const uint nFaces( cA.length() );

			for ( uint i( 0U ); i != nFaces; ++i )
			{
				meshFn.getPolygonVertices( cA[ i ], iIndexes );
				iCount = iIndexes.length();

				addI = false;

				for ( uint j( i + 1U ); j < nFaces; ++j )
				{
					meshFn.getPolygonVertices( cA[ j ], jIndexes );
					if ( jIndexes.length() == iCount )
					{
						same = true;

						for ( uint k( 0U ); k != iCount; ++k )
						{
							foundVertex = false;

							const MPoint &kPoint( points[ iIndexes[ k ] ] );

							for ( uint l( 0U ); l < iCount; ++l )
							{
								if ( kPoint.isEquivalent( points[ jIndexes[ l ] ], tolerance ) )
								{
									foundVertex = true;
									break;
								}
							}

							if ( !foundVertex )
							{
								same = false;
								break;
							}
						}

						if ( same )
						{
							addI = true;
							sFn.addElement( cA[ j ] );
						}
					}
				}

				if ( addI )
				{
					sFn.addElement( cA[ i ] );
				}
			}
		}

		if ( sFn.elementCount() > 0 )
		{
			coincidentList.add( mDagPath, sObj );
		}
		else
		{
			MSelectionList tmpList;
			tmpList.add( mDagPath, cObj );
			MStringArray tmpA;
			tmpList.getSelectionStrings( tmpA );

			minfo << "No coincident faces on:";
			for ( uint i( 0U ); i != tmpA.length(); ++i )
			{
				minfo << " " << tmpA[ i ];
			}
			minfo << std::endl;
		}
	}

	if ( coincidentList.length() )
	{
		MGlobal::setActiveSelectionList( coincidentList );
		MStringArray tmpA;
		coincidentList.getSelectionStrings( tmpA );
		setResult( tmpA );
	}
	else
	{
		if ( meshList.length() > 0U )
		{
			minfo << "No coincident faces found" << std::endl;
		}
	}

	return MS::kSuccess;
}
Ejemplo n.º 10
0
MayaMeshWriter::MayaMeshWriter(MDagPath & iDag,
    Alembic::Abc::OObject & iParent, Alembic::Util::uint32_t iTimeIndex,
    const JobArgs & iArgs, GetMembersMap& gmMap)
  : mNoNormals(iArgs.noNormals),
    mWriteUVs(iArgs.writeUVs),
    mWriteColorSets(iArgs.writeColorSets),
    mWriteUVSets(iArgs.writeUVSets),
    mIsGeometryAnimated(false),
    mDagPath(iDag)
{
    MStatus status = MS::kSuccess;
    MFnMesh lMesh( mDagPath, &status );
    if ( !status )
    {
        MGlobal::displayError( "MFnMesh() failed for MayaMeshWriter" );
    }

    // intermediate objects aren't translated
    MObject surface = iDag.node();

    if (iTimeIndex != 0 && util::isAnimated(surface))
    {
        mIsGeometryAnimated = true;
    }
    else
    {
        iTimeIndex = 0;
    }

    std::vector<float> uvs;
    std::vector<Alembic::Util::uint32_t> indices;
    std::string uvSetName;

    MString name = lMesh.name();
    name = util::stripNamespaces(name, iArgs.stripNamespace);

    // check to see if this poly has been tagged as a SubD
    MPlug plug = lMesh.findPlug("SubDivisionMesh");
    if ( !plug.isNull() && plug.asBool() )
    {
        Alembic::AbcGeom::OSubD obj(iParent, name.asChar(), iTimeIndex);
        mSubDSchema = obj.getSchema();

        Alembic::AbcGeom::OV2fGeomParam::Sample uvSamp;
        if (mWriteUVs || mWriteUVSets)
        {
            getUVs(uvs, indices, uvSetName);

            if (!uvs.empty())
            {
                if (!uvSetName.empty())
                {
                    mSubDSchema.setUVSourceName(uvSetName);
                }

                uvSamp.setScope( Alembic::AbcGeom::kFacevaryingScope );
                uvSamp.setVals(Alembic::AbcGeom::V2fArraySample(
                    (const Imath::V2f *) &uvs.front(), uvs.size() / 2));
                if (!indices.empty())
                {
                    uvSamp.setIndices(Alembic::Abc::UInt32ArraySample(
                        &indices.front(), indices.size()));
                }
            }
        }

        Alembic::Abc::OCompoundProperty cp;
        Alembic::Abc::OCompoundProperty up;
        if (AttributesWriter::hasAnyAttr(lMesh, iArgs))
        {
            cp = mSubDSchema.getArbGeomParams();
            up = mSubDSchema.getUserProperties();
        }
        mAttrs = AttributesWriterPtr(new AttributesWriter(cp, up, obj, lMesh,
            iTimeIndex, iArgs));

        writeSubD(uvSamp);
    }
    else
    {
        Alembic::AbcGeom::OPolyMesh obj(iParent, name.asChar(), iTimeIndex);
        mPolySchema = obj.getSchema();

        Alembic::AbcGeom::OV2fGeomParam::Sample uvSamp;

        if (mWriteUVs || mWriteUVSets)
        {
            getUVs(uvs, indices, uvSetName);

            if (!uvs.empty())
            {
                if (!uvSetName.empty())
                {
                    mPolySchema.setUVSourceName(uvSetName);
                }
                uvSamp.setScope( Alembic::AbcGeom::kFacevaryingScope );
                uvSamp.setVals(Alembic::AbcGeom::V2fArraySample(
                    (const Imath::V2f *) &uvs.front(), uvs.size() / 2));
                if (!indices.empty())
                {
                    uvSamp.setIndices(Alembic::Abc::UInt32ArraySample(
                        &indices.front(), indices.size()));
                }
            }
        }

        Alembic::Abc::OCompoundProperty cp;
        Alembic::Abc::OCompoundProperty up;
        if (AttributesWriter::hasAnyAttr(lMesh, iArgs))
        {
            cp = mPolySchema.getArbGeomParams();
            up = mPolySchema.getUserProperties();
        }

        // set the rest of the props and write to the writer node
        mAttrs = AttributesWriterPtr(new AttributesWriter(cp, up, obj, lMesh,
            iTimeIndex, iArgs));

        writePoly(uvSamp);
    }

    if (mWriteColorSets)
    {
        MStringArray colorSetNames;
        lMesh.getColorSetNames(colorSetNames);

        if (colorSetNames.length() > 0)
        {

            // Create the color sets compound prop
            Alembic::Abc::OCompoundProperty arbParams;
            if (mPolySchema.valid())
            {
                arbParams =  mPolySchema.getArbGeomParams();
            }
            else
            {
                arbParams =  mSubDSchema.getArbGeomParams();
            }

            std::string currentColorSet = lMesh.currentColorSetName().asChar();
            for (unsigned int i=0; i < colorSetNames.length(); ++i)
            {
                // Create an array property for each color set
                std::string colorSetPropName = colorSetNames[i].asChar();

                Alembic::AbcCoreAbstract::MetaData md;
                if (currentColorSet == colorSetPropName)
                {
                    md.set("mayaColorSet", "1");
                }
                else
                {
                    md.set("mayaColorSet", "0");
                }

                if (lMesh.getColorRepresentation(colorSetNames[i]) ==
                    MFnMesh::kRGB)
                {
                    Alembic::AbcGeom::OC3fGeomParam colorProp(arbParams,
                        colorSetPropName, true,
                        Alembic::AbcGeom::kFacevaryingScope, 1, iTimeIndex, md);
                    mRGBParams.push_back(colorProp);
                }
                else
                {
                    Alembic::AbcGeom::OC4fGeomParam colorProp(arbParams,
                        colorSetPropName, true,
                        Alembic::AbcGeom::kFacevaryingScope, 1, iTimeIndex, md);
                    mRGBAParams.push_back(colorProp);
                }
            }
            writeColor();
        }
    }

    if (mWriteUVSets)
    {
        MStringArray uvSetNames;
        lMesh.getUVSetNames(uvSetNames);
        unsigned int uvSetNamesLen = uvSetNames.length();

        if (uvSetNamesLen > 1)
        {
            // Create the uv sets compound prop
            Alembic::Abc::OCompoundProperty arbParams;
            if (mPolySchema.valid())
            {
                arbParams =  mPolySchema.getArbGeomParams();
            }
            else
            {
                arbParams =  mSubDSchema.getArbGeomParams();
            }

            MString currentUV = lMesh.currentUVSetName();

            for (unsigned int i = 0; i < uvSetNamesLen; ++i)
            {
                // Create an array property for each uv set
                MString uvSetPropName = uvSetNames[i];

                // the current UV set gets mapped to the primary UVs
                if (currentUV == uvSetPropName)
                {
                    continue;
                }

                if (uvSetPropName.length() > 0 &&
                    lMesh.numUVs(uvSetPropName) > 0)
                {
                    mUVparams.push_back(Alembic::AbcGeom::OV2fGeomParam(
                        arbParams, uvSetPropName.asChar(), true,
                        Alembic::AbcGeom::kFacevaryingScope, 1, iTimeIndex));
                }
            }
            writeUVSets();
        }
    }

    // write out facesets
    if(!iArgs.writeFaceSets)
        return;

    // get the connected shading engines
    MObjectArray connSGObjs (getOutConnectedSG(mDagPath));
    const unsigned int sgCount = connSGObjs.length();

    for (unsigned int i = 0; i < sgCount; ++i)
    {
        MObject connSGObj, compObj;

        connSGObj = connSGObjs[i];

        MFnDependencyNode fnDepNode(connSGObj);
        MString connSgObjName = fnDepNode.name();

        // retrive the component MObject
        status = getSetComponents(mDagPath, connSGObj, gmMap, compObj);

        if (status != MS::kSuccess)
        {
            // for some reason the shading group doesn't represent a face set
            continue;
        }

        // retrieve the face indices
        MIntArray indices;
        MFnSingleIndexedComponent compFn;
        compFn.setObject(compObj);
        compFn.getElements(indices);
        const unsigned int numData = indices.length();

        // encountered the whole object mapping. skip it.
        if (numData == 0)
            continue;

        std::vector<Alembic::Util::int32_t> faceIndices(numData);
        for (unsigned int j = 0; j < numData; ++j)
        {
            faceIndices[j] = indices[j];
        }

        connSgObjName = util::stripNamespaces(connSgObjName,
                                              iArgs.stripNamespace);

        Alembic::AbcGeom::OFaceSet faceSet;
        std::string faceSetName(connSgObjName.asChar());

        MPlug abcFacesetNamePlug = fnDepNode.findPlug("AbcFacesetName", true);
        if (!abcFacesetNamePlug.isNull())
        {
            faceSetName = abcFacesetNamePlug.asString().asChar();
        }

        if (mPolySchema.valid())
        {
            if (mPolySchema.hasFaceSet(faceSetName))
            {
                faceSet = mPolySchema.getFaceSet(faceSetName);
            }
            else
            {
                faceSet = mPolySchema.createFaceSet(faceSetName);
            }
        }
        else
        {
            if (mSubDSchema.hasFaceSet(faceSetName))
            {
                faceSet = mSubDSchema.getFaceSet(faceSetName);
            }
            else
            {
                faceSet = mSubDSchema.createFaceSet(faceSetName);
            }
        }
        Alembic::AbcGeom::OFaceSetSchema::Sample samp;
        samp.setFaces(Alembic::Abc::Int32ArraySample(faceIndices));

        Alembic::AbcGeom::OFaceSetSchema faceSetSchema = faceSet.getSchema();

        faceSetSchema.set(samp);
        faceSetSchema.setFaceExclusivity(Alembic::AbcGeom::kFaceSetExclusive);

        MFnDependencyNode iNode(connSGObj);

        Alembic::Abc::OCompoundProperty cp;
        Alembic::Abc::OCompoundProperty up;
        if (AttributesWriter::hasAnyAttr(iNode, iArgs))
        {
            cp = faceSetSchema.getArbGeomParams();
            up = faceSetSchema.getUserProperties();
        }

        AttributesWriter attrWriter(cp, up, faceSet, iNode, iTimeIndex, iArgs);
        attrWriter.write();
    }
}
Ejemplo n.º 11
0
bool apiSimpleShapeUI::selectVertices( MSelectInfo &selectInfo,
							 MSelectionList &selectionList,
							 MPointArray &worldSpaceSelectPts ) const
//
// Description:
//
//     Vertex selection.
//
// Arguments:
//
//     selectInfo           - the selection state information
//     selectionList        - the list of selected items to add to
//     worldSpaceSelectPts  -
//
{
	bool selected = false;
	M3dView view = selectInfo.view();

	MPoint 		xformedPoint;
	MPoint 		currentPoint;
	MPoint 		selectionPoint;
	double		z,previousZ = 0.0;
 	int			closestPointVertexIndex = -1;

	const MDagPath & path = selectInfo.multiPath();

	// Create a component that will store the selected vertices
	//
	MFnSingleIndexedComponent fnComponent;
	MObject surfaceComponent = fnComponent.create( MFn::kMeshVertComponent );
	int vertexIndex;

	// if the user did a single mouse click and we find > 1 selection
	// we will use the alignmentMatrix to find out which is the closest
	//
	MMatrix	alignmentMatrix;
	MPoint singlePoint; 
	bool singleSelection = selectInfo.singleSelection();
	if( singleSelection ) {
		alignmentMatrix = selectInfo.getAlignmentMatrix();
	}

	// Get the geometry information
	//
	apiSimpleShape* shape = (apiSimpleShape*) surfaceShape();
	MVectorArray* geomPtr = shape->getControlPoints();
	MVectorArray& geom = *geomPtr;


	// Loop through all vertices of the mesh and
	// see if they lie withing the selection area
	//
	int numVertices = geom.length();
	for ( vertexIndex=0; vertexIndex<numVertices; vertexIndex++ )
	{
		const MVector& point = geom[ vertexIndex ];

		// Sets OpenGL's render mode to select and stores
		// selected items in a pick buffer
		//
		view.beginSelect();

		glBegin( GL_POINTS );
		glVertex3f( (float)point[0], 
					(float)point[1], 
					(float)point[2] );
		glEnd();

		if ( view.endSelect() > 0 )	// Hit count > 0
		{
			selected = true;

			if ( singleSelection ) {
				xformedPoint = currentPoint;
				xformedPoint.homogenize();
				xformedPoint*= alignmentMatrix;
				z = xformedPoint.z;
				if ( closestPointVertexIndex < 0 || z > previousZ ) {
					closestPointVertexIndex = vertexIndex;
					singlePoint = currentPoint;
					previousZ = z;
				}
			} else {
				// multiple selection, store all elements
				//
				fnComponent.addElement( vertexIndex );
			}
		}
	}

	// If single selection, insert the closest point into the array
	//
	if ( selected && selectInfo.singleSelection() ) {
		fnComponent.addElement(closestPointVertexIndex);

		// need to get world space position for this vertex
		//
		selectionPoint = singlePoint;
		selectionPoint *= path.inclusiveMatrix();
	}

	// Add the selected component to the selection list
	//
	if ( selected ) {
		MSelectionList selectionItem;
		selectionItem.add( path, surfaceComponent );

		MSelectionMask mask( MSelectionMask::kSelectComponentsMask );
		selectInfo.addSelection(
			selectionItem, selectionPoint,
			selectionList, worldSpaceSelectPts,
			mask, true );
	}

	return selected;
}
MStatus SelectRingContext1::doRelease( MEvent &event )
{
	// Get the mouse release position
	event.getPosition( releaseX, releaseY );

	// Didn't select a single point
	if( abs(pressX - releaseX) > 1 || abs(pressY - releaseY) > 1 )
	{
		MGlobal::displayWarning( "Click on a single edge" );
		return MS::kFailure;
	}
	
	// Set the selection surface area
	int halfClickBoxSize = clickBoxSize / 2;
	pressX -= halfClickBoxSize;
	pressY -= halfClickBoxSize;
	releaseX = pressX + clickBoxSize;
	releaseY = pressY + clickBoxSize;
	 
	/*
	// Record previous selection state
	prevSelMode = MGlobal::selectionMode();
	prevCompMask = MGlobal::componentSelectionMask();
	*/

	// Get the current selection
	MSelectionList curSel;
	MGlobal::getActiveSelectionList( curSel );

	//MGlobal::displayInfo( MString("Dim: ") + start_x + " " + start_y + " " + last_x + " " + last_y );
	
	// Change to object selection mode
	MGlobal::setSelectionMode( MGlobal::kSelectObjectMode );
	MGlobal::setComponentSelectionMask( MSelectionMask( MSelectionMask::kSelectObjectsMask ) );

	// Select the object under the selection area
	MGlobal::selectFromScreen( pressX, pressY, releaseX, releaseY, MGlobal::kReplaceList);
	MGlobal::executeCommand( "hilite" );
	
	// Change selection mode to mesh edges
	MGlobal::setSelectionMode( MGlobal::kSelectComponentMode );
	MGlobal::setComponentSelectionMask( MSelectionMask( MSelectionMask::kSelectMeshEdges ) );
	
	// Select the edges
	MGlobal::selectFromScreen( pressX, pressY, releaseX, releaseY, MGlobal::kReplaceList );
								   
	// Get the list of selected edges
	MSelectionList origEdgesSel;
	MGlobal::getActiveSelectionList( origEdgesSel );	
	
	MSelectionList newEdgesSel;
	MDagPath dagPath;
	MObject component;	
	
	// Only use the first edge in the selection
	MItSelectionList selIter( origEdgesSel, MFn::kMeshEdgeComponent );
	if( !selIter.isDone() )
	{
		selIter.getDagPath( dagPath, component );
		
		MIntArray faces;
		MItMeshEdge edgeIter( dagPath, component );
		
		MIntArray edgesVisited, facesVisited;
		int edgeIndex, faceIndex;
		int prevIndex;
		unsigned int i;
		bool finished = false;
		while( !finished )
		{
			edgeIndex = edgeIter.index();
			edgesVisited.append( edgeIndex );
			
			// Create an edge component the current edge
			MFnSingleIndexedComponent indexedCompFn;
			MObject newComponent = indexedCompFn.create( MFn::kMeshEdgeComponent );
			indexedCompFn.addElement( edgeIndex );
			newEdgesSel.add( dagPath, newComponent );
			
			//MGlobal::displayInfo( MString("ADDING: ") + edgeIter.index() );
			
			edgeIter.getConnectedFaces( faces );
			faceIndex = faces[0];
			if( faces.length() > 1 )
			{
				// Select the face that hasn't already been visited
				for( i=0; i < facesVisited.length(); i++ )
				{
					if( faceIndex == facesVisited[i] )
					{
						faceIndex = faces[1];
						break;
					}
				}
			}
				
			//MGlobal::displayInfo( MString("FACE: ") + faceIndex );
			facesVisited.append( faceIndex );
			
			MItMeshPolygon polyIter( dagPath );
		
			polyIter.setIndex( faceIndex, prevIndex );
		
			//MGlobal::displayInfo( MString( "faces: " ) + faces[0] + " " + faces[1] );
		
			MIntArray edges;
			polyIter.getEdges( edges );
		
			// Determine the face-relative index of the current
			// edge
			unsigned int edgeFaceIndex = 0;
			for( i=0; i < edges.length(); i++ )
			{
				if( edges[i] == edgeIter.index() )
				{
					edgeFaceIndex = i;
					break;
				}
			}
			
			// Determine the edge that is opposite the current edge
			edgeIndex = edges[ (edgeFaceIndex + (edges.length() / 2) ) % edges.length() ];
			
			//int index = edgeIter.index();
			//MGlobal::displayInfo( MString( "sel edge index: " ) + index + " next edge: " + edgeIndex );	
			
			// Set the current edge to the opposite edge
			edgeIter.setIndex( edgeIndex, prevIndex );	

			// Determine if the edge has already been visited
			for( i=0; i < edgesVisited.length(); i++ )
			{
				if( edgeIndex == edgesVisited[i] )
				{
					finished = true;
					break;
				}							
			}
		}
	}
	
	// Set the active selection to the one previous to edge loop selection
	MGlobal::setActiveSelectionList( curSel, MGlobal::kReplaceList);

	// Update this selection based on the list adjustment setting
	MGlobal::selectCommand( newEdgesSel, listAdjust );

	return MS::kSuccess;		
}
Ejemplo n.º 13
0
void NifMeshExporterSkyrim::ExportMesh( MObject dagNode ) {
	//out << "NifTranslator::ExportMesh {";
	ComplexShape cs;
	MStatus stat;
	MObject mesh;

	//Find Mesh child of given transform object
	MFnDagNode nodeFn(dagNode);

	cs.SetName(this->translatorUtils->MakeNifName(nodeFn.name()));


	for (int i = 0; i != nodeFn.childCount(); ++i) {
		// get a handle to the child
		if (nodeFn.child(i).hasFn(MFn::kMesh)) {
			MFnMesh tempFn(nodeFn.child(i));
			//No history items
			if (!tempFn.isIntermediateObject()) {
				//out << "Found a mesh child." << endl;
				mesh = nodeFn.child(i);
				break;
			}
		}
	}

	MFnMesh visibleMeshFn(mesh, &stat);
	if (stat != MS::kSuccess) {
		//out << stat.errorString().asChar() << endl;
		throw runtime_error("Failed to create visibleMeshFn.");
	}

	//out << visibleMeshFn.name().asChar() << ") {" << endl;
	MFnMesh meshFn;
	MObject dataObj;
	MPlugArray inMeshPlugArray;
	MPlug childPlug;
	MPlug geomPlug;
	MPlug inputPlug;

	// this will hold the returned vertex positions
	MPointArray vts;

	//For now always use the visible mesh
	meshFn.setObject(mesh);

	//out << "Use the function set to get the points" << endl;
	// use the function set to get the points
	stat = meshFn.getPoints(vts);
	if (stat != MS::kSuccess) {
		//out << stat.errorString().asChar() << endl;
		throw runtime_error("Failed to get points.");
	}

	//Maya won't store any information about objects with no vertices.  Just skip it.
	if (vts.length() == 0) {
		MGlobal::displayWarning("An object in this scene has no vertices.  Nothing will be exported.");
		return;
	}

	vector<WeightedVertex> nif_vts(vts.length());
	for (int i = 0; i != vts.length(); ++i) {
		nif_vts[i].position.x = float(vts[i].x);
		nif_vts[i].position.y = float(vts[i].y);
		nif_vts[i].position.z = float(vts[i].z);
	}

	//Set vertex info later since it includes skin weights
	//cs.SetVertices( nif_vts );

	//out << "Use the function set to get the colors" << endl;
	MColorArray myColors;
	meshFn.getFaceVertexColors(myColors);

	//out << "Prepare NIF color vector" << endl;
	vector<Color4> niColors(myColors.length());
	for (unsigned int i = 0; i < myColors.length(); ++i) {
		niColors[i] = Color4(myColors[i].r, myColors[i].g, myColors[i].b, myColors[i].a);
	}
	cs.SetColors(niColors);


	// this will hold the returned vertex positions
	MFloatVectorArray nmls;

	//out << "Use the function set to get the normals" << endl;
	// use the function set to get the normals
	stat = meshFn.getNormals(nmls, MSpace::kTransform);
	if (stat != MS::kSuccess) {
		//out << stat.errorString().asChar() << endl;
		throw runtime_error("Failed to get normals");
	}

	//out << "Prepare NIF normal vector" << endl;
	vector<Vector3> nif_nmls(nmls.length());
	for (int i = 0; i != nmls.length(); ++i) {
		nif_nmls[i].x = float(nmls[i].x);
		nif_nmls[i].y = float(nmls[i].y);
		nif_nmls[i].z = float(nmls[i].z);
	}
	cs.SetNormals(nif_nmls);

	//out << "Use the function set to get the UV set names" << endl;
	MStringArray uvSetNames;
	MString baseUVSet;
	MFloatArray myUCoords;
	MFloatArray myVCoords;
	bool has_uvs = false;

	// get the names of the uv sets on the mesh
	meshFn.getUVSetNames(uvSetNames);

	vector<TexCoordSet> nif_uvs;

	//Record assotiation between name and uv set index for later
	map<string, int> uvSetNums;

	int set_num = 0;
	for (unsigned int i = 0; i < uvSetNames.length(); ++i) {
		if (meshFn.numUVs(uvSetNames[i]) > 0) {
			TexType tt;
			string set_name = uvSetNames[i].asChar();
			if (set_name == "base" || set_name == "map1") {
				tt = BASE_MAP;
			}
			else if (set_name == "dark") {
				tt = DARK_MAP;
			}
			else if (set_name == "detail") {
				tt = DETAIL_MAP;
			}
			else if (set_name == "gloss") {
				tt = GLOSS_MAP;
			}
			else if (set_name == "glow") {
				tt = GLOW_MAP;
			}
			else if (set_name == "bump") {
				tt = BUMP_MAP;
			}
			else if (set_name == "decal0") {
				tt = DECAL_0_MAP;
			}
			else if (set_name == "decal1") {
				tt = DECAL_1_MAP;
			}
			else {
				tt = BASE_MAP;
			}

			//Record the assotiation
			uvSetNums[set_name] = set_num;

			//Get the UVs
			meshFn.getUVs(myUCoords, myVCoords, &uvSetNames[i]);

			//Make sure this set actually has some UVs in it.  Maya sometimes returns empty UV sets.
			if (myUCoords.length() == 0) {
				continue;
			}

			//Store the data
			TexCoordSet tcs;
			tcs.texType = tt;
			tcs.texCoords.resize(myUCoords.length());
			for (unsigned int j = 0; j < myUCoords.length(); ++j) {
				tcs.texCoords[j].u = myUCoords[j];
				//Flip the V coords
				tcs.texCoords[j].v = 1.0f - myVCoords[j];
			}
			nif_uvs.push_back(tcs);

			baseUVSet = uvSetNames[i];
			has_uvs = true;

			set_num++;
		}
	}

	cs.SetTexCoordSets(nif_uvs);

	// this will hold references to the shaders used on the meshes
	MObjectArray Shaders;

	// this is used to hold indices to the materials returned in the object array
	MIntArray    FaceIndices;

	//out << "Get the connected shaders" << endl;
	// get the shaders used by the i'th mesh instance
	// Assume this is not instanced for now
	// TODO support instancing properly
	stat = visibleMeshFn.getConnectedShaders(0, Shaders, FaceIndices);

	if (stat != MS::kSuccess) {
		//out << stat.errorString().asChar() << endl;
		throw runtime_error("Failed to get connected shader list.");

	}

	vector<ComplexFace> nif_faces;


	//Add shaders to propGroup array
	vector< vector<NiPropertyRef> > propGroups;
	for (unsigned int shader_num = 0; shader_num < Shaders.length(); ++shader_num) {

		//Maya sometimes lists shaders that are not actually attached to any face.  Disregard them.
		bool shader_is_used = false;
		for (size_t f = 0; f < FaceIndices.length(); ++f) {
			if (FaceIndices[f] == shader_num) {
				shader_is_used = true;
				break;
			}
		}

		if (shader_is_used == false) {
			//Shader isn't actually used, so continue to the next one.
			continue;
		}

		//out << "Found attached shader:  ";
		//Attach all properties previously associated with this shader to
		//this NiTriShape
		MFnDependencyNode fnDep(Shaders[shader_num]);

		//Find the shader that this shading group connects to
		MPlug p = fnDep.findPlug("surfaceShader");
		MPlugArray plugs;
		p.connectedTo(plugs, true, false);
		for (unsigned int i = 0; i < plugs.length(); ++i) {
			if (plugs[i].node().hasFn(MFn::kLambert)) {
				fnDep.setObject(plugs[i].node());
				break;
			}
		}

		//out << fnDep.name().asChar() << endl;
		vector<NiPropertyRef> niProps = this->translatorData->shaders[fnDep.name().asChar()];

		propGroups.push_back(niProps);
	}
	cs.SetPropGroups(propGroups);

	//out << "Export vertex and normal data" << endl;
	// attach an iterator to the mesh
	MItMeshPolygon itPoly(mesh, &stat);
	if (stat != MS::kSuccess) {
		throw runtime_error("Failed to create polygon iterator.");
	}

	// Create a list of faces with vertex IDs, and duplicate normals so they have the same ID
	for (; !itPoly.isDone(); itPoly.next()) {
		int poly_vert_count = itPoly.polygonVertexCount(&stat);

		if (stat != MS::kSuccess) {
			throw runtime_error("Failed to get vertex count.");
		}

		//Ignore polygons with less than 3 vertices
		if (poly_vert_count < 3) {
			continue;
		}

		ComplexFace cf;

		//Assume all faces use material 0 for now
		cf.propGroupIndex = 0;

		for (int i = 0; i < poly_vert_count; ++i) {
			ComplexPoint cp;

			cp.vertexIndex = itPoly.vertexIndex(i);
			cp.normalIndex = itPoly.normalIndex(i);
			if (niColors.size() > 0) {
				int color_index;
				stat = meshFn.getFaceVertexColorIndex(itPoly.index(), i, color_index);
				if (stat != MS::kSuccess) {
					//out << stat.errorString().asChar() << endl;
					throw runtime_error("Failed to get vertex color.");
				}
				cp.colorIndex = color_index;
			}

			//Get the UV set names used by this particular vertex
			MStringArray vertUvSetNames;
			itPoly.getUVSetNames(vertUvSetNames);
			for (unsigned int j = 0; j < vertUvSetNames.length(); ++j) {
				TexCoordIndex tci;
				tci.texCoordSetIndex = uvSetNums[vertUvSetNames[j].asChar()];
				int uv_index;
				itPoly.getUVIndex(i, uv_index, &vertUvSetNames[j]);
				tci.texCoordIndex = uv_index;
				cp.texCoordIndices.push_back(tci);
			}
			cf.points.push_back(cp);
		}
		nif_faces.push_back(cf);
	}

	//Set shader/face association
	if (nif_faces.size() != FaceIndices.length()) {
		throw runtime_error("Num faces found do not match num faces reported.");
	}
	for (unsigned int face_index = 0; face_index < nif_faces.size(); ++face_index) {
		nif_faces[face_index].propGroupIndex = FaceIndices[face_index];
	}

	cs.SetFaces(nif_faces);

	//--Skin Processing--//

	//Look up any skin clusters
	if (this->translatorData->meshClusters.find(visibleMeshFn.fullPathName().asChar()) != this->translatorData->meshClusters.end()) {
		const vector<MObject> & clusters = this->translatorData->meshClusters[visibleMeshFn.fullPathName().asChar()];
		if (clusters.size() > 1) {
			throw runtime_error("Objects with multiple skin clusters affecting them are not currently supported.  Try deleting the history and re-binding them.");
		}

		vector<MObject>::const_iterator cluster = clusters.begin();
		if (cluster->isNull() != true) {
			MFnSkinCluster clusterFn(*cluster);


			//out << "Processing skin..." << endl;
			//Get path to visible mesh
			MDagPath meshPath;
			visibleMeshFn.getPath(meshPath);

			//out << "Getting a list of all verticies in this mesh" << endl;
			//Get a list of all vertices in this mesh
			MFnSingleIndexedComponent compFn;
			MObject vertices = compFn.create(MFn::kMeshVertComponent);
			MItGeometry gIt(meshPath);
			MIntArray vertex_indices(gIt.count());
			for (int vert_index = 0; vert_index < gIt.count(); ++vert_index) {
				vertex_indices[vert_index] = vert_index;
			}
			compFn.addElements(vertex_indices);

			//out << "Getting Influences" << endl;
			//Get influences
			MDagPathArray myBones;
			clusterFn.influenceObjects(myBones, &stat);

			//out << "Creating a list of NiNodeRefs of influences." << endl;
			//Create list of NiNodeRefs of influences
			vector<NiNodeRef> niBones(myBones.length());
			for (unsigned int bone_index = 0; bone_index < niBones.size(); ++bone_index) {
				const char* boneName = myBones[bone_index].fullPathName().asChar();
				if (this->translatorData->nodes.find(myBones[bone_index].fullPathName().asChar()) == this->translatorData->nodes.end()) {
					//There is a problem; one of the joints was not exported.  Abort.
					throw runtime_error("One of the joints necessary to export a bound skin was not exported.");
				}
				niBones[bone_index] = this->translatorData->nodes[myBones[bone_index].fullPathName().asChar()];
			}

			//out << "Getting weights from Maya" << endl;
			//Get weights from Maya
			MDoubleArray myWeights;
			unsigned int bone_count = myBones.length();
			stat = clusterFn.getWeights(meshPath, vertices, myWeights, bone_count);
			if (stat != MS::kSuccess) {
				//out << stat.errorString().asChar() << endl;
				throw runtime_error("Failed to get vertex weights.");
			}

			//out << "Setting skin influence list in ComplexShape" << endl;
			//Set skin information in ComplexShape
			cs.SetSkinInfluences(niBones);

			//out << "Adding weights to ComplexShape vertices" << endl;
			//out << "Number of weights:  " << myWeights.length() << endl;
			//out << "Number of bones:  " << myBones.length() << endl;
			//out << "Number of Maya vertices:  " << gIt.count() << endl;
			//out << "Number of NIF vertices:  " << int(nif_vts.size()) << endl;
			unsigned int weight_index = 0;
			SkinInfluence sk;
			for (unsigned int vert_index = 0; vert_index < nif_vts.size(); ++vert_index) {
				for (unsigned int bone_index = 0; bone_index < myBones.length(); ++bone_index) {
					//out << "vert_index:  " << vert_index << "  bone_index:  " << bone_index << "  weight_index:  " << weight_index << endl;	
					// Only bother with weights that are significant
					if (myWeights[weight_index] > 0.0f) {
						sk.influenceIndex = bone_index;
						sk.weight = float(myWeights[weight_index]);

						nif_vts[vert_index].weights.push_back(sk);
					}
					++weight_index;
				}
			}
		}

		MPlugArray connected_dismember_plugs;
		MObjectArray dismember_nodes;
		meshFn.findPlug("message").connectedTo(connected_dismember_plugs, false, true);

		bool has_valid_dismemember_partitions = true;
		int faces_count = cs.GetFaces().size();
		int current_face_index;
		vector<BodyPartList> body_parts_list;
		vector<uint> dismember_faces(faces_count, 0);

		for (int x = 0; x < connected_dismember_plugs.length(); x++) {
			MFnDependencyNode dependency_node(connected_dismember_plugs[x].node());
			if (dependency_node.typeName() == "nifDismemberPartition") {
				dismember_nodes.append(dependency_node.object());
			}
		}

		if (dismember_nodes.length() == 0) {
			has_valid_dismemember_partitions = false;
		}
		else {
			int blind_data_id;
			int blind_data_value;
			MStatus status;
			MPlug target_faces_plug;
			MItMeshPolygon it_polygons(meshFn.object());
			MString mel_command;
			MStringArray current_body_parts_flags;
			MFnDependencyNode current_dismember_node;
			MFnDependencyNode current_blind_data_node;

			//Naive sort here, there is no reason and is extremely undesirable and not recommended to have more
			//than 10-20 dismember partitions out of many reasons, so it's okay here
			//as it makes the code easier to understand
			vector<int> dismember_nodes_id(dismember_nodes.length(), -1);
			for (int x = 0; x < dismember_nodes.length(); x++) {
				current_dismember_node.setObject(dismember_nodes[x]);
				connected_dismember_plugs.clear();
				current_dismember_node.findPlug("targetFaces").connectedTo(connected_dismember_plugs, true, false);
				if (connected_dismember_plugs.length() == 0) {
					has_valid_dismemember_partitions = false;
					break;
				}
				current_blind_data_node.setObject(connected_dismember_plugs[0].node());
				dismember_nodes_id[x] = current_blind_data_node.findPlug("typeId").asInt();
			}
			for (int x = 0; x < dismember_nodes.length() - 1; x++) {
				for (int y = x + 1; y < dismember_nodes.length(); y++) {
					if (dismember_nodes_id[x] > dismember_nodes_id[y]) {
						MObject aux = dismember_nodes[x];
						blind_data_id = dismember_nodes_id[x];
						dismember_nodes[x] = dismember_nodes[y];
						dismember_nodes_id[x] = dismember_nodes_id[y];
						dismember_nodes[y] = aux;
						dismember_nodes_id[y] = blind_data_id;
					}
				}
			}

			for (int x = 0; x < dismember_nodes.length(); x++) {
				current_dismember_node.setObject(dismember_nodes[x]);
				target_faces_plug = current_dismember_node.findPlug("targetFaces");
				connected_dismember_plugs.clear();
				target_faces_plug.connectedTo(connected_dismember_plugs, true, false);
				if (connected_dismember_plugs.length() > 0) {
					current_blind_data_node.setObject(connected_dismember_plugs[0].node());
					current_face_index = 0;
					blind_data_id = current_blind_data_node.findPlug("typeId").asInt();
					for (it_polygons.reset(); !it_polygons.isDone(); it_polygons.next()) {
						if (it_polygons.polygonVertexCount() >= 3) {
							status = meshFn.getIntBlindData(it_polygons.index(), MFn::Type::kMeshPolygonComponent, blind_data_id, "dismemberValue", blind_data_value);
							if (status == MStatus::kSuccess && blind_data_value == 1 &&
								meshFn.hasBlindDataComponentId(it_polygons.index(), MFn::Type::kMeshPolygonComponent, blind_data_id)) {
								dismember_faces[current_face_index] = x;
							}
							current_face_index++;
						}
					}
				}
				else {
					has_valid_dismemember_partitions = false;
					break;
				}

				mel_command = "getAttr ";
				mel_command += current_dismember_node.name();
				mel_command += ".bodyPartsFlags";
				status = MGlobal::executeCommand(mel_command, current_body_parts_flags);
				BSDismemberBodyPartType body_part_type = NifDismemberPartition::stringArrayToBodyPartType(current_body_parts_flags);
				current_body_parts_flags.clear();

				mel_command = "getAttr ";
				mel_command += current_dismember_node.name();
				mel_command += ".partsFlags";
				status = MGlobal::executeCommand(mel_command, current_body_parts_flags);
				BSPartFlag part_type = NifDismemberPartition::stringArrayToPart(current_body_parts_flags);
				current_body_parts_flags.clear();

				BodyPartList body_part;
				body_part.bodyPart = body_part_type;
				body_part.partFlag = part_type;
				body_parts_list.push_back(body_part);
			}
		}

		if (has_valid_dismemember_partitions == false) {
			MGlobal::displayWarning("No proper dismember partitions, generating default ones for " + meshFn.name());

			for (int x = 0; x < dismember_faces.size(); x++) {
				dismember_faces[x] = 0;
			}

			BodyPartList body_part;
			body_part.bodyPart = (BSDismemberBodyPartType)0;
			body_part.partFlag = (BSPartFlag)(PF_EDITOR_VISIBLE | PF_START_NET_BONESET);
			body_parts_list.clear();
			body_parts_list.push_back(body_part);
		}

		cs.SetDismemberPartitionsBodyParts(body_parts_list);
		cs.SetDismemberPartitionsFaces(dismember_faces);
	}

	//out << "Setting vertex info" << endl;
	//Set vertex info now that any skins have been processed
	cs.SetVertices(nif_vts);

	//ComplexShape is now complete, so split it

	//Get parent
	NiNodeRef parNode = this->translatorUtils->GetDAGParent(dagNode);
	Matrix44 transform = Matrix44::IDENTITY;
	vector<NiNodeRef> influences = cs.GetSkinInfluences();
	if (influences.size() > 0) {
		//This is a skin, so we use the common ancestor of all influences
		//as the parent
		vector<NiAVObjectRef> objects;
		for (size_t i = 0; i < influences.size(); ++i) {
			objects.push_back(StaticCast<NiAVObject>(influences[i]));
		}

		//Get world transform of existing parent
		Matrix44 oldParWorld = parNode->GetWorldTransform();

		//Set new parent node
		parNode = FindCommonAncestor(objects);

		transform = oldParWorld * parNode->GetWorldTransform().Inverse();
	}

	//Get transform using temporary NiAVObject
	NiAVObjectRef tempAV = new NiAVObject;
	this->nodeExporter->ExportAV(tempAV, dagNode);

	NiAVObjectRef avObj;
	if (this->translatorOptions->exportTangentSpace == "falloutskyrimtangentspace") {
		//out << "Split ComplexShape from " << meshFn.name().asChar() << endl;
		avObj = cs.Split(parNode, tempAV->GetLocalTransform() * transform, this->translatorOptions->exportBonesPerSkinPartition,
			this->translatorOptions->exportAsTriStrips, true, this->translatorOptions->exportMinimumVertexWeight, 16);
	}
	else {
		avObj = cs.Split(parNode, tempAV->GetLocalTransform() * transform, this->translatorOptions->exportBonesPerSkinPartition,
			this->translatorOptions->exportAsTriStrips, false, this->translatorOptions->exportMinimumVertexWeight);
	}


	//out << "Get the NiAVObject portion of the root of the split" <<endl;
	//Get the NiAVObject portion of the root of the split
	avObj->SetName(tempAV->GetName());
	avObj->SetVisibility(tempAV->GetVisibility());
	avObj->SetFlags(tempAV->GetFlags());

	//If polygon mesh is hidden, hide tri_shape
	MPlug vis = visibleMeshFn.findPlug(MString("visibility"));
	bool visibility;
	vis.getValue(visibility);


	NiNodeRef splitRoot = DynamicCast<NiNode>(avObj);
	if (splitRoot != NULL) {
		//Root is a NiNode with NiTriBasedGeom children.
		vector<NiAVObjectRef> children = splitRoot->GetChildren();
		for (unsigned c = 0; c < children.size(); ++c) {
			//Set the default collision propogation flag to "use triangles"
			children[c]->SetFlags(2);
			// Make the mesh invisible if necessary
			if (visibility == false) {
				children[c]->SetVisibility(false);
			}
		}

	}
	else {
		//Root must be a NiTriBasedGeom.  Make it invisible if necessary
		if (visibility == false) {
			avObj->SetVisibility(false);
		}
	}

}
Ejemplo n.º 14
0
bool SceneShapeUI::select( MSelectInfo &selectInfo, MSelectionList &selectionList, MPointArray &worldSpaceSelectPts ) const
{
	MStatus s;

	// early out if we're not selectable. we always allow components to be selected if we're highlighted,
	// but we don't allow ourselves to be selected as a whole unless meshes are in the selection mask.
	// it's not ideal that we act like a mesh, but it's at least consistent with the drawing mask we use.
	if( selectInfo.displayStatus() != M3dView::kHilite )
	{
		MSelectionMask meshMask( MSelectionMask::kSelectMeshes );
		// Apparently selectInfo.selectable() still returns true when meshes are not
		// displayed by the M3dView, so we are also testing the objectDisplay status.
		// This was last confirmed in Maya 2014, and is presumably a Maya bug.
		if( !selectInfo.selectable( meshMask ) || !selectInfo.objectDisplayStatus( M3dView::kDisplayMeshes ) )
		{
			return false;
		}
	}

	// early out if we have no scene to draw
	SceneShape *sceneShape = static_cast<SceneShape *>( surfaceShape() );
	if( !sceneShape->getSceneInterface() )
	{
		return false;
	}

	IECoreGL::ConstScenePtr scene = sceneShape->glScene();
	if( !scene )
	{
		return false;
	}

	// we want to perform the selection using an IECoreGL::Selector, so we
	// can avoid the performance penalty associated with using GL_SELECT mode.
	// that means we don't really want to call view.beginSelect(), but we have to
	// call it just to get the projection matrix for our own selection, because as far
	// as I can tell, there is no other way of getting it reliably.

	M3dView view = selectInfo.view();
	view.beginSelect();
		Imath::M44d projectionMatrix;
		glGetDoublev( GL_PROJECTION_MATRIX, projectionMatrix.getValue() );
	view.endSelect();
		
	view.beginGL();
	
		glMatrixMode( GL_PROJECTION );
		glLoadMatrixd( projectionMatrix.getValue() );
		
		IECoreGL::Selector::Mode selectionMode = IECoreGL::Selector::IDRender;
		if( selectInfo.displayStatus() == M3dView::kHilite && !selectInfo.singleSelection() )
		{
			selectionMode = IECoreGL::Selector::OcclusionQuery;
		}

		std::vector<IECoreGL::HitRecord> hits;
		{
			IECoreGL::Selector selector( Imath::Box2f( Imath::V2f( 0 ), Imath::V2f( 1 ) ), selectionMode, hits );
				
			IECoreGL::State::bindBaseState();
			selector.baseState()->bind();
			scene->render( selector.baseState() );

			if( selectInfo.displayStatus() != M3dView::kHilite )
			{
				// We're not in component selection mode. We'd like to be able to select the scene shape
				// using the bounding box so we draw it too but only if it is visible
				MPlug pDrawBound( sceneShape->thisMObject(), SceneShape::aDrawRootBound );
				bool drawBound;
				pDrawBound.getValue( drawBound );
				if( drawBound )
				{
					IECoreGL::BoxPrimitive::renderWireframe( IECore::convert<Imath::Box3f>( sceneShape->boundingBox() ) );
				}
			}
		}
						
	view.endGL();
	
	if( hits.empty() )
	{
		return false;
	}
	
	// iterate over the hits, converting them into components and also finding
	// the closest one.
	MIntArray componentIndices;
	
	float depthMin = std::numeric_limits<float>::max();
	int depthMinIndex = -1;
	for( unsigned int i=0, e = hits.size(); i < e; i++ )
	{		
		if( hits[i].depthMin < depthMin )
		{
			depthMin = hits[i].depthMin;
			depthMinIndex = componentIndices.length();
		}
		int index = sceneShape->selectionIndex( IECoreGL::NameStateComponent::nameFromGLName( hits[i].name ) );
		componentIndices.append( index );
	}
	
	assert( depthMinIndex >= 0 );

	// figure out the world space location of the closest hit	
	MDagPath camera;
	view.getCamera( camera );
	
	MPoint worldIntersectionPoint;
	selectionRayToWorldSpacePoint( camera, selectInfo, depthMin, worldIntersectionPoint );

	// turn the processed hits into appropriate changes to the current selection
	if( selectInfo.displayStatus() == M3dView::kHilite )
	{
		// selecting components
		MFnSingleIndexedComponent fnComponent;
		MObject component = fnComponent.create( MFn::kMeshPolygonComponent, &s ); assert( s );
	
		if( selectInfo.singleSelection() )
		{
			fnComponent.addElement( componentIndices[depthMinIndex] );
		}
		else
		{
			fnComponent.addElements( componentIndices );
		}
		
		MSelectionList items;
		items.add( selectInfo.multiPath(), component );
		
		MDagPath path = selectInfo.multiPath();

		selectInfo.addSelection(
			items, worldIntersectionPoint,
			selectionList, worldSpaceSelectPts,
			MSelectionMask::kSelectMeshFaces,
			true
		);
		
	}
	else
	{
		// Check if we should be able to select that object
		MPlug pObjectOnly( sceneShape->thisMObject(), SceneShape::aObjectOnly );
		bool objectOnly;
		pObjectOnly.getValue( objectOnly );
		if( objectOnly && !sceneShape->getSceneInterface()->hasObject() )
		{
			return true;
		}
		
		// selecting objects
		MSelectionList item;
		item.add( selectInfo.selectPath() );

		selectInfo.addSelection(
			item, worldIntersectionPoint,
			selectionList, worldSpaceSelectPts,
			MSelectionMask::kSelectMeshes,
			false
		);
	}
	
	return true;
}
Ejemplo n.º 15
0
CacheMeshSampler::AttributeSet::AttributeSet(
    MFnMesh& mesh,
    const bool needUVs,
    const bool useBaseTessellation
)
    :fNumWires(0),
    fNumTriangles(0),
    fNumVerts(0)
{
    // Refresh the internal shape, otherwise topo changes make mesh.numPolygons() crash.
    // Note that buildShaderAssignmentGroups() also call mesh.numPolygons().
    mesh.syncObject();

    MDagPath dagPath;
    mesh.getPath(dagPath);

    // build a geometry request and add requirements to it.
    MHWRender::MGeometryRequirements geomRequirements;

    // Build descriptors to request the positions, normals and UVs	
    MVertexBufferDescriptor posDesc("", MGeometry::kPosition, MGeometry::kFloat, 3);
    MVertexBufferDescriptor normalDesc("", MGeometry::kNormal, MGeometry::kFloat, 3);
    MVertexBufferDescriptor uvDesc(mesh.currentUVSetName(), MGeometry::kTexture, MGeometry::kFloat, 2);

    // add the descriptors to the geometry requirements
    geomRequirements.addVertexRequirement(posDesc);
    geomRequirements.addVertexRequirement(normalDesc);
    if (needUVs)
        geomRequirements.addVertexRequirement(uvDesc);

    MString noName; // we do not need custom named index buffers here.

    // create a component to include all elements.
    MFnSingleIndexedComponent comp;
    MObject compObj = comp.create(MFn::kMeshPolygonComponent);
    comp.setCompleteData(mesh.numPolygons());

	// create edge component
	MFnSingleIndexedComponent edgeComp;
	MObject edgeCompObj = edgeComp.create(MFn::kMeshEdgeComponent);
	edgeComp.setCompleteData(mesh.numEdges());

    // Add the edge line index buffer to the requirements
    MIndexBufferDescriptor edgeDesc(MIndexBufferDescriptor::kEdgeLine, noName, MGeometry::kLines, 2, edgeCompObj);
    geomRequirements.addIndexingRequirement(edgeDesc);

    // add a triangle buffer to the requirements
    MIndexBufferDescriptor triangleDesc(MIndexBufferDescriptor::kTriangle, noName, MGeometry::kTriangles, 3, compObj);
    geomRequirements.addIndexingRequirement(triangleDesc);

    typedef IndexBuffer::index_t index_t;

    // We ignore the Smooth Preview option on the mesh shape node when
    // using base tessellation.
    int extractorOptions = MHWRender::kPolyGeom_Normal;
    if (useBaseTessellation) {
        extractorOptions |= MHWRender::kPolyGeom_BaseMesh;
    }

    // create an extractor to get the geometry
	MStatus status;
    MHWRender::MGeometryExtractor extractor(geomRequirements,dagPath, extractorOptions, &status);
	if (MS::kFailure==status)
		return;

    // get the number of vertices from the extractor
    unsigned int numVertices   = extractor.vertexCount();
    // get the number of primitives (triangles, lines, etc.)
    unsigned int numWires = extractor.primitiveCount(edgeDesc);

    // create the arrays that the generator will fill
    boost::shared_array<float> vertices(new float[numVertices*posDesc.stride()]);
    boost::shared_array<float> normals(new float[numVertices*normalDesc.stride()]);
    boost::shared_array<float> uvs(new float[numVertices*uvDesc.stride()]);

    unsigned int minBufferSize = extractor.minimumBufferSize(numWires, edgeDesc.primitive());
    boost::shared_array<index_t> wireframeIdx(new index_t[minBufferSize]);

    // populate the index buffer for the edges
    if (MS::kFailure==extractor.populateIndexBuffer(wireframeIdx.get(), numWires, edgeDesc))
        return;
    // populate the vertex buffers you are interested in. (pos, normal, and uv)
    if (MS::kFailure==extractor.populateVertexBuffer(vertices.get(), numVertices, posDesc))
        return;
    if (MS::kFailure==extractor.populateVertexBuffer(normals.get(), numVertices, normalDesc))
        return; 
    if (needUVs && MS::kFailure==extractor.populateVertexBuffer(uvs.get(), numVertices, uvDesc))
        return; 

    // populate the index buffers for all the triangle components
    {
        std::vector<boost::shared_ptr<Array<index_t> > > trgIdxGrps;

        // get the index buffer count from the extractor
        unsigned int numTriangles = extractor.primitiveCount(triangleDesc);
        minBufferSize = extractor.minimumBufferSize(numTriangles, triangleDesc.primitive());
        boost::shared_array<index_t> triangleIdx(new index_t[minBufferSize]);

        if (numTriangles != 0) 
        {
            if (MS::kFailure==extractor.populateIndexBuffer(triangleIdx.get(), numTriangles, triangleDesc))
                return;
            fNumTriangles += (size_t)numTriangles;
        }

        trgIdxGrps.push_back(SharedArray<index_t>::create(
            triangleIdx, 3 * numTriangles));

        for(size_t i=0, offset=0; i<trgIdxGrps.size(); ++i) {
            fTriangleVertIndices.push_back(IndexBuffer::create(
                trgIdxGrps[0], offset, offset + trgIdxGrps[i]->size()));
            offset += trgIdxGrps[i]->size();
        }
    }

    fNumWires     = (size_t)numWires;
    fNumVerts     = (size_t)numVertices;

    fWireVertIndices = IndexBuffer::create(
        SharedArray<index_t>::create( wireframeIdx, 2 * fNumWires));   

    fPositions = VertexBuffer::createPositions(
        SharedArray<float>::create( vertices, 3 * fNumVerts));

    fNormals = VertexBuffer::createNormals(
        SharedArray<float>::create( normals, 3 * fNumVerts));

    if (needUVs) {
        fUVs = VertexBuffer::createUVs(
            SharedArray<float>::create(uvs, 2 * fNumVerts));
    }

    fBoundingBox = mesh.boundingBox();

    // Check visibility 
    fVisibility = ShapeVisibilityChecker(mesh.object()).isVisible();
}