bool
VertexPolyColourCommand::ReadColoursFromNode(MDagPath& dagPath, MColorArray& colors, bool isSource)
{
	MStatus status;

	MObject ourNode;
	if (!FindNodeOnMesh(dagPath,ourNode))
		return false;

	// Pass the component list down to the node.
	// To do so, we create/retrieve the current plug
	// from the uvList attribute on the node and simply
	// set the value to our component list.
	//

	{
		MPlug* colourPlug = NULL;
		if (isSource)
		{
			colourPlug = new MPlug(ourNode, PolyColourNode::m_colors);
		}
		else
		{
			colourPlug = new MPlug(ourNode, PolyColourNode::m_colorsDest);
		}
		MObject obj;
		status = colourPlug->getValue(obj);
		delete colourPlug;
		MFnDoubleArrayData wrapper(obj);

		unsigned int numDoubles=wrapper.length();
		unsigned int numColors=numDoubles/4;

#ifdef _DEBUG
		assert(numColors == colors.length());
#endif
		if (numColors == colors.length())
		{
			int idx = 0;
			for (unsigned int i = 0; i < numColors; i++)
			{
				colors[i].r=(float)wrapper[idx]; idx++;
				colors[i].g=(float)wrapper[idx]; idx++;
				colors[i].b=(float)wrapper[idx]; idx++;
				colors[i].a=(float)wrapper[idx]; idx++;
			}
		}
	}

	return true;
}
void
VertexPolyColourCommand::WriteColoursToNode(MDagPath& dagPath, MColorArray& colors, bool isSource)
{
	MStatus status;

	// Try to find the colour node attached to the mesh
	// If it's not found, create it
	MObject ourNode;
	if (!FindNodeOnMesh(dagPath,ourNode))
	{
		CreateNodeOnMesh(dagPath, ourNode);
	}

	// Send the selection to the node
	{
		// Pass the component list down to the node.
		// To do so, we create/retrieve the current plug
		// from the uvList attribute on the node and simply
		// set the value to our component list.
		//

		MDoubleArray dcols;
		dcols.setLength(colors.length()*4);
		int idx = 0;
		for (unsigned int i=0; i<colors.length(); i++)
		{
			dcols[idx] = (double)colors[i].r; idx++;
			dcols[idx] = (double)colors[i].g; idx++;
			dcols[idx] = (double)colors[i].b; idx++;
			dcols[idx] = (double)colors[i].a; idx++;
		}
		MFnDoubleArrayData wrapper;
		wrapper.create(dcols,&status);

		MPlug* colourPlug = NULL;
		if (isSource)
		{
			colourPlug = new MPlug(ourNode, PolyColourNode::m_colors);
		}
		else
		{
			colourPlug = new MPlug(ourNode, PolyColourNode::m_colorsDest);
		}

		// Warning, we have to do this as GCC doesn't like to pass by temporary reference
		MObject wrapperObjRef = wrapper.object();
		status = colourPlug->setValue(wrapperObjRef);
		delete colourPlug;
	}
}
/* static */
bool
UsdMayaTranslatorMesh::_AssignColorSetPrimvarToMesh(
        const UsdGeomMesh& primSchema,
        const UsdGeomPrimvar& primvar,
        MFnMesh& meshFn)
{
    const TfToken& primvarName = primvar.GetPrimvarName();
    const SdfValueTypeName& typeName = primvar.GetTypeName();

    MString colorSetName(primvarName.GetText());

    // If the primvar is displayOpacity and it is a FloatArray, check if
    // displayColor is authored. If not, we'll import this 'displayOpacity'
    // primvar as a 'displayColor' color set. This supports cases where the
    // user created a single channel value for displayColor.
    // Note that if BOTH displayColor and displayOpacity are authored, they will
    // be imported as separate color sets. We do not attempt to combine them
    // into a single color set.
    if (primvarName == UsdMayaMeshColorSetTokens->DisplayOpacityColorSetName &&
            typeName == SdfValueTypeNames->FloatArray) {
        if (!UsdMayaRoundTripUtil::IsAttributeUserAuthored(primSchema.GetDisplayColorPrimvar())) {
            colorSetName = UsdMayaMeshColorSetTokens->DisplayColorColorSetName.GetText();
        }
    }

    // We'll need to convert colors from linear to display if this color set is
    // for display colors.
    const bool isDisplayColor =
        (colorSetName == UsdMayaMeshColorSetTokens->DisplayColorColorSetName.GetText());

    // Get the raw data before applying any indexing. We'll only populate one
    // of these arrays based on the primvar's typeName, and we'll also set the
    // color representation so we know which array to use later.
    VtFloatArray alphaArray;
    VtVec3fArray rgbArray;
    VtVec4fArray rgbaArray;
    MFnMesh::MColorRepresentation colorRep;
    size_t numValues = 0;

    MStatus status = MS::kSuccess;

    if (typeName == SdfValueTypeNames->FloatArray) {
        colorRep = MFnMesh::kAlpha;
        if (!primvar.Get(&alphaArray) || alphaArray.empty()) {
            status = MS::kFailure;
        } else {
            numValues = alphaArray.size();
        }
    } else if (typeName == SdfValueTypeNames->Float3Array ||
               typeName == SdfValueTypeNames->Color3fArray) {
        colorRep = MFnMesh::kRGB;
        if (!primvar.Get(&rgbArray) || rgbArray.empty()) {
            status = MS::kFailure;
        } else {
            numValues = rgbArray.size();
        }
    } else if (typeName == SdfValueTypeNames->Float4Array ||
               typeName == SdfValueTypeNames->Color4fArray) {
        colorRep = MFnMesh::kRGBA;
        if (!primvar.Get(&rgbaArray) || rgbaArray.empty()) {
            status = MS::kFailure;
        } else {
            numValues = rgbaArray.size();
        }
    } else {
        TF_WARN("Unsupported color set primvar type '%s' for primvar '%s' on "
                "mesh: %s",
                typeName.GetAsToken().GetText(),
                primvarName.GetText(),
                primvar.GetAttr().GetPrimPath().GetText());
        return false;
    }

    if (status != MS::kSuccess || numValues == 0) {
        TF_WARN("Could not read color set values from primvar '%s' on mesh: %s",
                primvarName.GetText(),
                primvar.GetAttr().GetPrimPath().GetText());
        return false;
    }

    VtIntArray assignmentIndices;
    int unauthoredValuesIndex = -1;
    if (primvar.GetIndices(&assignmentIndices)) {
        // The primvar IS indexed, so the indices array is what determines the
        // number of color values.
        numValues = assignmentIndices.size();
        unauthoredValuesIndex = primvar.GetUnauthoredValuesIndex();
    }

    // Go through the color data and translate the values into MColors in the
    // colorArray, taking into consideration that indexed data may have been
    // authored sparsely. If the assignmentIndices array is empty then the data
    // is NOT indexed.
    // Note that with indexed data, the data is added to the arrays in ascending
    // component ID order according to the primvar's interpolation (ascending
    // face ID for uniform interpolation, ascending vertex ID for vertex
    // interpolation, etc.). This ordering may be different from the way the
    // values are ordered in the primvar. Because of this, we recycle the
    // assignmentIndices array as we go to store the new mapping from component
    // index to color index.
    MColorArray colorArray;
    for (size_t i = 0; i < numValues; ++i) {
        int valueIndex = i;

        if (i < assignmentIndices.size()) {
            // The data is indexed, so consult the indices array for the
            // correct index into the data.
            valueIndex = assignmentIndices[i];

            if (valueIndex == unauthoredValuesIndex) {
                // This component is unauthored, so just update the
                // mapping in assignmentIndices and then skip the value.
                // We don't actually use the value at the unassigned index.
                assignmentIndices[i] = -1;
                continue;
            }

            // We'll be appending a new value, so the current length of the
            // array gives us the new value's index.
            assignmentIndices[i] = colorArray.length();
        }

        GfVec4f colorValue(1.0);

        switch(colorRep) {
            case MFnMesh::kAlpha:
                colorValue[3] = alphaArray[valueIndex];
                break;
            case MFnMesh::kRGB:
                colorValue[0] = rgbArray[valueIndex][0];
                colorValue[1] = rgbArray[valueIndex][1];
                colorValue[2] = rgbArray[valueIndex][2];
                break;
            case MFnMesh::kRGBA:
                colorValue[0] = rgbaArray[valueIndex][0];
                colorValue[1] = rgbaArray[valueIndex][1];
                colorValue[2] = rgbaArray[valueIndex][2];
                colorValue[3] = rgbaArray[valueIndex][3];
                break;
            default:
                break;
        }

        if (isDisplayColor) {
            colorValue = UsdMayaColorSpace::ConvertLinearToMaya(colorValue);
        }

        MColor mColor(colorValue[0], colorValue[1], colorValue[2], colorValue[3]);
        colorArray.append(mColor);
    }

    // colorArray now stores all of the values and any unassigned components
    // have had their indices set to -1, so update the unauthored values index.
    unauthoredValuesIndex = -1;

    const bool clamped = UsdMayaRoundTripUtil::IsPrimvarClamped(primvar);

    status = meshFn.createColorSet(colorSetName, nullptr, clamped, colorRep);
    if (status != MS::kSuccess) {
        TF_WARN("Unable to create color set '%s' for mesh: %s",
                colorSetName.asChar(),
                meshFn.fullPathName().asChar());
        return false;
    }

    // Create colors on the mesh from the values we collected out of the
    // primvar. We'll assign mesh components to these values below.
    status = meshFn.setColors(colorArray, &colorSetName, colorRep);
    if (status != MS::kSuccess) {
        TF_WARN("Unable to set color data on color set '%s' for mesh: %s",
                colorSetName.asChar(),
                meshFn.fullPathName().asChar());
        return false;
    }

    const TfToken& interpolation = primvar.GetInterpolation();

    // Build an array of value assignments for each face vertex in the mesh.
    // Any assignments left as -1 will not be assigned a value.
    MIntArray colorIds = _GetMayaFaceVertexAssignmentIds(meshFn,
                                                         interpolation,
                                                         assignmentIndices,
                                                         unauthoredValuesIndex);

    status = meshFn.assignColors(colorIds, &colorSetName);
    if (status != MS::kSuccess) {
        TF_WARN("Could not assign color values to color set '%s' on mesh: %s",
                colorSetName.asChar(),
                meshFn.fullPathName().asChar());
        return false;
    }

    return true;
}
/// Collect values from the color set named \p colorSet.
/// If \p isDisplayColor is true and this color set represents displayColor,
/// the unauthored/unpainted values in the color set will be filled in using
/// the shader values in \p shadersRGBData and \p shadersAlphaData if available.
/// Values are gathered per face vertex, but then the data is compressed to
/// vertex, uniform, or constant interpolation if possible.
/// Unauthored/unpainted values will be given the index -1.
bool MayaMeshWriter::_GetMeshColorSetData(
        MFnMesh& mesh,
        const MString& colorSet,
        bool isDisplayColor,
        const VtArray<GfVec3f>& shadersRGBData,
        const VtArray<float>& shadersAlphaData,
        const VtArray<int>& shadersAssignmentIndices,
        VtArray<GfVec3f>* colorSetRGBData,
        VtArray<float>* colorSetAlphaData,
        TfToken* interpolation,
        VtArray<int>* colorSetAssignmentIndices,
        MFnMesh::MColorRepresentation* colorSetRep,
        bool* clamped)
{
    // If there are no colors, return immediately as failure.
    if (mesh.numColors(colorSet) == 0) {
        return false;
    }

    MColorArray colorSetData;
    const MColor unsetColor(-FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX);
    if (mesh.getFaceVertexColors(colorSetData, &colorSet, &unsetColor)
            == MS::kFailure) {
        return false;
    }

    if (colorSetData.length() == 0) {
        return false;
    }

    // Get the color set representation and clamping.
    *colorSetRep = mesh.getColorRepresentation(colorSet);
    *clamped = mesh.isColorClamped(colorSet);

    // We'll populate the assignment indices for every face vertex, but we'll
    // only push values into the data if the face vertex has a value. All face
    // vertices are initially unassigned/unauthored.
    colorSetRGBData->clear();
    colorSetAlphaData->clear();
    colorSetAssignmentIndices->assign((size_t)colorSetData.length(), -1);
    *interpolation = UsdGeomTokens->faceVarying;

    // Loop over every face vertex to populate the value arrays.
    MItMeshFaceVertex itFV(mesh.object());
    unsigned int fvi = 0;
    for (itFV.reset(); !itFV.isDone(); itFV.next(), ++fvi) {
        // If this is a displayColor color set, we may need to fallback on the
        // bound shader colors/alphas for this face in some cases. In
        // particular, if the color set is alpha-only, we fallback on the
        // shader values for the color. If the color set is RGB-only, we
        // fallback on the shader values for alpha only. If there's no authored
        // color for this face vertex, we use both the color AND alpha values
        // from the shader.
        bool useShaderColorFallback = false;
        bool useShaderAlphaFallback = false;
        if (isDisplayColor) {
            if (colorSetData[fvi] == unsetColor) {
                useShaderColorFallback = true;
                useShaderAlphaFallback = true;
            } else if (*colorSetRep == MFnMesh::kAlpha) {
                // The color set does not provide color, so fallback on shaders.
                useShaderColorFallback = true;
            } else if (*colorSetRep == MFnMesh::kRGB) {
                // The color set does not provide alpha, so fallback on shaders.
                useShaderAlphaFallback = true;
            }
        }

        // If we're exporting displayColor and we use the value from the color
        // set, we need to convert it to linear.
        bool convertDisplayColorToLinear = isDisplayColor;

        // Shader values for the mesh could be constant
        // (shadersAssignmentIndices is empty) or uniform.
        int faceIndex = itFV.faceId();
        if (useShaderColorFallback) {
            // There was no color value in the color set to use, so we use the
            // shader color, or the default color if there is no shader color.
            // This color will already be in linear space, so don't convert it
            // again.
            convertDisplayColorToLinear = false;

            int valueIndex = -1;
            if (shadersAssignmentIndices.empty()) {
                if (shadersRGBData.size() == 1) {
                    valueIndex = 0;
                }
            } else if (faceIndex >= 0 && 
                static_cast<size_t>(faceIndex) < shadersAssignmentIndices.size()) {

                int tmpIndex = shadersAssignmentIndices[faceIndex];
                if (tmpIndex >= 0 && 
                    static_cast<size_t>(tmpIndex) < shadersRGBData.size()) {
                    valueIndex = tmpIndex;
                }
            }
            if (valueIndex >= 0) {
                colorSetData[fvi][0] = shadersRGBData[valueIndex][0];
                colorSetData[fvi][1] = shadersRGBData[valueIndex][1];
                colorSetData[fvi][2] = shadersRGBData[valueIndex][2];
            } else {
                // No shader color to fallback on. Use the default shader color.
                colorSetData[fvi][0] = _ShaderDefaultRGB[0];
                colorSetData[fvi][1] = _ShaderDefaultRGB[1];
                colorSetData[fvi][2] = _ShaderDefaultRGB[2];
            }
        }
        if (useShaderAlphaFallback) {
            int valueIndex = -1;
            if (shadersAssignmentIndices.empty()) {
                if (shadersAlphaData.size() == 1) {
                    valueIndex = 0;
                }
            } else if (faceIndex >= 0 && 
                static_cast<size_t>(faceIndex) < shadersAssignmentIndices.size()) {
                int tmpIndex = shadersAssignmentIndices[faceIndex];
                if (tmpIndex >= 0 && 
                    static_cast<size_t>(tmpIndex) < shadersAlphaData.size()) {
                    valueIndex = tmpIndex;
                }
            }
            if (valueIndex >= 0) {
                colorSetData[fvi][3] = shadersAlphaData[valueIndex];
            } else {
                // No shader alpha to fallback on. Use the default shader alpha.
                colorSetData[fvi][3] = _ShaderDefaultAlpha;
            }
        }

        // If we have a color/alpha value, add it to the data to be returned.
        if (colorSetData[fvi] != unsetColor) {
            GfVec3f rgbValue = _ColorSetDefaultRGB;
            float alphaValue = _ColorSetDefaultAlpha;

            if (useShaderColorFallback              || 
                    (*colorSetRep == MFnMesh::kRGB) || 
                    (*colorSetRep == MFnMesh::kRGBA)) {
                rgbValue = _LinearColorFromColorSet(colorSetData[fvi],
                                                    convertDisplayColorToLinear);
            }
            if (useShaderAlphaFallback                || 
                    (*colorSetRep == MFnMesh::kAlpha) || 
                    (*colorSetRep == MFnMesh::kRGBA)) {
                alphaValue = colorSetData[fvi][3];
            }

            colorSetRGBData->push_back(rgbValue);
            colorSetAlphaData->push_back(alphaValue);
            (*colorSetAssignmentIndices)[fvi] = colorSetRGBData->size() - 1;
        }
    }

    _MergeEquivalentColorSetValues(colorSetRGBData,
                                   colorSetAlphaData,
                                   colorSetAssignmentIndices);
    PxrUsdMayaUtil::CompressFaceVaryingPrimvarIndices(mesh,
                                                      interpolation,
                                                      colorSetAssignmentIndices);

    return true;
}
Exemple #5
0
// get the mesh informations from the current time and save it in the meshDataList
void MayaObject::addMeshData()
{
	MeshData mdata;

	if (this->hasBifrostVelocityChannel())
	{
		bool doMb = this->motionBlurred;
		std::shared_ptr<RenderGlobals> renderGlobals = MayaTo::getWorldPtr()->worldRenderGlobalsPtr;		
		doMb = doMb && renderGlobals->doMb;

		Logging::debug(MString("Found bifrost velocity data for object: ") + this->shortName);
		if ((this->meshDataList.size() == 2) || !doMb)
		{
			Logging::debug("Bifrost mesh already has two motion steps or mb is turned off for it -> skipping");
			return;
		}

		this->getMeshData(mdata.points, mdata.normals);

		// if we have a bifrost velocity channel I suppose this is a bifost mesh and there is no need to 
		// save the mesh motion steps because it has changing topology what does not work in most renderers
		// so we save only the very first motion step and derive the other steps from velocity channel.
		// and we simply produce two steps only because everything else would not make sense.

		MFnMesh meshFn(this->mobject);
		if (meshFn.hasColorChannels("bifrostVelocity"))
		{
			MColorArray colors;
			MString colorSetName = "bifrostVelocity";
			meshFn.getVertexColors(colors, &colorSetName);

			if (colors.length() == mdata.points.length())
			{
				for (uint ptId = 0; ptId < mdata.points.length(); ptId++)
				{
					MColor c = colors[ptId];
					MVector v = MVector(c.r, c.g, c.b);
					mdata.points[ptId] -= v * 0.5 / 24.0;
				}
				this->meshDataList.push_back(mdata);
				for (uint ptId = 0; ptId < mdata.points.length(); ptId++)
				{
					MColor c = colors[ptId];
					MVector v = MVector(c.r, c.g, c.b);
					mdata.points[ptId] += v * 0.5 / 24.0;
				}
				this->meshDataList.push_back(mdata);
			}
		}else{
			Logging::debug("Bifrost mesh has no velocity data, no motionblur.");
			if (this->meshDataList.size() == 0)
				this->meshDataList.push_back(mdata);
		}
	}
	else{
		this->getMeshData(mdata.points, mdata.normals);
		int np = mdata.points.length();
		this->meshDataList.push_back(mdata);

		for (auto md : this->meshDataList)
		{
			np = md.points.length();
		}
	}

}
void
VertexPolyColourCommand::PrepareMesh(VertexColourJob& job)
{
	MDGModifier* modifier = NULL;

	// If the mesh doesn't have any vertex colours yet, create some default ones
#if (MAYA_API_VERSION > 650)
	bool hasVertexColours = true;
	if (0 == job.meshData->mesh->numColorSets())
	{
		hasVertexColours = false;
	}
	else
	{
		// Check the color set isn't the VCP_MasterComp used by the layers system
		if (job.meshData->mesh->numColorSets() == 1 && job.meshData->hasLayers)
		{
			hasVertexColours = false;
		}
	}

	if (!hasVertexColours)
	{
		if (m_isUndoable)
			modifier = new MDGModifier;

		// Create a colour set
		MString colorSetName("colorSet1");
		job.meshData->mesh->createColorSet(colorSetName, modifier);
		job.meshData->mesh->setCurrentColorSetName(colorSetName, modifier);

		// Create default vertex colours
		MIntArray vertList;
		MColorArray colors;
		unsigned int numVerts = (unsigned int)job.meshData->mesh->numVertices();
		vertList.setLength(numVerts);
		colors.setLength(numVerts);
		unsigned int i = 0;
		for (i = 0; i < numVerts; i++)
			vertList[i] = i;
		for (i = 0; i < numVerts; i++)
			colors[i].set(MColor::kRGB, 0.37f, 0.37f, 0.37f, 1.0f);		// default rgb is default 0.37

		// Fill vertex colours
		job.meshData->mesh->setVertexColors(colors, vertList, modifier);

		// If the mesh has layer data, set the active layer
		if (job.meshData->hasLayers)
		{
			MString name("colorSet1");

			MFnDagNode attribNode(job.meshData->mesh->parent(0));
			MObject activeLayerObj = attribNode.attribute(MString("VCP_Layer_Active"));
			if (!activeLayerObj.isNull())
			{
				MPlug activeLayerPlug(attribNode.object(), activeLayerObj);
				activeLayerPlug.setValue(name);
			}

			MString command = "VCP_Layer_CreateDefaultLayerAttribs " + attribNode.fullPathName() + " colorSet1";
			MGlobal::executeCommand(command);
			MGlobal::executeCommand("VCP_Layers_UpdateUI();");
/*
			
			attribNode.addAttribute(attr);
			MObject layerBlendAmountObj = attribNode.attribute(MString("VCP_Layer_") + name + "_BlendAmount");
			if (!layerBlendAmountObj.isNull())
			{
				MPlug layerBlendAmountPlug(attribNode.object(), layerBlendAmountObj);
				layerBlendAmountPlug.setValue(100.0f);
			}

			MObject layerBlendModeObj = attribNode.attribute(MString("VCP_Layer_") + name + "_BlendMode");
			if (!layerBlendModeObj.isNull())
			{
				MPlug layerBlendModePlug(attribNode.object(), layerBlendModeObj);
				layerBlendModePlug.setValue("Replace");
			}

			MObject layerVisibleObj = attribNode.attribute(MString("VCP_Layer_") + name + "_Visible");
			if (!layerVisibleObj.isNull())
			{
				MPlug layerVisiblePlug(attribNode.object(), layerVisibleObj);
				layerVisiblePlug.setValue(true);
			}*/

			job.meshData->DeleteLayersData();
			GatherLayersData();
		}
	}
#endif

	if (modifier != NULL)
	{
		m_undos.push_back(modifier);
	}
}
MStatus ColorSplineParameterHandler<S>::doSetValue( IECore::ConstParameterPtr parameter, MPlug &plug ) const
{
	assert( parameter );
	typename IECore::TypedParameter< S >::ConstPtr p = IECore::runTimeCast<const IECore::TypedParameter< S > >( parameter );
	if( !p )
	{
		return MS::kFailure;
	}

	MRampAttribute fnRAttr( plug );
	if ( !fnRAttr.isColorRamp() )
	{
		return MS::kFailure;
	}

	const S &spline = p->getTypedValue();

	MStatus s;
	MIntArray indicesToReuse;
	plug.getExistingArrayAttributeIndices( indicesToReuse, &s );
	assert( s );

	int nextNewLogicalIndex = 0;
	if( indicesToReuse.length() )
	{
		nextNewLogicalIndex = 1 + *std::max_element( MArrayIter<MIntArray>::begin( indicesToReuse ), MArrayIter<MIntArray>::end( indicesToReuse ) );
	}
	
	assert( indicesToReuse.length() == fnRAttr.getNumEntries() );

	size_t pointsSizeMinus2 = spline.points.size() - 2;
	unsigned pointIndex = 0;
	unsigned numExpectedPoints = 0;
	for ( typename S::PointContainer::const_iterator it = spline.points.begin(); it != spline.points.end(); ++it, ++pointIndex )
	{
		// we commonly double up the endpoints on cortex splines to force interpolation to the end.
		// maya does this implicitly, so we skip duplicated endpoints when passing the splines into maya.
		// this avoids users having to be responsible for managing the duplicates, and gives them some consistency
		// with the splines they edit elsewhere in maya.
		if( ( pointIndex==1 && *it == *spline.points.begin() ) || ( pointIndex==pointsSizeMinus2 && *it == *spline.points.rbegin() ) )
		{
			continue;
		}

		MPlug pointPlug;
		if( indicesToReuse.length() )
		{
			pointPlug = plug.elementByLogicalIndex( indicesToReuse[0] );
			indicesToReuse.remove( 0 );
		}
		else
		{
			pointPlug = plug.elementByLogicalIndex( nextNewLogicalIndex++ );		
		}
		
		s = pointPlug.child( 0 ).setValue( it->first ); assert( s );
		MPlug colorPlug = pointPlug.child( 1 );
		colorPlug.child( 0 ).setValue( it->second[0] );
		colorPlug.child( 1 ).setValue( it->second[1] );
		colorPlug.child( 2 ).setValue( it->second[2] );
		// hardcoding interpolation of 3 (spline) because the MRampAttribute::MInterpolation enum values don't actually
		// correspond to the necessary plug values at all.
		s = pointPlug.child( 2 ).setValue( 3 ); assert( s );

		numExpectedPoints++;
	}

	// delete any of the original indices which we didn't reuse. we can't use MRampAttribute::deleteEntries
	// here as it's utterly unreliable.
	if( indicesToReuse.length() )
	{
		MString plugName = plug.name();
		MObject node = plug.node();
		MFnDagNode fnDAGN( node );
		if( fnDAGN.hasObj( node ) )
		{
			plugName = fnDAGN.fullPathName() + "." + plug.partialName();
		}
		for( unsigned i=0; i<indicesToReuse.length(); i++ )
		{
			// using mel because there's no equivalant api method as far as i know.
			MString command = "removeMultiInstance -b true \"" + plugName + "[" + indicesToReuse[i] + "]\"";
			s = MGlobal::executeCommand( command );
			assert( s );
			if( !s )
			{
				return s;
			}
		}
	}

#ifndef NDEBUG
	{
		assert( fnRAttr.getNumEntries() == numExpectedPoints );

		MIntArray indices;
		MFloatArray positions;
		MColorArray colors;
		MIntArray interps;
		fnRAttr.getEntries( indices, positions, colors, interps, &s );
		assert( s );
		assert( numExpectedPoints == positions.length() );
		assert( numExpectedPoints == colors.length() );
		assert( numExpectedPoints == interps.length() );
		assert( numExpectedPoints == indices.length() );

		for ( unsigned i = 0; i < positions.length(); i++ )
		{
			float position = positions[ i ];
			const MVector color( colors[ i ][ 0 ], colors[ i ][ 1 ], colors[ i ][ 2 ] );

			bool found = false;

			for ( typename S::PointContainer::const_iterator it = spline.points.begin(); it != spline.points.end() && !found; ++it )
			{
				MVector color2( it->second[0], it->second[1], it->second[2] );
				if ( fabs( it->first - position ) < 1.e-3f && ( color2 - color ).length() < 1.e-3f )
				{
					found = true;
				}
			}
			assert( found );
		}
	}
#endif

	return MS::kSuccess;
}
IECoreScene::PrimitiveVariable FromMayaMeshConverter::colors( const MString &colorSet, bool forceRgb ) const
{
	MFnMesh fnMesh( object() );
	MFnMesh::MColorRepresentation rep = fnMesh.getColorRepresentation( colorSet );

	int numColors = fnMesh.numFaceVertices();
	MColorArray colors;
	MColor defaultColor(0,0,0,1);
	if ( !fnMesh.getFaceVertexColors( colors, &colorSet, &defaultColor ) )
	{
		throw Exception( ( boost::format( "Failed to obtain colors from color set '%s'" ) % colorSet ).str() );
	}

	int availableColors = colors.length();
	if ( availableColors > numColors )
	{
		availableColors = numColors;
	}

	DataPtr data;

	if ( rep == MFnMesh::kAlpha )
	{
		if ( forceRgb )
		{
			Color3fVectorDataPtr colorVec = new Color3fVectorData();
			colorVec->writable().resize( numColors, Imath::Color3f(1) );
			std::vector< Imath::Color3f >::iterator it = colorVec->writable().begin();
			for ( int i = 0; i < availableColors; i++, it++ )
			{
				*it = Imath::Color3f( colors[i][3] );
			}
			data = colorVec;
		}
		else
		{
			FloatVectorDataPtr colorVec = new FloatVectorData();
			colorVec->writable().resize( numColors, 1 );
			std::vector< float >::iterator it = colorVec->writable().begin();
			for ( int i = 0; i < availableColors; i++, it++ )
			{
				*it = colors[i][3];
			}
			data = colorVec;
		}
	}
	else
	{
		if ( rep == MFnMesh::kRGB || forceRgb )
		{
			Color3fVectorDataPtr colorVec = new Color3fVectorData();
			colorVec->writable().resize( numColors, Imath::Color3f(0,0,0) );
			std::vector< Imath::Color3f >::iterator it = colorVec->writable().begin();
			for ( int i = 0; i < availableColors; i++, it++ )
			{
				const MColor &c = colors[i];
				*it = Imath::Color3f( c[0], c[1], c[2] );
			}
			data = colorVec;
		}
		else
		{
			Color4fVectorDataPtr colorVec = new Color4fVectorData();
			colorVec->writable().resize( numColors, Imath::Color4f(0,0,0,1) );
			std::vector< Imath::Color4f >::iterator it = colorVec->writable().begin();
			for ( int i = 0; i < availableColors; i++, it++ )
			{
				const MColor &c = colors[i];
				*it = Imath::Color4f( c[0], c[1], c[2], c[3] );
			}
			data = colorVec;
		}
	}
	return PrimitiveVariable( PrimitiveVariable::FaceVarying, data );
}
Exemple #9
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);
		}
	}

}