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; }
// 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 ); }
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); } } }