// Shave and a Haircut // (c) 2019 Epic Games // US Patent 6720962 //Qt headers must be included before any others !!! #include #include #include #include #if QT_VERSION < 0x050000 # include # include #else # include # include #endif #include #include #include #include #include #include #include #include #include #include #include #include "shaveGlobals.h" #include "shaveHairShape.h" #include "shaveSDK.h" #include "shaveStyleCmd.h" #include "shaveUtil.h" #include "shaveCursorCtx.h" #include #include #include #include #include #include static const char* flAttenuate = "-attenuate"; static const char* fsAttenuate = "-at"; static const char* flGrowSelection = "-growSelection"; static const char* fsGrowSelection = "-gs"; static const char* flHideSelection = "-hideSelection"; static const char* fsHideSelection = "-hs"; static const char* flInvertSelection = "-invertSelection"; static const char* fsInvertSelection = "-is"; static const char* flLock = "-lock"; static const char* fsLock = "-l"; static const char* flMergeSelection = "-mergeSelection"; static const char* fsMergeSelection = "-ms"; static const char* flPopSelected = "-popSelected"; static const char* fsPopSelected = "-ps"; static const char* flPopZeroSized = "-popZeroSized"; static const char* fsPopZeroSized = "-pzs"; static const char* flRecomb = "-recomb"; static const char* fsRecomb = "-r"; static const char* flReplaceRest = "-replaceRest"; static const char* fsReplaceRest = "-rr"; static const char* flRotateSelectionUp = "-rotateSelectionUp"; static const char* fsRotateSelectionUp = "-rsu"; static const char* flSplitSelection = "-splitSelection"; static const char* fsSplitSelection = "-ss"; static const char* flToggleCollision = "-tglCollision"; static const char* fsToggleCollision = "-tc"; static const char* flUndo = "-undo"; static const char* fsUndo = "-u"; #ifdef GLOBAL_FALLBACK static const char* flRedrawOnIdle = "-redrawOnIdle"; static const char* fsRedrawOnIdle = "-rdi"; static const char* flFullRedrawOnIdle = "-fullRedrawOnIdle"; static const char* fsFullRedrawOnIdle = "-fri"; #endif const MString shaveStyleCmd::commandName("shaveStyle"); shaveStyleCmd::shaveStyleCmd() {} shaveStyleCmd::~shaveStyleCmd() {} void* shaveStyleCmd::createCmd() { return new shaveStyleCmd(); } MSyntax shaveStyleCmd::createSyntax() { MSyntax syntax; syntax.enableEdit(false); syntax.enableQuery(false); syntax.addFlag(fsAttenuate, flAttenuate); syntax.addFlag(fsGrowSelection, flGrowSelection); syntax.addFlag(fsHideSelection, flHideSelection, MSyntax::kBoolean); syntax.addFlag(fsInvertSelection, flInvertSelection); syntax.addFlag(fsLock, flLock, MSyntax::kBoolean); syntax.addFlag(fsMergeSelection, flMergeSelection); syntax.addFlag(fsPopSelected, flPopSelected); syntax.addFlag(fsPopZeroSized, flPopZeroSized); syntax.addFlag(fsRecomb, flRecomb); syntax.addFlag(fsReplaceRest, flReplaceRest); syntax.addFlag(fsRotateSelectionUp, flRotateSelectionUp); syntax.addFlag(fsSplitSelection, flSplitSelection); syntax.addFlag(fsToggleCollision, flToggleCollision); syntax.addFlag(fsUndo, flUndo); #ifdef GLOBAL_FALLBACK syntax.addFlag(fsRedrawOnIdle, flRedrawOnIdle); syntax.addFlag(fsFullRedrawOnIdle, flFullRedrawOnIdle); #endif return syntax; } MStatus shaveStyleCmd::doIt(const MArgList& argList) { MStatus st; MArgDatabase args(syntax(), argList, &st); if (!st) return st; // // Is there a hair shape selected? // unsigned i; MFnDagNode nodeFn; unsigned numHairShapes = 0; MDagPath targetPath; MSelectionList selection; MDagPath tempPath; #ifdef GLOBAL_FALLBACK if (args.isFlagSet(fsRedrawOnIdle)) { redrawOnIdle(false); return MS::kSuccess; } else if (args.isFlagSet(fsFullRedrawOnIdle)) { redrawOnIdle(true); return MS::kSuccess; } #endif MGlobal::getActiveSelectionList(selection); for (i = 0; i < selection.length(); i++) { if (selection.getDagPath(i, tempPath) && tempPath.isValid()) { tempPath.extendToShape(); nodeFn.setObject(tempPath); if (nodeFn.typeId() == shaveHairShape::id) { if (numHairShapes++ == 0) targetPath = tempPath; else break; } } } if (numHairShapes == 0 #ifdef GLOBAL_FALLBACK && !args.isFlagSet(fsFullRedrawOnIdle) && !args.isFlagSet(fsRedrawOnIdle) #endif ) { //////// debug /////////////// if(args.isFlagSet(fsAttenuate)) displayError("Shave Attenuate."); if(args.isFlagSet(fsLock)) displayError("Shave Lock."); if(args.isFlagSet(fsMergeSelection)) displayError("Shave Merge Selection."); if(args.isFlagSet(fsPopSelected)) displayError("Shave Pop Selected."); if(args.isFlagSet(fsPopZeroSized)) displayError("Shave Pop Zerosized."); if(args.isFlagSet(fsRecomb)) displayError("Shave Recomb."); if(args.isFlagSet(fsReplaceRest)) displayError("Shave Replace Rest."); if(args.isFlagSet(fsSplitSelection))displayError("Shave Split Selection."); if(args.isFlagSet(fsToggleCollision))displayError("Shave Toggle Collision."); if(args.isFlagSet(fsUndo)) displayError("Shave Undo."); #ifdef GLOBAL_FALLBACK if(args.isFlagSet(fsRedrawOnIdle)) displayError("Redraw higr res on idle."); if(args.isFlagSet(fsFullRedrawOnIdle)) displayError("Force update and redraw higr res on idle."); #endif ////////////////////////////// displayError("No Shave node selected."); return MS::kFailure; } if (numHairShapes > 1) { displayWarning( "Multiple Shave nodes are selected:" " only the first will be affected." ); } // // Make sure that the shaveNode is loaded into the engine. // nodeFn.setObject(targetPath); shaveHairShape* hairShape = (shaveHairShape*)nodeFn.userNode(); hairShape->makeCurrent(); hairShape->applySelections(); // // Execute the command. // bool enable; bool updateSelections = false; if (args.isFlagSet(fsAttenuate) || args.isFlagSet(fsLock) || args.isFlagSet(fsMergeSelection) || args.isFlagSet(fsPopSelected) || args.isFlagSet(fsPopZeroSized) || args.isFlagSet(fsRecomb) || args.isFlagSet(fsReplaceRest) || args.isFlagSet(fsSplitSelection) || args.isFlagSet(fsToggleCollision) || args.isFlagSet(fsUndo)) { MGlobal::executeCommand("shave_prepareForEditing"); if (args.isFlagSet(fsAttenuate)) SHAVEattenuate(); else if (args.isFlagSet(fsLock)) { args.getFlagArgument(fsLock, 0, enable); if (enable) SHAVElock(); else SHAVEunlock(); } else if (args.isFlagSet(fsPopSelected)) SHAVEpop_selected(); else if (args.isFlagSet(fsPopZeroSized)) SHAVEpop_zero_sized(); else if (args.isFlagSet(fsRecomb)) SHAVErecomb(); else if (args.isFlagSet(fsReplaceRest)) SHAVEreplace_rest_interactive(); else if (args.isFlagSet(fsToggleCollision)) SHAVEtoggle_collision(); else if (args.isFlagSet(fsUndo)) SHAVEundo(); else if (args.isFlagSet(fsSplitSelection)) SHAVEsplit_selection(); else if (args.isFlagSet(fsMergeSelection)) SHAVEmerge_selection(); hairShape->applyEdits(true); } else if (args.isFlagSet(fsGrowSelection)) { SHAVEselect_grow(); updateSelections = true; } else if (args.isFlagSet(fsHideSelection)) { args.getFlagArgument(fsHideSelection, 0, enable); if (enable) hairShape->hideSelectedGuides(); else hairShape->unhideGuides(); } else if (args.isFlagSet(fsInvertSelection)) { invertSelection(); updateSelections = true; } else if (args.isFlagSet(fsRotateSelectionUp)) { SHAVEselect_rotate_up(); updateSelections = true; } //else if (args.isFlagSet(fsRedrawOnIdle)) //{ // redrawOnIdle(false); //} //else if (args.isFlagSet(fsFullRedrawOnIdle)) //{ // redrawOnIdle(true); //} // // Have the hair shape put it's component selections onto Maya's // selection list. // if (updateSelections) hairShape->updateMayaComponentSelections(); return MS::kSuccess; } void shaveStyleCmd::invertSelection() { // The behaviour of SHAVEselect_inverse isn't appropriate for tip // selection mode: it turns off the tip and turns on all the other // verts whereas if the tip is on we want it to turn off the entire // guide. So we have to handle that mode ourselves. MString selectType; MGlobal::executeCommand("optionVar -q shaveBrushSelectMode", selectType); if (selectType == "tip") { SOFTGUIDE g; int i; for (i = 0; SHAVEfetch_guide(i, &g) != -1; ++i) { g.select[0] = 1 - g.select[0]; g.select[SHAVE_VERTS_PER_GUIDE-1] = g.select[0]; SOFTput_guideSELECTONLY(i, &g); } } else { SHAVEselect_inverse(); } } #ifdef GLOBAL_FALLBACK //static LARGE_INTEGER now={ static_cast(0) }; static qint64 now; bool shaveStyleCmd::redrawOnIdlDone = false; void shaveStyleCmd::redrawOnIdle(bool fullUpdate) { if(!redrawOnIdlDone) { if(IsMouseDown()) { redrawOnIdlDone = true; return; } ////need some time intervall cut off to avoid flickering //LARGE_INTEGER last = GetLastMoveTime(); //QueryPerformanceCounter(&now); //LARGE_INTEGER freq; //QueryPerformanceFrequency(&freq); //long long from_last = now.QuadPart - last.QuadPart; //float t = (float)(from_last*1000.0/freq.QuadPart); ////printf("t %f\n",t);fflush(stdout); //if(t < 150.f /*&& !fullUpdate*/) now = GetQTimer().elapsed(); qint64 t = now-GetLastMoveTime(); // printf("t %i\n",(int)t);fflush(stdout); if(t < /*150*/300 /*|| QCoreApplication::hasPendingEvents()*/) { //redrawOnIdlDone = true; // printf("refire \n");fflush(stdout); if(fullUpdate) MGlobal::executeCommandOnIdle("shaveStyle -fullRedrawOnIdle"); else MGlobal::executeCommandOnIdle("shaveStyle -redrawOnIdle"); return; } ////last = now; SetLastMoveTime(now); MFnDagNode nodeFn; MDagPathArray paths; shaveUtil::getShaveNodes(paths); //it should not trigger ::compute for (unsigned int i = 0; i < paths.length(); i++) { nodeFn.setObject(paths[i].node()); if (nodeFn.typeId() == shaveHairShape::id) { shaveHairShape* shape = (shaveHairShape*)nodeFn.userNode(); shape->dirtyDisplay(); shape->dirties.BRUSH_JUST_MOVED = 0; shape->dirties.GLOBAL_MOUSE_DOWN = 0; /////// need to make sure that textures are connected /////// shape->dirties.DIRTY_TEXTURE = 1; shape->dirties.DIRTY_TEXTURE_JOE = 1; ///////////////////////////////////////////////////////////// //////////////// //shape->updateTexLookups(); if(fullUpdate) { //shape->makeCurrent(); float trigger; MPlug triggerPlug = nodeFn.findPlug("trigger"); triggerPlug.getValue(trigger); triggerPlug.setValue(trigger+1.0f); } MHWRender::MRenderer::setGeometryDrawDirty(paths[i].node()); } } M3dView::active3dView().refresh(true,true); //is it needed? redrawOnIdlDone = true; //for (unsigned int i = 0; i < paths.length(); i++) //{ // nodeFn.setObject(paths[i].node()); // if (nodeFn.typeId() == shaveHairShape::id) // { // shaveHairShape* shape = (shaveHairShape*)nodeFn.userNode(); // shape->dirtyDisplay(); // // } //} } } unsigned int shaveStyleCmd::getTotalHaircount() { return 0; } unsigned int shaveStyleCmd::getTotalDisplayMem() { return 0; } #endif