// Shave and a Haircut // (c) 2019 Epic Games // US Patent 6720962 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if MAYA_API_VERSION >= 201600 #include #include #endif #include #include #include "shaveGeometryOverride.h" #include "shaveGlobals.h" // Names of render items. // const MString shaveGeometryOverride::kHairShadedItemName = "shaveShaded"; const MString shaveGeometryOverride::kHairShadedSelectedItemName = "shaveShadedSelection"; const MString shaveGeometryOverride::kHairWireItemName = "shaveWire"; const MString shaveGeometryOverride::kHairWireSelectedItemName = "shaveWireSelection"; const MString shaveGeometryOverride::kInstanceShadedItemName = "shaveInstSolid"; const MString shaveGeometryOverride::kInstanceWireItemName = "shaveInstWire"; const MString shaveGeometryOverride::kInstanceWireSelectedItemName = "shaveInstWireSelection"; const MString shaveGeometryOverride::kGuidesItemName = "shaveGuides"; const MString shaveGeometryOverride::kKnotsItemName = "shaveKnots"; // pre-draw callback //MHWRender::MShaderInstance* gShader = NULL; #if 0 static void shavePreDrawCallback( MHWRender::MDrawContext& context, const MHWRender::MRenderItemList& renderItemList, MHWRender::MShaderInstance *shaderInstance ) { //printf("Pre-draw callback triggered for render item with name '%s'\n", renderItem->name().asChar()); } // post-draw callback static void shavePostDrawCallback( MHWRender::MDrawContext& context, const MHWRender::MRenderItemList& renderItemList, MHWRender::MShaderInstance *shaderInstance ) { //printf("Post-draw callback triggered for render item with name '%s'\n", renderItem->name().asChar()); } #endif #if MAYA_API_VERSION >= 201600 // Custom component converter for selecting guide vertices. // class guideVertComponentConverter : public MHWRender::MPxComponentConverter { public: guideVertComponentConverter() : MHWRender::MPxComponentConverter() {} virtual ~guideVertComponentConverter() {} virtual void initialize(const MHWRender::MRenderItem& renderItem) { mComponent = MObject::kNullObj; mPrimIdxToVertIdx = NULL; mSelectType = ""; shaveGeometryOverride::SelectionData* itemData = dynamic_cast(renderItem.customData()); if (itemData) { mSelectType = itemData->mShaveSelectType; mPrimIdxToVertIdx = &itemData->mPrimIdxToVertIdx; // If the selection type is anything other than 'vert' or 'tip' // then we are not selecting verts, we're using verts to select // guides. // if ((mSelectType == "vert") || (mSelectType == "tip")) { mComponent = mVertComponentFn.create(shaveHairShape::kShaveGuideVertComponent); } else { mComponent = mGuideComponentFn.create(shaveHairShape::kShaveGuideComponent); } } } virtual void addIntersection(MHWRender::MIntersection& intersection) { int primIdx = intersection.index(); if (mPrimIdxToVertIdx != NULL) { if ((primIdx >= 0) && (primIdx < mPrimIdxToVertIdx->size())) { int vertIdx = mPrimIdxToVertIdx->at(primIdx); int guideIdx = vertIdx / SHAVE_VERTS_PER_GUIDE; if ((mSelectType == "vert") || (mSelectType == "tip")) { vertIdx = vertIdx % SHAVE_VERTS_PER_GUIDE; mVertComponentFn.addElement(guideIdx, vertIdx); } else { mGuideComponentFn.addElement(guideIdx); } } } } virtual MObject component() { return mComponent; } virtual MSelectionMask selectionMask() const { MSelectionMask mask; if ((mSelectType == "vert") || (mSelectType == "tip")) { mask.addMask(MSelectionMask::kSelectCVs); // Allow the converter to be used for snapping as well. // mask.addMask(MSelectionMask::kSelectPointsForGravity); } else { mask.addMask(MSelectionMask::kSelectMeshEdges); } return mask; } static MPxComponentConverter* create() { return new guideVertComponentConverter(); } private: MObject mComponent; MFnSingleIndexedComponent mGuideComponentFn; MFnDoubleIndexedComponent mVertComponentFn; const std::vector* mPrimIdxToVertIdx; MString mSelectType; }; // Custom component converter for selecting guides. // class guideComponentConverter : public MHWRender::MPxComponentConverter { public: guideComponentConverter() : MHWRender::MPxComponentConverter() {} virtual ~guideComponentConverter() {} virtual void initialize(const MHWRender::MRenderItem& renderItem) { mComponent = MObject::kNullObj; mPrimIdxToGuideIdx = NULL; mSelectType = ""; shaveGeometryOverride::SelectionData* itemData = dynamic_cast(renderItem.customData()); if (itemData) { mSelectType = itemData->mShaveSelectType; // Only use guides for selection if we're in 'guide' mode. // if (mSelectType == "guide") { mComponent = mComponentFn.create(shaveHairShape::kShaveGuideComponent); mPrimIdxToGuideIdx = &itemData->mPrimIdxToGuideIdx; } } } virtual void addIntersection(MHWRender::MIntersection& intersection) { int primIdx = intersection.index(); if (mPrimIdxToGuideIdx != NULL) { if ((primIdx >= 0) && (primIdx < mPrimIdxToGuideIdx->size())) { int guideIdx = mPrimIdxToGuideIdx->at(primIdx); mComponentFn.addElement(guideIdx); } } } virtual MObject component() { return mComponent; } virtual MSelectionMask selectionMask() const { MSelectionMask mask; if (mSelectType == "guide") { mask.addMask(MSelectionMask::kSelectMeshEdges); } return mask; } static MPxComponentConverter* create() { return new guideComponentConverter(); } private: MObject mComponent; MFnSingleIndexedComponent mComponentFn; const std::vector* mPrimIdxToGuideIdx; MString mSelectType; }; #endif shaveGeometryOverride::shaveGeometryOverride(const MObject& obj) : MPxGeometryOverride(obj) , shave(NULL) , instex (NULL) { MStatus status; shobj = obj; MFnDependencyNode node(obj, &status); if (status) { shave = dynamic_cast(node.userNode()); } else { MGlobal::displayError("can not attach MFnDependencyNode function set to the shave node"); } MFnDagNode dag(obj,&status); if (status) { MObject tm = dag.parent(0,&status); if(!tm.isNull() && status == MStatus::kSuccess) { MFnDependencyNode tmfn(tm, &status); //if(!tmfn.isLocked()) { //does not have any effect //status = tmfn.setLocked(true); //if(status != MStatus::kSuccess) // MGlobal::displayError("can not lock transfrom"); MPlug plug = tmfn.findPlug("translate", &status); status = plug.setLocked(true); // lock the attribute plug = tmfn.findPlug("rotate", &status); status = plug.setLocked(true); // lock the attribute plug = tmfn.findPlug("scale", &status); status = plug.setLocked(true); // lock the attribute } } else MGlobal::displayError("can not get parent for shave node"); } else MGlobal::displayError("can not attach MFnDagNode function set to the shave node"); } shaveGeometryOverride::~shaveGeometryOverride() { } MHWRender::DrawAPI shaveGeometryOverride::supportedDrawAPIs() const { //return MHWRender::kAllDevices; //return (MHWRender::kOpenGL | MHWRender::kDirectX11); return MHWRender::kOpenGL; } #define POLY_HAIR static const bool debugShader = false; void shaveGeometryOverride::updateDG() { MStatus st; if (shave) { mNumHairsToDraw = shave->getNumDisplayHairs(false); // TODO: // // We assume that the "active" 3d view (i.e. the one the mouse is in) // is also the one currently being drawn. That is not necessarily // true, especially when there are multiple views. // // We can determine the camera being rendered from the MFrameContext // passed to addUIDrawables() (which we don't currently implement) but // that won't be called until *after* updateDG() when it will be too // late to pull data from the shaveHairShape's attrs. // // addUIDrawables() is called *before* populateGeometry(), so one // workaround would be to cache data for *all* available views here, // then select the appropriate set of data in populateGeometry(), based // on the camera determined in addUIDrawables(). However, it would make // sense to first check with ADesk to see if there is a way to get the // necessary cam info (viewport, matrix, etc) here in updateDG(). // M3dView view = M3dView::active3dView(); mLightMode = M3dView::kLightDefault; view.getLightingMode(mLightMode); MDagPath camPath; st = view.getCamera(camPath); if (st) { MFnCamera camFn(camPath); mViewDir = camFn.viewDirection(MSpace::kWorld); mViewDir.normalize(); mUpDir = camFn.upDirection(MSpace::kWorld); mUpDir.normalize(); mRightDir = camFn.rightDirection(MSpace::kWorld); mRightDir.normalize(); mWorldToCam = camPath.inclusiveMatrixInverse(); } else { mViewDir = MVector::zAxis; mUpDir = MVector::xAxis; mRightDir = MVector::yAxis; } #if 1 mHairCache = &shave->getDisplayHairs(view, true); while (mNumHairsToDraw != mHairCache->displayHair.size()) { #ifdef GLOBAL_FALLBACK if((IsMouseDown() || GetEventsHappen()) && !liveModeGlob && fallback) { shave->invalidateDisplayHairs(); break; } #endif mHairCache = &shave->getDisplayHairs(view, false); } #ifdef GLOBAL_FALLBACK ClearEvents(); #endif #else mHairCache = &shave->getDisplayHairs(); #endif if (debugShader) { printf("cache size: %zi\n", mHairCache->displayHair.size()); } if (mNumHairsToDraw > mHairCache->displayHair.size()) { mNumHairsToDraw = (unsigned int)mHairCache->displayHair.size(); } mHairDisplayMode = shave->getDisplayMode(); mSpecularTint = shave->getSpecularTint(); mSpecularTint2 = shave->getSpecularTint2(); mGloss = shave->getGloss(); mSpecular = shave->getSpecular(); mTransparency = shave->getHairXparency(); mDoTipFade = shave->getDoTipfade(); mPasses = shave->getNumPasses(); mInstanceCache = &shave->getInstanceDisplay(); mGuideCache = &shave->getGuides().guides; mGuideExtent = shave->getGuideExt(); // If the guide extent is zero then it hasn't yet been calculated, so do it now. // if (mGuideExtent == 0.0f) //compute once { MBoundingBox bbox; bbox.clear(); shaveHairShape::Guides::const_iterator iter; for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) { const shaveHairShape::Guide& guide = *iter; if (!guide.hidden) { MPoint v = guide.verts[SHAVE_VERTS_PER_GUIDE-1];//tip only bbox.expand(v); } } mGuideExtent = (float)(bbox.max() - bbox.min()).length(); shave->setGuideExt(mGuideExtent); } if (!glisntGlob) { shave->updateTexLookups(); } } } #if 0 /////////////////////////////////////////////////////////////////////////////////// #ifdef _WIN32 #else # include # define MAX_PATH 1024 #endif #ifndef _CGFX_PLUGIN_MAX_COMPILER_ARGS_ #define _CGFX_PLUGIN_MAX_COMPILER_ARGS_ 20 #endif MString cgfxFindFile(const MString& name, const MString &searchpath) { MString file = name; struct stat statBuf; char path[MAX_PATH]; const char * psearchpath = searchpath.asChar(); OutputDebugString("File = "); OutputDebugString(file.asChar()); OutputDebugString("\n"); // First we check if it is a fully qualified path... if (stat(file.asChar(), &statBuf) == -1) { bool found = false; while (found == false && psearchpath < searchpath.asChar() + searchpath.length()) { const char * endpath = strchr(psearchpath,';'); if (endpath) { strncpy(path,psearchpath, endpath - psearchpath); path[endpath - psearchpath] = '\0'; } else { strcpy(path,psearchpath); } psearchpath += strlen(path)+1; bool fullPath = (path[0] == '/' || path[0] == '\\'); if (strlen(path) > 2) { fullPath = fullPath || (path[1] == ':' && (path[2] == '/' || path[2] == '\\')); } // Add the path and the filename together to get the full path file = MString(path) + "/" + name; OutputDebugString("Try File = "); OutputDebugString(file.asChar()); OutputDebugString("\n"); if (stat(file.asChar(), &statBuf) != -1) found = true; else file = ""; } } OutputDebugString("Returning: "); OutputDebugString(file.asChar()); OutputDebugString("\n"); return file; } MString cgfxFindFile(const MString& name, bool projectRelative) { // Our result MString fileName; // Do we have an image to look for? if (name.asChar() != NULL && strcmp(name.asChar(), "")) { // Build a list of places we'll look for textures // Start with the current working directory static MString texturePath( "."); // Add the standard Maya project paths MString workspace; MStatus status = MGlobal::executeCommand(MString("workspace -q -rd;"), workspace); if ( status == MS::kSuccess) { texturePath += ";"; texturePath += workspace; texturePath += ";"; texturePath += workspace; texturePath += "/textures;"; texturePath += workspace; texturePath += "/images;"; texturePath += workspace; } // Finally, see if any CgFX environment variable paths are set char * cgfxPath = getenv("CGFX_TEXTURE_PATH"); if (cgfxPath) { texturePath += ";"; texturePath += cgfxPath; } else { char * cgfxRoot = getenv("CGFX_ROOT"); if (cgfxRoot) { texturePath += ";"; texturePath += cgfxRoot; texturePath += "/textures/2D;"; texturePath += cgfxRoot; texturePath += "/textures/cubemaps;"; texturePath += cgfxRoot; texturePath += "/textures/3D;"; texturePath += cgfxRoot; texturePath += "/textures/rectangles;"; texturePath += cgfxRoot; texturePath += "/CgFX_Textures;"; texturePath += cgfxRoot; texturePath += "/CgFX"; } } OutputDebugString("CgFX texture path is: "); OutputDebugString(texturePath.asChar()); OutputDebugString("\n"); fileName = cgfxFindFile(name, texturePath); int hasFile = fileName.asChar() != NULL && strcmp(fileName.asChar(), ""); if (hasFile == 0) { // lets extract the filename and try it again... int idx = name.rindex('/'); if (idx == -1) idx = name.rindex('\\'); if (idx != -1) { MString filename = name.substring(idx+1,name.length()-1); fileName = cgfxFindFile(filename, texturePath); hasFile = fileName.asChar() != NULL && strcmp(fileName.asChar(), ""); } } // If we found the file and the user wants project relative, try // to strip the project directory off the front of our result if( hasFile && projectRelative) { if( fileName.length() > workspace.length() && workspace.length() > 0 && fileName.substring( 0, workspace.length() - 1) == workspace) // Strip the project path off the front INCLUDING the // separating '/' (otherwise we'd create an absolute path) fileName = fileName.substring( workspace.length() + 1, fileName.length() - 1); } if (hasFile == 0) OutputDebugString("Error: file not found.\n"); } return fileName; } void cgfxGetFxIncludePath( const MString &fxFile, MStringArray &pathOptions ) { // Append the path of the cgfx file as a possible include search path // MString option; if (fxFile.length()) { MFileObject fobject; fobject.setRawFullName( fxFile ); option = MString("-I") + fobject.resolvedPath(); pathOptions.append( option ); } // Add in "standard" cgfx search for cgfx files as a possible include // search path // char * cgfxRoot = getenv("CGFX_ROOT"); if (cgfxRoot) { option = MString("-I") + MString(cgfxRoot); pathOptions.append( option ); option = MString("-I") + MString(cgfxRoot) + MString("/CgFX"); pathOptions.append( option ); } // Add in Maya's Cg directory char * mayaLocation = getenv("MAYA_LOCATION"); if (mayaLocation) { MString mayaCgLocation(MString(mayaLocation) + MString("/bin/Cg/")); option = MString("-I") + mayaCgLocation; pathOptions.append( option ); } } /////////////////////////////////////////////////////////////////////////////////// #endif static bool vp20dlgFired = false; void shaveGeometryOverride::updateRenderItems(const MDagPath& path, MHWRender::MRenderItemList& list) { if (mLightMode == M3dView::kLightActive || mLightMode == M3dView::kLightAll) { if(!vp20dlgFired) { vp20dlgFired = true; MGlobal::executeCommand("shave_showVP2lightWarning"); } } else vp20dlgFired = false; if(debugShader) printf("updateRenderItems\n"); if(!shave) return; MHWRender::MRenderer* renderer = MHWRender::MRenderer::theRenderer(); if (!renderer) return; const MHWRender::MShaderManager* shaderMgr = renderer->getShaderManager(); if (!shaderMgr) return; MHWRender::MTextureManager* textureMgr = renderer->getTextureManager(); if(!textureMgr) return; if(debugShader) { MStringArray paths; shaderMgr->shaderPaths(paths); printf("Cgfx shader paths:\n"); for(unsigned int i = 0; i < paths.length(); i++) printf("%s\n",paths[i].asChar()); } // This is the type of primitive we use for drawing anything // which isn't an instance. // #ifdef POLY_HAIR MHWRender::MGeometry::Primitive nonInstPrim = MHWRender::MGeometry::kTriangles; #else MHWRender::MGeometry::Primitive nonInstPrim = MHWRender::MGeometry::kLines; #endif if (mHairDisplayMode == shaveHairShape::kHairDisplayNone) { // Disable all render items. // disableRenderItem(list, kInstanceWireItemName, MHWRender::MGeometry::kLines, MHWRender::MGeometry::kWireframe); disableRenderItem(list, kInstanceWireSelectedItemName, MHWRender::MGeometry::kLines, MHWRender::MGeometry::kWireframe); disableRenderItem(list, kHairWireItemName, nonInstPrim, MHWRender::MGeometry::kWireframe); disableRenderItem(list, kHairWireSelectedItemName, nonInstPrim, MHWRender::MGeometry::kWireframe/*::kAll*/); disableRenderItem( list, kInstanceShadedItemName, MHWRender::MGeometry::kTriangles, (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured) ); disableRenderItem( list, kHairShadedItemName, nonInstPrim, (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured) ); disableRenderItem( list, kHairShadedSelectedItemName, nonInstPrim, (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured) ); } else if (mHairDisplayMode == shaveHairShape::kHairDisplayHair) { // disable instances disableRenderItem( list, kInstanceWireItemName, MHWRender::MGeometry::kLines, MHWRender::MGeometry::kWireframe ); disableRenderItem( list, kInstanceWireSelectedItemName, MHWRender::MGeometry::kLines, MHWRender::MGeometry::kWireframe ); disableRenderItem( list, kInstanceShadedItemName, MHWRender::MGeometry::kTriangles, (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured) ); ///////////// wirewrame not selected ///////// // int index ; // add wire frame item if it's not there MHWRender::MRenderItem* wireItem = NULL; index = list.indexOf(kHairWireItemName, nonInstPrim, MHWRender::MGeometry::kWireframe); if (index < 0) { wireItem = MHWRender::MRenderItem::Create (kHairWireItemName, nonInstPrim, MHWRender::MGeometry::kWireframe, false); //wireItem->castsShadows(true); MHWRender::MShaderInstance* shader = shaderMgr->getEffectsFileShader("shaveHair.cgfx", "StrandLines"/*, 0,0,true, debugShader ? shavePreDrawCallback : NULL, debugShader ? shavePostDrawCallback : NULL*/); if (shader) { list.append(wireItem); static const float theColor[] = {0.0f, 0.01f, 0.27f, 1.0f}; shader->setParameter("gUseColor", true); shader->setParameter("gColor", theColor); // assign shader wireItem->setShader(shader); // sample debug code if (debugShader) { MStringArray params; shader->parameterList(params); unsigned int numParams = params.length(); printf("DEBUGGING SHADER, BEGIN PARAM LIST OF LENGTH %d\n", numParams); for (unsigned int i=0; iisArrayParameter(params[i]) ? "YES" : "NO"); } printf("END PARAM LIST\n"); fflush(stdout); } } else //let's try to get debug info { #if 0 ////////////////////////////////////////////////////// MString fileName = cgfxFindFile("shaveHair.cgfx",false); if(fileName.asChar() != NULL && strcmp(fileName.asChar(), "")) { // Compile and create the effect. MStringArray fileOptions; cgfxGetFxIncludePath( fileName, fileOptions ); const char *opts[_CGFX_PLUGIN_MAX_COMPILER_ARGS_]; unsigned int numOpts = fileOptions.length(); if (numOpts) { numOpts = (numOpts > _CGFX_PLUGIN_MAX_COMPILER_ARGS_) ? _CGFX_PLUGIN_MAX_COMPILER_ARGS_ : numOpts; for (unsigned int i=0; icastsShadows(true); MHWRender::MShaderInstance* shader = shaderMgr->getEffectsFileShader("shaveHair.cgfx", "StrandLines" /*, 0,0,true, debugShader ? shavePreDrawCallback : NULL, debugShader ? shavePostDrawCallback : NULL*/ ); if (shader) { list.append(selectItem); static const float selColor[] = {0.0f, 0.6f, 0.32f, 1.0f}; shader->setParameter("gUseColor", true); shader->setParameter("gColor", selColor); //shader->setIsTransparent(true);//crap, why it does not change anything? selectItem->setShader(shader); /////////////////// //printf("transparent %s\n", shader->isTransparent()?"yes":"no");fflush(stdout); /////////////////// } } else { selectItem = list.itemAt(index); } // Update selection item, disable by default bool brushDown = shave->getBrushDown(); selectItem->enable(false && !brushDown /*&& !hideHairGlob*/); wireItem->enable(true && !brushDown /*&& !hideHairGlob*/); { MStatus status; MSelectionList selectedList; status = MGlobal::getActiveSelectionList(selectedList); if (status) { MDagPath pathCopy = path; do { if (selectedList.hasItem(pathCopy)) { selectItem->enable(true && !brushDown/* && !hideHairGlob*/); wireItem->enable(false && !brushDown /*&& !hideHairGlob*/); break; } status = pathCopy.pop(); } while(status); } } ///////////// solid not selected ///////// // MHWRender::MShaderInstance* shader = NULL; MHWRender::MRenderItem* shadedItem = NULL; index = list.indexOf( kHairShadedItemName, nonInstPrim, (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured)); if (index < 0) { shadedItem = MHWRender::MRenderItem::Create (kHairShadedItemName, nonInstPrim, (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured), false); /*MHWRender::MShaderInstance* */ shader = shaderMgr->getEffectsFileShader("shaveHair.cgfx", "StrandLines" /*, 0,0,true, debugShader ? shavePreDrawCallback : NULL, debugShader ? shavePostDrawCallback : NULL*/); //shadedItem->castsShadows(true); if (shader) { list.append(shadedItem); // assign shader shader->setParameter("gUseColor", false); shader->setParameter("gDimmed", true); shadedItem->setShader(shader); } else MGlobal::displayError("Can't load 'shaveHair.cgfx' shader"); } else { shadedItem = list.itemAt(index); shader = shadedItem->getShader(); } if(shader) { shader->setParameter("gSpecular", mGloss); shader->setParameter("gSpecularAmt", mSpecular); float tint[3]; mSpecularTint.get(tint); shader->setParameter("gSpecularTint", tint); mSpecularTint2.get(tint); shader->setParameter("gSpecularTint2", tint); } ///////////// solid selected ///////// // MHWRender::MShaderInstance* shader2 = NULL; MHWRender::MRenderItem* shadedItemSel = NULL; index = list.indexOf( kHairShadedSelectedItemName, nonInstPrim, (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured)); if (index < 0) { // shadedItemSel = MHWRender::MRenderItem::Create // (kHairShadedSelectedItemName, // MHWRender::MGeometry::kLines, // MHWRender::MGeometry::kShaded, /*false*/ true); //#if MAYA_API_VERSION >= 201500 // shadedItemSel = MHWRender::MRenderItem::Create // (kHairShadedSelectedItemName, // MHWRender::MRenderItem::MaterialSceneItem, // MHWRender::MGeometry::kLines); // shadedItemSel->setDrawMode(MHWRender::MGeometry::kShaded); //#else // shadedItemSel = MHWRender::MRenderItem::Create // (kHairShadedSelectedItemName, // MHWRender::MGeometry::kLines, // MHWRender::MGeometry::kShaded, /*false*/ true); //#endif shadedItemSel = MHWRender::MRenderItem::Create (kHairShadedSelectedItemName, nonInstPrim, (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured), false); //shadedItemSel->castsShadows(true); /*MHWRender::MShaderInstance* */ shader2 = shaderMgr->getEffectsFileShader("shaveHair.cgfx", "StrandLines"/*, 0,0,true, debugShader ? shavePreDrawCallback : NULL, debugShader ? shavePostDrawCallback : NULL*/); if (shader2) { list.append(shadedItemSel); // assign shader shader2->setParameter("gUseColor", false); shader2->setParameter("gDimmed", false); shader2->setParameter("gSpecular", mGloss); shader2->setParameter("gSpecularAmt", mSpecular); float tint[3]; mSpecularTint.get(tint); shader2->setParameter("gSpecularTint", tint); mSpecularTint2.get(tint); shader2->setParameter("gSpecularTint2", tint); shadedItemSel->setShader(shader2); // once assigned, no need to hold on to shader instance //shaderMgr->releaseShader(shader); } else MGlobal::displayError("Can't load 'shaveHair.cgfx' shader"); /* //let's try stock shader - its ok MHWRender::MShaderInstance* shader = shaderMgr->getStockShader( MHWRender::MShaderManager::k3dSolidShader, NULL, NULL); if (shader) { // assign shader list.append(shadedItemSel); shadedItemSel->setShader(shader); // once assigned, no need to hold on to shader instance shaderMgr->releaseShader(shader); } */ } else { shadedItemSel = list.itemAt(index); shader2 = shadedItemSel->getShader(); } if(shader2) { shader2->setParameter("gSpecular", mGloss); shader2->setParameter("gSpecularAmt", mSpecular); float tint[3]; mSpecularTint.get(tint); shader2->setParameter("gSpecularTint", tint); mSpecularTint2.get(tint); shader2->setParameter("gSpecularTint2", tint); } // Update selection item, disable by default shadedItemSel->enable(false && !brushDown /*&& !hideHairGlob*/); shadedItem->enable(true && !brushDown /*&& !hideHairGlob*/); { MStatus status; MSelectionList selectedList; status = MGlobal::getActiveSelectionList(selectedList); if (status) { MDagPath pathCopy = path; do { if (selectedList.hasItem(pathCopy)) { shadedItemSel->enable(true && !brushDown /*&& !hideHairGlob*/); shadedItem->enable(false && !brushDown/* && !hideHairGlob*/); break; } status = pathCopy.pop(); } while(status); } } if (mNumHairsToDraw == 0) { shadedItemSel->enable(false); shadedItem->enable(false); selectItem->enable(false); wireItem->enable(false); } else if(!doFallbackGlob) { shadedItemSel->enable(true); shadedItem->enable(false); } } else if(mHairDisplayMode == shaveHairShape::kHairDisplayGeom && glisntGlob /*false*/) { // disable regular hair items disableRenderItem(list, kHairWireItemName, nonInstPrim, MHWRender::MGeometry::kWireframe); disableRenderItem(list, kHairWireSelectedItemName, nonInstPrim, MHWRender::MGeometry::kWireframe/*::kAll*/); disableRenderItem( list, kHairShadedItemName, nonInstPrim, (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured) ); disableRenderItem( list, kHairShadedSelectedItemName, nonInstPrim, (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured) ); ///////////// wirewrame not selected ///////// // MHWRender::MRenderItem* wireItem = NULL; { int index = list.indexOf( kInstanceWireItemName, MHWRender::MGeometry::kLines, MHWRender::MGeometry::kWireframe); if (index < 0) { wireItem = MHWRender::MRenderItem::Create (kInstanceWireItemName, MHWRender::MGeometry::kLines, MHWRender::MGeometry::kWireframe, false); MHWRender::MShaderInstance* shader = shaderMgr->getEffectsFileShader("shaveHair.cgfx","InstanceWire"/*, 0,0,true, debugShader ? shavePreDrawCallback : NULL, debugShader ? shavePostDrawCallback : NULL*/); //wireItem->castsShadows(true); if (shader) { list.append(wireItem); static const float theColor[] = {0.0f, 0.01f, 0.27f, 1.0f}; shader->setParameter("gColor", theColor); // assign shader wireItem->setShader(shader); // sample debug code if (debugShader) { MStringArray params; shader->parameterList(params); unsigned int numParams = params.length(); printf("DEBUGGING SHADER, BEGIN PARAM LIST OF LENGTH %d\n", numParams); for (unsigned int i=0; iisArrayParameter(params[i]) ? "YES" : "NO"); } printf("END PARAM LIST\n"); fflush(stdout); } } } else { wireItem = list.itemAt(index); } } ///////////// wirewrame selected ///////// // MHWRender::MRenderItem* wireItemSel = NULL; { int index = list.indexOf( kInstanceWireSelectedItemName, MHWRender::MGeometry::kLines, MHWRender::MGeometry::kWireframe); if (index < 0) { wireItemSel = MHWRender::MRenderItem::Create (kInstanceWireSelectedItemName, MHWRender::MGeometry::kLines, MHWRender::MGeometry::kWireframe, false); MHWRender::MShaderInstance* shader = shaderMgr->getEffectsFileShader("shaveHair.cgfx","InstanceWire"/*, 0,0,true, debugShader ? shavePreDrawCallback : NULL, debugShader ? shavePostDrawCallback : NULL*/); //wireItemSel->castsShadows(true); if (shader) { list.append(wireItemSel); static const float selColor[] = {0.0f, 0.6f, 0.32f, 1.0f}; shader->setParameter("gColor", selColor); // assign shader wireItemSel->setShader(shader); } } else { wireItemSel = list.itemAt(index); } } // Update selection item, disable by default bool brushDown = shave->getBrushDown(); wireItemSel->enable(false && !brushDown /*&& !hideHairGlob*/); wireItem->enable(true && !brushDown /*&& !hideHairGlob*/); { MStatus status; MSelectionList selectedList; status = MGlobal::getActiveSelectionList(selectedList); if (status) { MDagPath pathCopy = path; do { if (selectedList.hasItem(pathCopy)) { wireItemSel->enable(true && !brushDown /*&& !hideHairGlob*/); wireItem->enable(false && !brushDown /*&& !hideHairGlob*/); break; } status = pathCopy.pop(); } while(status); } } MHWRender::MRenderItem* solidItem = NULL; MHWRender::MShaderInstance* shader = NULL; { int index = list.indexOf( kInstanceShadedItemName, MHWRender::MGeometry::kTriangles, (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured)); if (index < 0) { solidItem = MHWRender::MRenderItem::Create (kInstanceShadedItemName, MHWRender::MGeometry::kTriangles, (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured), false); shader = shaderMgr->getEffectsFileShader("shaveHair.cgfx","InstanceSolid" /*, 0,0,true, debugShader ? shavePreDrawCallback : NULL, debugShader ? shavePostDrawCallback : NULL*/); //solidItem->castsShadows(true); if (shader) { list.append(solidItem); solidItem->setShader(shader); } } else { solidItem = list.itemAt(index); shader = solidItem->getShader(); } if(shader) { static const float theColor[] = {0.0f, 0.01f, 0.27f, 1.0f}; shader->setParameter("gColor", theColor); if (mInstanceCache->diffuse != 0) { shader->setParameter("gUseTexture", true); if(!instex) { MHWRender::MTextureDescription desc; { desc.setToDefault2DTexture(); desc.fWidth = mInstanceCache->eTexW; desc.fHeight = mInstanceCache->eTexH; desc.fDepth = 1; desc.fBytesPerRow = 4*mInstanceCache->eTexW; desc.fBytesPerSlice = 4*mInstanceCache->eTexW*mInstanceCache->eTexH; desc.fMipmaps = 0; desc.fArraySlices = 0; desc.fFormat = MHWRender::kR8G8B8A8_UNORM; desc.fTextureType = MHWRender::kImage2D; } MString texName("Diffuse"); instex = textureMgr->acquireTexture(texName, desc, mInstanceCache->img.pixels()); } MHWRender::MTextureAssignment tas; tas.texture = instex; shader->setParameter("gTexture",tas); } else { shader->setParameter("gUseTexture", false); } } } solidItem->enable(true); if (mNumHairsToDraw == 0) { //solidItemSel->enable(false); solidItem->enable(false); wireItemSel->enable(false); wireItem->enable(false); } if (!shave->getInstancingStatus() /*|| hideHairGlob*/) { solidItem->enable(false); wireItemSel->enable(false); wireItem->enable(false); } } else if (!glisntGlob) { // disable instances disableRenderItem(list, kInstanceWireItemName, MHWRender::MGeometry::kLines, MHWRender::MGeometry::kWireframe); disableRenderItem(list, kInstanceWireSelectedItemName, MHWRender::MGeometry::kLines, MHWRender::MGeometry::kWireframe); disableRenderItem( list, kInstanceShadedItemName, MHWRender::MGeometry::kTriangles, (MHWRender::MGeometry::DrawMode) (MHWRender::MGeometry::kShaded|MHWRender::MGeometry::kTextured) ); } //end of hair /////////////////////////////// MHWRender::DisplayStatus displayStatus = MHWRender::MGeometryUtilities::displayStatus(path); bool componentSelectionMode = (displayStatus == MHWRender::kHilite ); MString selectType; MGlobal::executeCommand( "optionVar -q shaveBrushSelectMode", selectType ); mSelectionData.mShaveSelectType = selectType; ///// guides display MHWRender::MRenderItem* guidesItem = NULL; { int index = list.indexOf(kGuidesItemName, nonInstPrim, MHWRender::MGeometry::kAll); if (index < 0) { guidesItem = MHWRender::MRenderItem::Create (kGuidesItemName, nonInstPrim, MHWRender::MGeometry::kAll, false); guidesItem->setCustomData(&mSelectionData); MHWRender::MShaderInstance* shader = shaderMgr->getEffectsFileShader("shaveHair.cgfx","Guides"); if (shader) { list.append(guidesItem); //static const float selColor[] = {0.0f, 0.6f, 0.32f, 1.0f}; //shader->setParameter("gColor", selColor); // assign shader guidesItem->setShader(shader); } } else { guidesItem = list.itemAt(index); } bool displayGuides = shave->getDisplayGuides(); bool brushActive = shave->getBrushActive(); if (displayGuides || brushActive || (componentSelectionMode && (selectType == "root" || selectType == "tip" || selectType == "vert" || selectType == "guide" )) ) guidesItem->enable(true); else guidesItem->enable(false); { MHWRender::MShaderInstance* shader = NULL; shader = guidesItem->getShader(); //assert(shader); if(shader) { float w = shave->getGuideThinkness(); if(w < 0.01f) w=0.01f; shader->setParameter("gGuideWidth",w); } } } //vertices display MHWRender::MRenderItem* knotsItem = NULL; { int index = list.indexOf(kKnotsItemName, nonInstPrim, MHWRender::MGeometry::kAll); if (index < 0) { knotsItem = MHWRender::MRenderItem::Create (kKnotsItemName, nonInstPrim, MHWRender::MGeometry::kAll, false); knotsItem->setCustomData(&mSelectionData); MHWRender::MShaderInstance* shader = shaderMgr->getEffectsFileShader("shaveHair.cgfx","Knots"); //assert(shader); if (shader) { list.append(knotsItem); //static const float selColor[] = {0.0f, 0.6f, 0.32f, 1.0f}; //shader->setParameter("gColor", selColor); knotsItem->depthPriority( MHWRender::MRenderItem::sActiveWireDepthPriority /*sDormantWireDepthPriority*/ ); // assign shader knotsItem->setShader(shader); } } else { knotsItem = list.itemAt(index); } bool displayGuides = shave->getDisplayGuides(); bool brushActive = shave->getBrushActive(); //can not access it here its a part of getDrawRequests //bool componentSelectionMode = (!noActiveComponents && (dStatus == M3dView::kHilite)); if (/*displayGuides || brushActive*/ componentSelectionMode && (selectType == "root" || selectType == "tip" || selectType == "vert" )) { knotsItem->enable(true); } else { knotsItem->enable(false); } { MHWRender::MShaderInstance* shader = NULL; shader = knotsItem->getShader(); //assert(shader); if(shader) { float w = shave->getGuideThinkness(); if(w < 0.01f) w=0.01f; shader->setParameter("gGuideWidth",w); } } } } void shaveGeometryOverride::populateGeometry( const MHWRender::MGeometryRequirements& requirements, const MHWRender::MRenderItemList& itemList, MHWRender::MGeometry& data ) { if (mHairDisplayMode == shaveHairShape::kHairDisplayHair) { #ifdef POLY_HAIR populateHairPolys(requirements, itemList, data); #else populateHairLines(requirements, itemList, data); #endif } else if (mHairDisplayMode == shaveHairShape::kHairDisplayGeom) { populateHairInstances(requirements, itemList, data); } #if 0 bool displayGuides = shave->getDisplayGuides(); if (/*displayGuides || mBrushIsActive*/ true) { populateHairGuides( requirements,itemList,data ); } #endif } void shaveGeometryOverride::populateHairInstances( const MHWRender::MGeometryRequirements& requirements, const MHWRender::MRenderItemList& itemList, MHWRender::MGeometry& data ) { cacheGuidePolys(); cacheKnotPolys(); unsigned int guideTriangles = (unsigned int)guidePolys.size(); unsigned int knotTriangles = (unsigned int)knotPolys.size(); unsigned int instTriangles = mInstanceCache->faceCounts.length(); unsigned int totalTriangles = guideTriangles+ instTriangles + knotTriangles; unsigned int totalLines = instTriangles*3; unsigned int instVerts = instTriangles*3; unsigned int guideVerts = guideTriangles*3; unsigned int knotVerts = knotTriangles*3; unsigned int totalVerts = totalTriangles*3; if(debugShader) { printf("triangles: %i verts: %i\n",totalTriangles, totalVerts); } MHWRender::MVertexBuffer* positionBuffer = NULL; float* positions = NULL; MHWRender::MVertexBuffer* colorBuffer = NULL; float* colors = NULL; MHWRender::MVertexBuffer* normalBuffer = NULL; float* normals = NULL; MHWRender::MVertexBuffer* uvBuffer = NULL; float* uvs = NULL; const MHWRender::MVertexBufferDescriptorList& descList = requirements.vertexRequirements(); int numVertexReqs = descList.length(); MHWRender::MVertexBufferDescriptor desc; for (int reqNum=0; reqNumacquire(totalVerts); } break; case MHWRender::MGeometry::kColor: { colorBuffer = data.createVertexBuffer(desc); if (colorBuffer) colors = (float*)colorBuffer->acquire(totalVerts, true); } break; //let's use texture to pass radius as uv.x case MHWRender::MGeometry::kTexture: { uvBuffer = data.createVertexBuffer(desc); if (uvBuffer) uvs = (float*)uvBuffer->acquire(totalVerts, true); } break; case MHWRender::MGeometry::kNormal: { normalBuffer = data.createVertexBuffer(desc); if (normalBuffer) normals = (float*)normalBuffer->acquire(totalVerts, true); } break; case MHWRender::MGeometry::kTangent: case MHWRender::MGeometry::kBitangent: break; default: // do nothing for stuff we don't understand break; } } if(positions) { int vid = 0; int pid = 0; unsigned int nf = mInstanceCache->faceCounts.length(); for(unsigned int i = 0; i < nf; i++) { int fc = mInstanceCache->faceCounts[i]; if(fc != 3) continue; //trianglular faces expected int fs = mInstanceCache->faceStart[i]; int fe = mInstanceCache->faceEnd[i]; for(int j = fs; j < fe; j++) { int k = mInstanceCache->faceVerts[j]; const MFloatPoint& p = mInstanceCache->points[k]; positions[pid] = p.x; positions[pid+1] = p.y; positions[pid+2] = p.z; pid += 3; } } //////////////////////////// for (unsigned int i = 0; i < guidePolys.size(); i++) { const GuidePoly& p = guidePolys[i]; for(int j=0; j<3; j++) { positions[pid++] = (float)p.v[j].x; positions[pid++] = (float)p.v[j].y; positions[pid++] = (float)p.v[j].z; } } //////////////////////////// for (unsigned int i = 0; i < knotPolys.size(); i++) { const KnotPoly& p = knotPolys[i]; for(int j=0; j<3; j++) { positions[pid++] = (float)p.v[j].x; positions[pid++] = (float)p.v[j].y; positions[pid++] = (float)p.v[j].z; } } positionBuffer->commit(positions); } if (colors) { unsigned int nf = mInstanceCache->faceCounts.length(); int cid = nf*3*4; //int cid = 0; //for(unsigned int i = 0; i < nf; i++) //{ // int fc = mInstanceCache->faceCounts[i]; // if(fc != 3) // continue; //trianglular faces expected // int fs = mInstanceCache->faceStart[i]; // int fe = mInstanceCache->faceEnd[i]; // for(int j = fs; j < fe; j++) // cid += 3; //} ///////////////////////////////////////////////// for (unsigned int i = 0; i < guidePolys.size(); i++) { const GuidePoly& p = guidePolys[i]; for(int j=0; j<3; j++) { colors[cid] = p.active?1.0f:0.0f; colors[cid+1] = 0.0f; colors[cid+2] = 0.0f; colors[cid+3] = 0.0f; cid+=4; } } //////////////////////////////////////////////// for (unsigned int i = 0; i < knotPolys.size(); i++) { const KnotPoly& p = knotPolys[i]; for(int j=0; j<3; j++) { colors[cid] = p.active?1.0f:0.0f; colors[cid+1] = 0.0f; colors[cid+2] = 0.0f; colors[cid+3] = 0.0f; cid+=4; } } colorBuffer->commit(colors); } if(normals) { int nid = 0; unsigned int nf = mInstanceCache->faceCounts.length(); for(unsigned int i = 0; i < nf; i++) { int fc = mInstanceCache->faceCounts[i]; if(fc != 3) continue; //trianglular faces expected int fs = mInstanceCache->faceStart[i]; int fe = mInstanceCache->faceEnd[i]; for(int j = fs; j < fe; j++) { int k = mInstanceCache->faceVerts[j]; //something is weird with this, normals are inverted... const MFloatPoint& n = mInstanceCache->normals[k]; normals[nid] = n.x; normals[nid+1] = n.y; normals[nid+2] = n.z; nid += 3; } } normalBuffer->commit(normals); } if(uvs) { int uid = 0; int oo = 0; unsigned int nf =mInstanceCache->faceCounts.length(); for(unsigned int i = 0; i < nf; i++) { int fc = mInstanceCache->faceCounts[i]; if(fc != 3) continue; //trianglular faces expected int fs = mInstanceCache->faceStart[i]; int fe = mInstanceCache->faceEnd[i]; for(int j = fs; j < fe; j++) { int k = mInstanceCache->faceVerts[j]; const MFloatPoint& t = mInstanceCache->uvs[oo /*k*/]; uvs[uid] = t.x; uvs[uid+1] = t.y; uid += 2; oo++; } } uvBuffer->commit(uvs); } // index data int numItems = itemList.length(); for (int i=0; irequiredVertexBuffers(); int numBufs = itemBuffers.length(); MHWRender::MVertexBufferDescriptor desc; for (int bufNum=0; bufNumname().asChar()); printf("\tBufferName: %s\n", desc.name().asChar()); printf("\tDataType: %s (dimension %d)\n", MHWRender::MGeometry::dataTypeString(desc.dataType()).asChar(), desc.dimension()); printf("\tSemantic: %s\n", MHWRender::MGeometry::semanticString(desc.semantic()).asChar()); printf("\n"); } } fflush(stdout); } //same indexing for all if (item->name() == kInstanceWireItemName || item->name() == kInstanceWireSelectedItemName) { MHWRender::MIndexBuffer* indexBuffer = NULL; if (!indexBuffer) { indexBuffer = data.createIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); if (indexBuffer) { unsigned int* buffer = (unsigned int*)indexBuffer->acquire(totalLines*2); if (buffer) { int idx = 0; int vid = 0; unsigned int nf = mInstanceCache->faceCounts.length(); for(unsigned int i = 0; i < nf; i++) { int fc = mInstanceCache->faceCounts[i]; if(fc != 3) continue; //trianglular faces expected //buffer[idx] = idx; //buffer[idx+1] = idx+1; //buffer[idx+2] = idx+2; ////printf("f %i [%i %i %i]\n",i, idx, idx+1, idx+2);fflush(stdout); //idx += 3; int fs = mInstanceCache->faceStart[i]; int fe = mInstanceCache->faceEnd[i]; int k=0; for(int j = fs; j < fe; j++) { buffer[idx] = vid; unsigned int vid2 = (k < 2) ? vid+1 : vid-2; buffer[idx+1] = vid2; idx += 2; vid++; k++; } } indexBuffer->commit(buffer); } } } // Associate same index buffer with either render item if (indexBuffer) { item->associateWithIndexBuffer(indexBuffer); } } if (item->name() == kInstanceShadedItemName ) { MHWRender::MIndexBuffer* indexBuffer = NULL; if (!indexBuffer) { indexBuffer = data.createIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); if (indexBuffer) { unsigned int* buffer = (unsigned int*)indexBuffer->acquire(instTriangles*3); if (buffer) { int idx = 0; int vid = 0; unsigned int nf = mInstanceCache->faceCounts.length(); for(unsigned int i = 0; i < nf; i++) { int fc = mInstanceCache->faceCounts[i]; if(fc != 3) continue; //trianglular faces expected buffer[idx] = idx; buffer[idx+1] = idx+1; buffer[idx+2] = idx+2; //printf("f %i [%i %i %i]\n",i, idx, idx+1, idx+2);fflush(stdout); idx += 3; //int fs = mInstanceCache->faceStart[i]; //int fe = mInstanceCache->faceEnd[i]; //int k=0; //for(int j = fs; j < fe; j++) //{ // buffer[idx] = vid; // unsigned int vid2 = (k < 2) ? vid+1 : vid-2; // buffer[idx+1] = vid2; // idx += 2; // vid++; // k++; //} } indexBuffer->commit(buffer); } } } // Associate same index buffer with either render item if (indexBuffer) { item->associateWithIndexBuffer(indexBuffer); } } //////////////////////////////////////// //// guide indexing if (item->name() == kGuidesItemName) { MHWRender::MIndexBuffer* indexBuffer2 = NULL; // Wireframe index buffer is same for both wireframe and selected render item // so we only compute and allocate it once, but reuse it for both render items if (!indexBuffer2) { indexBuffer2 = data.createIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); //indexBuffer2 = new MHWRender::MIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); if (indexBuffer2) { unsigned int* buffer = (unsigned int*)indexBuffer2->acquire(guideVerts); if (buffer) { for (unsigned int i = 0; i < guideVerts; i++) { //simple indexing buffer[i] = i+instVerts; } indexBuffer2->commit(buffer); } } } if(indexBuffer2) item->associateWithIndexBuffer(indexBuffer2); } ////////////////////////////// // knot indexing if (item->name() == kKnotsItemName) { MHWRender::MIndexBuffer* indexBuffer3 = NULL; if (!indexBuffer3) { indexBuffer3 = data.createIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); if (indexBuffer3) { unsigned int* buffer = (unsigned int*)indexBuffer3->acquire(knotVerts); if (buffer) { for (unsigned int i = 0; i < knotVerts; i++) { //simple indexing buffer[i] = i+instVerts+guideVerts; } indexBuffer3->commit(buffer); } } } if(indexBuffer3) item->associateWithIndexBuffer(indexBuffer3); } } } void shaveGeometryOverride::populateHairLines( const MHWRender::MGeometryRequirements& requirements, const MHWRender::MRenderItemList& itemList, MHWRender::MGeometry& data ) { // // Determine the number of hairs to be drawn. // bool doxpar = shave->getDoHairXparency(); bool fallback = shave->getDoFallback(); float ipasses = 1.0f/(float)mPasses; if(debugShader) { printf("numHairsToDraw: %i\n",mNumHairsToDraw); } unsigned int numGuidesToDraw = (unsigned int)mGuideCache->size(); if(debugShader) printf("numGuidesToDraw: %i\n",numGuidesToDraw); // // compute number of elements // unsigned int totalLines = 0; unsigned int hairLines = 0; unsigned int guideLines = 0; for (unsigned int h = 0; h < mNumHairsToDraw; h++) { const shaveHairShape::DisplayHair& hair = mHairCache->displayHair[h]; for (unsigned int s = 0; s < hair.size(); s++) { const shaveHairShape::DisplayStrand& strand = hair[s]; unsigned int strandlen = strand.verts.length(); hairLines += (strandlen-1); } } #if 1 { shaveHairShape::Guides::const_iterator iter; for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) { const shaveHairShape::Guide& guide = *iter; if (!guide.hidden) guideLines += (SHAVE_VERTS_PER_GUIDE-1); } } #endif totalLines = guideLines + hairLines; unsigned int hairVerts = hairLines*2; unsigned int guideVerts = guideLines*2; unsigned int totalVerts = totalLines*2; // vertex data MHWRender::MVertexBuffer* positionBuffer = NULL; float* positions = NULL; MHWRender::MVertexBuffer* colorBuffer = NULL; float* colors = NULL; MHWRender::MVertexBuffer* uvBuffer = NULL; float* uvs = NULL; MHWRender::MVertexBuffer* tangBuffer = NULL; float* tang = NULL; MHWRender::MVertexBuffer* guidePositionBuffer = NULL; float* guidepositions = NULL; ///////////////////////////////////////////////////////////// //int numItems = itemList.length(); //for (int i=0; irequiredVertexBuffers(); int numVertexReqs = descList.length(); MHWRender::MVertexBufferDescriptor desc; if(debugShader) { printf("numVertexReqs: %i\n",numVertexReqs); /* const MHWRender::MVertexBufferDescriptorList& itemBuffers = item->requiredVertexBuffers(); int numBufs = itemBuffers.length(); MHWRender::MVertexBufferDescriptor desc; for (int bufNum=0; bufNumname().asChar()); printf("\tBufferName: %s\n", desc.name().asChar()); printf("\tDataType: %s (dimension %d)\n", MHWRender::MGeometry::dataTypeString(desc.dataType()).asChar(), desc.dimension()); printf("\tSemantic: %s\n", MHWRender::MGeometry::semanticString(desc.semantic()).asChar()); printf("\n"); } } */ } for (int reqNum=0; reqNumacquire(totalVerts, true); } break; case MHWRender::MGeometry::kColor: { colorBuffer = data.createVertexBuffer(desc); if (colorBuffer) colors = (float*)colorBuffer->acquire(totalVerts, true); } break; //let's use texture to pass radius as uv.x case MHWRender::MGeometry::kTexture: { uvBuffer = data.createVertexBuffer(desc); if (uvBuffer) uvs = (float*)uvBuffer->acquire(totalVerts, true); } break; //case MHWRender::MGeometry::kNormal: // break; #if 0 case MHWRender::MGeometry::kNormal: { guidePositionBuffer = data.createVertexBuffer(desc); if (guidePositionBuffer) guidepositions = (float*)guidePositionBuffer->acquire(totalVerts,true); } break; #endif case MHWRender::MGeometry::kTangent: { tangBuffer = data.createVertexBuffer(desc); if (tangBuffer) tang = (float*)tangBuffer->acquire(totalVerts, true); } break; case MHWRender::MGeometry::kBitangent: break; default: // do nothing for stuff we don't understand break; } } if(debugShader) fflush(stdout); if (positions) { int pid = 0; for (unsigned int h = 0; h < mNumHairsToDraw; h++) { const shaveHairShape::DisplayHair& hair = mHairCache->displayHair[h]; for (unsigned int s = 0; s < hair.size(); s++) { const shaveHairShape::DisplayStrand& strand = hair[s]; int numS = strand.verts.length()-1; //////////////////////// //printf("strand%i \n",s); //////////////////////// for (int v = 0; v < numS; v++) { int vv = v+1; positions[pid++] = (float)strand.verts[v].x; positions[pid++] = (float)strand.verts[v].y; positions[pid++] = (float)strand.verts[v].z; positions[pid++] = (float)strand.verts[vv].x; positions[pid++] = (float)strand.verts[vv].y; positions[pid++] = (float)strand.verts[vv].z; //////////////////////// //printf("v%i [%f %f %f] v%i [%f %f %f]\n",v,strand.verts[v].x,strand.verts[v].y,strand.verts[v].z,vv,strand.verts[vv].x,strand.verts[vv].y,strand.verts[vv].z); //////////////////////// } } } #if 1 ///////////////////////////////////////////////// shaveHairShape::Guides::const_iterator iter; for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) { const shaveHairShape::Guide& guide = *iter; if (!guide.hidden) { for (int v = 0; v < SHAVE_VERTS_PER_GUIDE-1; v++) { int vv = v+1; positions[pid++] = guide.verts[v].x; positions[pid++] = guide.verts[v].y; positions[pid++] = guide.verts[v].z; positions[pid++] = guide.verts[vv].x; positions[pid++] = guide.verts[vv].y; positions[pid++] = guide.verts[vv].z; } } } #endif positionBuffer->commit(positions); } if (colors) { int cid = 0; for (unsigned int h = 0; h < mNumHairsToDraw; h++) { const shaveHairShape::DisplayHair& hair = mHairCache->displayHair[h]; for (unsigned int s = 0; s < hair.size(); s++) { const shaveHairShape::DisplayStrand& strand = hair[s]; int numS = strand.verts.length()-1; for (int v = 0; v < numS; v++) { int vv = v+1; MColor col0 = strand.colors[v]; MColor col1 = strand.colors[vv]; //we do get 1.0 in WFTYPE.alpha[] at the moment, lets fake it with tipfade if(doxpar ) { if(mDoTipFade) { col0.a = 1.0f - v/(float)numS; col1.a = 1.0f - vv/(float)numS; } if(mPasses > 1) { col0.a *= ipasses; col1.a *= ipasses; } if(mTransparency != 1.0f) { col0.a *= mTransparency; col1.a *= mTransparency; } } else col1.a = col0.a = 1.0f; colors[cid++] = col0.r; colors[cid++] = col0.g; colors[cid++] = col0.b; colors[cid++] = col0.a; colors[cid++] = col1.r; colors[cid++] = col1.g; colors[cid++] = col1.b; colors[cid++] = col1.a; } } } colorBuffer->commit(colors); } if (uvs) { int uid = 0; for (unsigned int h = 0; h < mNumHairsToDraw; h++) { const shaveHairShape::DisplayHair& hair = mHairCache->displayHair[h]; for (unsigned int s = 0; s < hair.size(); s++) { const shaveHairShape::DisplayStrand& strand = hair[s]; float r0, r1; int numS = strand.verts.length()-1; for (int v = 0; v < numS; v++) { int vv = v+1; int vvv = v+2; MVector dv = strand.verts[v]-strand.verts[vv]; float dvl = (float)dv.length(); float t1 = vv/(float)numS; if(v == 0) //root { r0 = strand.rootrad; r1 = strand.rootrad*(1.0f - t1) + strand.tiprad*t1; } else if(vv == numS) //tip { r0 = r1; r1 = strand.tiprad; } else { r0 = r1; r1 = strand.rootrad*(1.0f - t1) + strand.tiprad*t1; } uvs[uid++] = r0; uvs[uid++] = dvl; uvs[uid++] = r1; uvs[uid++] = 0.0f; } } } uvBuffer->commit(uvs); } if (tang) { int pid = 0; MVector tangent0; MVector tangent1; MVector p0; MVector p1; MVector p2; for (unsigned int h = 0; h < mNumHairsToDraw; h++) { const shaveHairShape::DisplayHair& hair = mHairCache->displayHair[h]; for (unsigned int s = 0; s < hair.size(); s++) { const shaveHairShape::DisplayStrand& strand = hair[s]; int numS = strand.verts.length()-1; //////////////////////// //printf("strand%i \n",s); //////////////////////// for (int v = 0; v < numS; v++) { int vv = v+1; int vvv = v+2; if(v == 0) //root { p0.x = strand.verts[0].x; p0.y = strand.verts[0].y; p0.z = strand.verts[0].z; p1.x = strand.verts[1].x; p1.y = strand.verts[1].y; p1.z = strand.verts[1].z; tangent0 = p1 - p0; tangent0.normalize(); tangent1 = tangent0; } else if(vv == numS) //tip { p0 = p1; p1.x = strand.verts[vv].x; p1.y = strand.verts[vv].y; p1.z = strand.verts[vv].z; tangent0 = tangent1; tangent1 = p1 - p0; tangent1.normalize(); } else { p0 = p1; p1.x = strand.verts[vv].x; p1.y = strand.verts[vv].y; p1.z = strand.verts[vv].z; tangent0 = tangent1; if(vvv < (int)strand.verts.length()) { p2.x = strand.verts[vvv].x; p2.y = strand.verts[vvv].y; p2.z = strand.verts[vvv].z; tangent1 = p2 - p0; } else { tangent1 = p1 - p0; } tangent1.normalize(); } tang[pid++] = (float)tangent0.x; tang[pid++] = (float)tangent0.y; tang[pid++] = (float)tangent0.z; tang[pid++] = (float)tangent1.x; tang[pid++] = (float)tangent1.y; tang[pid++] = (float)tangent1.z; } } } tangBuffer->commit(tang); } // index data MHWRender::MIndexBuffer* indexBuffer = NULL; MHWRender::MIndexBuffer* indexBuffer2 = NULL; MHWRender::MIndexBuffer* indexBuffer3 = NULL; int numItems = itemList.length(); for (int i=0; irequiredVertexBuffers(); int numBufs = itemBuffers.length(); MHWRender::MVertexBufferDescriptor desc; for (int bufNum=0; bufNumname().asChar()); printf("\tBufferName: %s\n", desc.name().asChar()); printf("\tDataType: %s (dimension %d)\n", MHWRender::MGeometry::dataTypeString(desc.dataType()).asChar(), desc.dimension()); printf("\tSemantic: %s\n", MHWRender::MGeometry::semanticString(desc.semantic()).asChar()); printf("\n"); } } fflush(stdout); } //same indexing for all hair if (item->name() == kHairWireItemName || item->name() == kHairWireSelectedItemName || item->name() == kHairShadedItemName || item->name() == kHairShadedSelectedItemName) { // Wireframe index buffer is same for both wireframe and selected render item // so we only compute and allocate it once, but reuse it for both render items if (!indexBuffer) { indexBuffer = data.createIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); if (indexBuffer) { unsigned int* buffer = (unsigned int*)indexBuffer->acquire(hairLines*2); if (buffer) { int idx = 0; for (unsigned int h = 0; h < mNumHairsToDraw; h++) { const shaveHairShape::DisplayHair& hair = mHairCache->displayHair[h]; for (unsigned int s = 0; s < hair.size(); s++) { const shaveHairShape::DisplayStrand& strand = hair[s]; int numS = strand.verts.length()-1; for (int v = 0; v < numS; v++) { //simple indexing buffer[idx] = idx; buffer[idx+1] = idx+1; idx += 2; } } } indexBuffer->commit(buffer); } } } // Associate same index buffer with either render item if (indexBuffer) { item->associateWithIndexBuffer(indexBuffer); } } ////////////////////////////////////////// // guide indexing if (item->name() == kGuidesItemName) { // Wireframe index buffer is same for both wireframe and selected render item // so we only compute and allocate it once, but reuse it for both render items if (!indexBuffer2) { indexBuffer2 = data.createIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); //indexBuffer2 = new MHWRender::MIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); if (indexBuffer2) { unsigned int* buffer = (unsigned int*)indexBuffer2->acquire(guideLines*2); if (buffer) { int idx0 = 0; int idx1 = hairVerts; shaveHairShape::Guides::const_iterator iter; for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) { const shaveHairShape::Guide& guide = *iter; if (!guide.hidden) { for (int v = 0; v < SHAVE_VERTS_PER_GUIDE-1; v++) { //simple indexing buffer[idx0] = idx1; buffer[idx0+1] = idx1+1; idx0 += 2; idx1 += 2; } } } indexBuffer2->commit(buffer); //data.addIndexBuffer(indexBuffer2); item->associateWithIndexBuffer(indexBuffer2); } } } } ////////////////////////////// // guide indexing if (item->name() == kKnotsItemName) { if (!indexBuffer3) { indexBuffer3 = data.createIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); if (indexBuffer3) { unsigned int* buffer = (unsigned int*)indexBuffer3->acquire(guideVerts/2); if (buffer) { int idx0 = 0; int idx1 = hairVerts; shaveHairShape::Guides::const_iterator iter; for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) { const shaveHairShape::Guide& guide = *iter; if (!guide.hidden) { for (int v = 0; v < SHAVE_VERTS_PER_GUIDE-1; v++) { //simple indexing buffer[idx0] = idx1; idx0 += 1; idx1 += 2; } } } indexBuffer3->commit(buffer); item->associateWithIndexBuffer(indexBuffer3); } } } } /////////////////////////////// } ////////////////////////////////////// //} ////////////////////////////////////// } ///////////////// cache polys & z-sorty strands/////////////////////////////////// void shaveGeometryOverride::cacheHairPolys() { bool doxpar = shave->getDoHairXparency(); bool fallback = shave->getDoFallback(); float ipasses = 1.0f/(float)mPasses; // far/near MPoint centerPt(mHairCache->center); MPoint nearPt = centerPt - mViewDir*mHairCache->radius; MPoint farPt = centerPt + mViewDir*mHairCache->radius; MPoint nearSc = nearPt*mWorldToCam; MPoint farSc = farPt*mWorldToCam; //sort to groups ingroups = 0; int numS = 1; groups.resize(400); for(int g = (int)groups.size()-1; g >= 0; g--) { groups[g].p.clear(); groups[g].p.reserve(300); } for (unsigned int h = 0; h < mNumHairsToDraw; h++) { const shaveHairShape::DisplayHair& hair = mHairCache->displayHair[h]; for (unsigned int s = 0; s < hair.size(); s++) { const shaveHairShape::DisplayStrand& strand = hair[s]; MVector tangent0; MVector tangent1; MVector p0; MVector p1; MVector p2; float r0; float r1; numS = strand.verts.length()-1; float inumS = 1.0f/(float)numS; for (int v = 0; v < numS; v++) { int vv = v+1; int vvv = v+2; float t1 = vv*inumS; if(v == 0) //root polygon { p0.x = strand.verts[0].x; p0.y = strand.verts[0].y; p0.z = strand.verts[0].z; p1.x = strand.verts[1].x; p1.y = strand.verts[1].y; p1.z = strand.verts[1].z; tangent0 = p1 - p0; tangent0.normalize(); tangent1 = tangent0; r0 = strand.rootrad; r1 = strand.rootrad*(1.0f - t1) + strand.tiprad*t1; } else if(vv == numS) //tip poly { p0 = p1; p1.x = strand.verts[vv].x; p1.y = strand.verts[vv].y; p1.z = strand.verts[vv].z; tangent0 = tangent1; tangent1 = p1 - p0; tangent1.normalize(); r0 = r1; r1 = strand.tiprad; } else { p0 = p1; p1.x = strand.verts[vv].x; p1.y = strand.verts[vv].y; p1.z = strand.verts[vv].z; tangent0 = tangent1; if(vvv < (int)strand.verts.length()) { p2.x = strand.verts[vvv].x; p2.y = strand.verts[vvv].y; p2.z = strand.verts[vvv].z; tangent1 = p2 - p0; } else { tangent1 = p1 - p0; } tangent1.normalize(); r0 = r1; r1 = strand.rootrad*(1.0f - t1) + strand.tiprad*t1; } if(p0 == p1) continue; MVector d0 = tangent0^mViewDir; MVector d1 = tangent1^mViewDir; d0.normalize(); d1.normalize(); MVector v0 = p0 + d0*r0; MVector v1 = p0 - d0*r0; MVector v2 = p1 - d1*r1; MVector v3 = p1 + d1*r1; MColor col0 = strand.colors[v]; //we do get 1.0 in WFTYPE.alpha[] at the moment, lets fake it with tipfade //col.a = 1.0f - vv*inumS; col0.a = 1.0f; if(mDoTipFade) col0.a -= v*inumS; if(mPasses) col0.a *= ipasses; MColor col1 = strand.colors[vv]; col1.a = 1.0f; if(mDoTipFade) col1.a -= vv*inumS; if(mPasses) col1.a *= ipasses; Poly P1; P1.v[0] = v0; P1.c[0] = col0; P1.t[0] = tangent0; P1.v[1] = v1; P1.c[1] = col0; P1.t[1] = tangent0; P1.v[2] = v2; P1.c[2] = col1; P1.t[2] = tangent1; Poly P2; P2.v[0] = v2; P2.c[0] = col1; P2.t[0] = tangent1; P2.v[1] = v3; P2.c[1] = col1; P2.t[1] = tangent1; P2.v[2] = v0; P2.c[2] = col0; P2.t[2] = tangent0; //for Joe: what I am doing wrong with z-sort? MPoint V(0.5f*(p0.x+p1.x), 0.5f*(p0.y+p1.y), 0.5f*(p0.z+p1.z));//a middle of poly MPoint S = V*mWorldToCam; //to camera space float d = (float)((S.z-nearSc.z)/(farSc.z-nearSc.z)); //if(d > 1.0f) d = 1.0f; //if(d < 0.0f) d = 0.0f; int pos = (int)(d*(float)groups.size()); if(pos >= groups.size()) pos = (int)groups.size()-1; if(pos < 0) pos = 0; groups[pos].p.push_back(P1); groups[pos].p.push_back(P2); ingroups+=2; } } } }; void shaveGeometryOverride::cacheGuidePolys() { int numS = SHAVE_VERTS_PER_GUIDE-1; shaveHairShape::Guides::const_iterator iter; float w = shave->getGuideThinkness(); w *= (4.0f*mGuideExtent)/1500.0f; int nvisible = 0; for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) { const shaveHairShape::Guide& guide = *iter; if (!guide.hidden) nvisible++; } guidePolys.clear(); guidePolys.reserve(numS*2*nvisible); mSelectionData.mPrimIdxToGuideIdx.clear(); mSelectionData.mPrimIdxToGuideIdx.reserve(numS*2*nvisible); int v; for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) { const shaveHairShape::Guide& guide = *iter; if (!guide.hidden) { unsigned int guideIdx = (unsigned int)(iter - mGuideCache->begin()); bool isActive = (guide.select != 0); MVector tangent0; MVector tangent1; MVector p0; MVector p1; MVector p2; int numS = SHAVE_VERTS_PER_GUIDE-1; for (v = 0; v < numS; v++) { int vv = v+1; int vvv = v+2; if(v == 0) //root polygon { p0.x = guide.verts[0].x; p0.y = guide.verts[0].y; p0.z = guide.verts[0].z; p1.x = guide.verts[1].x; p1.y = guide.verts[1].y; p1.z = guide.verts[1].z; tangent0 = p1 - p0; tangent0.normalize(); tangent1 = tangent0; } else if(vv == numS) //tip poly { p0 = p1; p1.x = guide.verts[vv].x; p1.y = guide.verts[vv].y; p1.z = guide.verts[vv].z; tangent0 = tangent1; tangent1 = p1 - p0; tangent1.normalize(); } else { p0 = p1; p1.x = guide.verts[vv].x; p1.y = guide.verts[vv].y; p1.z = guide.verts[vv].z; tangent0 = tangent1; if(vvv < SHAVE_VERTS_PER_GUIDE) { p2.x = guide.verts[vvv].x; p2.y = guide.verts[vvv].y; p2.z = guide.verts[vvv].z; tangent1 = p2 - p0; } else { tangent1 = p1 - p0; } tangent1.normalize(); } if(p0 == p1) continue; MVector d0 = tangent0^mViewDir; MVector d1 = tangent1^mViewDir; d0.normalize(); d1.normalize(); MVector v0 = p0 + d0*(float)w; MVector v1 = p0 - d0*(float)w; MVector v2 = p1 - d1*(float)w; MVector v3 = p1 + d1*(float)w; GuidePoly P1; P1.v[0] = v0; P1.v[1] = v1; P1.v[2] = v2; P1.active = isActive; GuidePoly P2; P2.v[0] = v2; P2.v[1] = v3; P2.v[2] = v0; P2.active = isActive; guidePolys.push_back(P1); guidePolys.push_back(P2); // Both polys are on the same guide so we store // the same index in them. // mSelectionData.mPrimIdxToGuideIdx.push_back(guideIdx); mSelectionData.mPrimIdxToGuideIdx.push_back(guideIdx); } } } } void shaveGeometryOverride::cacheKnotPolys() { int numS = SHAVE_VERTS_PER_GUIDE-1; shaveHairShape::Guides::const_iterator iter; float w = shave->getGuideThinkness(); w *= (4.0f*mGuideExtent)/1500.0f; w *= 2.0f; /////////////////////////////////////// bool drawActive = false; int startVert = 0; int endVert = SHAVE_VERTS_PER_GUIDE - 1; MString selectType; MGlobal::executeCommand( "optionVar -q shaveBrushSelectMode", selectType ); if(selectType == "root") { startVert = endVert = 0; } else if(selectType == "tip") { startVert = endVert = SHAVE_VERTS_PER_GUIDE - 1; } else if(selectType == "vert") { startVert = 0; endVert = SHAVE_VERTS_PER_GUIDE - 1; } const unsigned int numDrawnVertsPerGuide = endVert - startVert + 1; /////////////////////////////////////// int nvisible = 0; for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) { const shaveHairShape::Guide& guide = *iter; if (!guide.hidden) nvisible++; } knotPolys.clear(); knotPolys.reserve(numDrawnVertsPerGuide*2*nvisible); mSelectionData.mPrimIdxToVertIdx.clear(); mSelectionData.mPrimIdxToVertIdx.reserve(numDrawnVertsPerGuide*2*nvisible); MVector p0; for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) { const shaveHairShape::Guide& guide = *iter; if (!guide.hidden) { unsigned int guideIdx = (unsigned int)(iter - mGuideCache->begin()); for (int i = startVert; i <= endVert ; i++) { // // Is this vert active? // bool isActive = ((guide.select & (1 << i)) != 0); p0.x = guide.verts[i].x; p0.y = guide.verts[i].y; p0.z = guide.verts[i].z; MVector v0 = p0 + (mUpDir+mRightDir)*(float)w; MVector v1 = p0 + (-mUpDir+mRightDir)*(float)w; MVector v2 = p0 + (-mUpDir-mRightDir)*(float)w; MVector v3 = p0 + (mUpDir-mRightDir)*(float)w; KnotPoly P1; P1.v[0] = v0; P1.v[1] = v1; P1.v[2] = v2; P1.active = isActive; KnotPoly P2; P2.v[0] = v2; P2.v[1] = v3; P2.v[2] = v0; P2.active = isActive; knotPolys.push_back(P1); knotPolys.push_back(P2); // Both polys represent the same guide vert so we store the same // index for each of them. // mSelectionData.mPrimIdxToVertIdx.push_back(guideIdx * SHAVE_VERTS_PER_GUIDE + i); mSelectionData.mPrimIdxToVertIdx.push_back(guideIdx * SHAVE_VERTS_PER_GUIDE + i); } } } } ////////////////////// draw polyginal hair /////////////////////////////////// void shaveGeometryOverride::populateHairPolys( const MHWRender::MGeometryRequirements& requirements, const MHWRender::MRenderItemList& itemList, MHWRender::MGeometry& data ) { bool doxpar = shave->getDoHairXparency(); bool fallback = shave->getDoFallback(); float ipasses = 1.0f/(float)mPasses; cacheHairPolys(); cacheGuidePolys(); cacheKnotPolys(); #if 0 //skip guides for now unsigned int numGuidesToDraw = mGuideCache->size(); #endif // // compute number of elements // unsigned int hairPolys = ingroups; unsigned int nguidePolys = (unsigned int)guidePolys.size(); unsigned int nknotPolys = (unsigned int)knotPolys.size(); unsigned int totalPolys = nguidePolys + hairPolys + nknotPolys; unsigned int hairVerts = hairPolys*3; unsigned int guideVerts = nguidePolys*3; unsigned int knotVerts = nknotPolys*3; unsigned int totalVerts = totalPolys*3; // vertex data MHWRender::MVertexBuffer* positionBuffer = NULL; float* positions = NULL; MHWRender::MVertexBuffer* colorBuffer = NULL; float* colors = NULL; MHWRender::MVertexBuffer* uvBuffer = NULL; float* uvs = NULL; MHWRender::MVertexBuffer* tangBuffer = NULL; float* tang = NULL; MHWRender::MVertexBuffer* guidePositionBuffer = NULL; float* guidepositions = NULL; const MHWRender::MVertexBufferDescriptorList& descList = requirements.vertexRequirements(); int numVertexReqs = descList.length(); MHWRender::MVertexBufferDescriptor desc; if(debugShader) { printf("numVertexReqs: %i\n",numVertexReqs); /* const MHWRender::MVertexBufferDescriptorList& itemBuffers = item->requiredVertexBuffers(); int numBufs = itemBuffers.length(); MHWRender::MVertexBufferDescriptor desc; for (int bufNum=0; bufNumname().asChar()); printf("\tBufferName: %s\n", desc.name().asChar()); printf("\tDataType: %s (dimension %d)\n", MHWRender::MGeometry::dataTypeString(desc.dataType()).asChar(), desc.dimension()); printf("\tSemantic: %s\n", MHWRender::MGeometry::semanticString(desc.semantic()).asChar()); printf("\n"); } } */ } for (int reqNum=0; reqNumacquire(totalVerts,true); } break; case MHWRender::MGeometry::kColor: { if(debugShader) printf("COLOR BUFFER\n"); colorBuffer = data.createVertexBuffer(desc); if (colorBuffer) colors = (float*)colorBuffer->acquire(totalVerts,true); } break; //let's use texture to pass radius as uv.x case MHWRender::MGeometry::kTexture: { if(debugShader) printf("TEXTURE BUFFER\n"); uvBuffer = data.createVertexBuffer(desc); if (uvBuffer) uvs = (float*)uvBuffer->acquire(totalVerts,true); } break; //case MHWRender::MGeometry::kTangent: case MHWRender::MGeometry::kNormal: { if(debugShader) printf("TANTGET/NORMAL BUFFER\n"); tangBuffer = data.createVertexBuffer(desc); if (tangBuffer) tang = (float*)tangBuffer->acquire(totalVerts,true); } break; case MHWRender::MGeometry::kBitangent: break; default: // do nothing for stuff we don't understand break; } } if(debugShader) fflush(stdout); if (positions) { int pid = 0; for(int g = (int)groups.size()-1; g >= 0; g--) { const PolyGroup& gr = groups[g]; unsigned int gs = (unsigned int)gr.p.size(); for(unsigned int i = 0; i < gs; i++) { const Poly& p = gr.p[i]; for(int j=0; j<3; j++) { positions[pid] = (float)p.v[j].x; positions[pid+1] = (float)p.v[j].y; positions[pid+2] = (float)p.v[j].z; pid+=3; } } } ///////////////////////////////////////////////// for (unsigned int i = 0; i < guidePolys.size(); i++) { const GuidePoly& p = guidePolys[i]; for(int j=0; j<3; j++) { positions[pid++] = (float)p.v[j].x; positions[pid++] = (float)p.v[j].y; positions[pid++] = (float)p.v[j].z; } } //////////////////////////////////////////////// for (unsigned int i = 0; i < knotPolys.size(); i++) { const KnotPoly& p = knotPolys[i]; for(int j=0; j<3; j++) { positions[pid++] = (float)p.v[j].x; positions[pid++] = (float)p.v[j].y; positions[pid++] = (float)p.v[j].z; } } positionBuffer->commit(positions); } if (colors) { int cid = 0; for(int g = (int)groups.size()-1; g >= 0; g--) { const PolyGroup& gr = groups[g]; unsigned int gs = (unsigned int)gr.p.size(); for(unsigned int i = 0; i < gs; i++) { const Poly& p = gr.p[i]; for(int j=0; j<3; j++) { colors[cid] = p.c[j].r; colors[cid+1] = p.c[j].g; colors[cid+2] = p.c[j].b; colors[cid+3] = p.c[j].a; cid+=4; } } } ///////////////////////////////////////////////// for (unsigned int i = 0; i < guidePolys.size(); i++) { const GuidePoly& p = guidePolys[i]; for(int j=0; j<3; j++) { colors[cid] = p.active?1.0f:0.0f; colors[cid+1] = 0.0f; colors[cid+2] = 0.0f; colors[cid+3] = 0.0f; cid+=4; } } //////////////////////////////////////////////// for (unsigned int i = 0; i < knotPolys.size(); i++) { const KnotPoly& p = knotPolys[i]; for(int j=0; j<3; j++) { colors[cid] = p.active?1.0f:0.0f; colors[cid+1] = 0.0f; colors[cid+2] = 0.0f; colors[cid+3] = 0.0f; cid+=4; } } colorBuffer->commit(colors); } if (uvs) { int uid = 0; for(int g = (int)groups.size()-1; g >= 0; g--) { const PolyGroup& gr = groups[g]; unsigned int gs = (unsigned int)gr.p.size(); for(unsigned int i = 0; i < gs; i++) { const Poly& p = gr.p[i]; for(int j=0; j<3; j++) { uvs[uid] = (float)p.uv[j].x; uvs[uid+1] = (float)p.uv[j].y; uid+=2; } } } uvBuffer->commit(uvs); } if (tang) { int tid = 0; for(int g = (int)groups.size()-1; g >= 0; g--) { const PolyGroup& gr = groups[g]; unsigned int gs = (unsigned int)gr.p.size(); for(unsigned int i = 0; i < gs; i++) { const Poly& p = gr.p[i]; for(int j=0; j<3; j++) { tang[tid] = (float)p.t[j].x; tang[tid+1] = (float)p.t[j].y; tang[tid+2] = (float)p.t[j].z; tid+=3; } } } tangBuffer->commit(tang); } // index data MHWRender::MIndexBuffer* indexBuffer = NULL; MHWRender::MIndexBuffer* indexBuffer2 = NULL; MHWRender::MIndexBuffer* indexBuffer3 = NULL; int numItems = itemList.length(); for (int i=0; irequiredVertexBuffers(); int numBufs = itemBuffers.length(); MHWRender::MVertexBufferDescriptor desc; for (int bufNum=0; bufNumname().asChar()); printf("\tBufferName: %s\n", desc.name().asChar()); printf("\tDataType: %s (dimension %d)\n", MHWRender::MGeometry::dataTypeString(desc.dataType()).asChar(), desc.dimension()); printf("\tSemantic: %s\n", MHWRender::MGeometry::semanticString(desc.semantic()).asChar()); printf("\n"); } } fflush(stdout); } //same indexing for all hair if (item->name() == kHairWireItemName || item->name() == kHairWireSelectedItemName || item->name() == kHairShadedItemName || item->name() == kHairShadedSelectedItemName) { // Wireframe index buffer is same for both wireframe and selected render item // so we only compute and allocate it once, but reuse it for both render items if (!indexBuffer) { indexBuffer = data.createIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); if (indexBuffer) { unsigned int* buffer = (unsigned int*)indexBuffer->acquire(hairVerts); if (buffer) { for(unsigned int k = 0; k < hairVerts; k++) buffer[k] = k; indexBuffer->commit(buffer); } } } // Associate same index buffer with either render item if (indexBuffer) { item->associateWithIndexBuffer(indexBuffer); } } ////////////////////////////////////////// // guide indexing if (item->name() == kGuidesItemName) { // Wireframe index buffer is same for both wireframe and selected render item // so we only compute and allocate it once, but reuse it for both render items if (!indexBuffer2) { indexBuffer2 = data.createIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); //indexBuffer2 = new MHWRender::MIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); if (indexBuffer2) { unsigned int* buffer = (unsigned int*)indexBuffer2->acquire(guideVerts); if (buffer) { for (unsigned int i = 0; i < guideVerts; i++) { //simple indexing buffer[i] = i+hairVerts; } indexBuffer2->commit(buffer); } } } if(indexBuffer2) item->associateWithIndexBuffer(indexBuffer2); } ////////////////////////////// // knot indexing if (item->name() == kKnotsItemName) { if (!indexBuffer3) { indexBuffer3 = data.createIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); if (indexBuffer3) { unsigned int* buffer = (unsigned int*)indexBuffer3->acquire(knotVerts); if (buffer) { for (unsigned int i = 0; i < knotVerts; i++) { //simple indexing buffer[i] = i+hairVerts+guideVerts; } indexBuffer3->commit(buffer); } } } if(indexBuffer3) { item->associateWithIndexBuffer(indexBuffer3); } } /////////////////////////////// } ////////////////////////////////////// //} ////////////////////////////////////// } /////////// not used ///////////////////// void shaveGeometryOverride::populateHairGuides( const MHWRender::MGeometryRequirements& requirements, const MHWRender::MRenderItemList& itemList, MHWRender::MGeometry& data ) { float w = shave->getGuideThinkness(); unsigned int numGuidesToDraw = (unsigned int)mGuideCache->size(); if(debugShader) printf("numGuidesToDraw: %i\n",numGuidesToDraw); // // compute number of elements // unsigned int totalLines = 0; { shaveHairShape::Guides::const_iterator iter; for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) { const shaveHairShape::Guide& guide = *iter; if (!guide.hidden) totalLines += (SHAVE_VERTS_PER_GUIDE-1); } } unsigned int totalVerts = totalLines*2; // vertex data int positionStart = 0; MHWRender::MVertexBuffer* positionBuffer = NULL; float* positions = NULL; MHWRender::MVertexBuffer* tangBuffer = NULL; float* tang = NULL; const MHWRender::MVertexBufferDescriptorList& descList = requirements.vertexRequirements(); int numVertexReqs = descList.length(); MHWRender::MVertexBufferDescriptor desc; if(debugShader) printf("numVertexReqs: %i\n",numVertexReqs); for (int reqNum=0; reqNumdescriptor().semantic() == desc.semantic()) { positionBuffer = vb; positionStart = positionBuffer->vertexCount(); positions = (float*)malloc(totalVerts*3*sizeof(float)); if(debugShader) printf("startPosVertex: %i\n",positionStart); } } } //positionBuffer = data.createVertexBuffer(desc); //if (positionBuffer) // positions = (float*)positionBuffer->acquire(totalVerts); } break; case MHWRender::MGeometry::kTangent: { tangBuffer = data.createVertexBuffer(desc); if (tangBuffer) tang = (float*)tangBuffer->acquire(totalVerts); } break; case MHWRender::MGeometry::kNormal: { positionBuffer = data.createVertexBuffer(desc); if (positionBuffer) positions = (float*)positionBuffer->acquire(totalVerts); } break; default: // do nothing for stuff we don't understand break; } #endif } if(debugShader) fflush(stdout); if (positions ) { int pid = 0; int g = 0; shaveHairShape::Guides::const_iterator iter; for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) { const shaveHairShape::Guide& guide = *iter; if (!guide.hidden) { for (int v = 0; v < SHAVE_VERTS_PER_GUIDE-1; v++) { int vv = v+1; positions[pid++] = guide.verts[v].x; positions[pid++] = guide.verts[v].y; positions[pid++] = guide.verts[v].z; positions[pid++] = guide.verts[vv].x; positions[pid++] = guide.verts[vv].y; positions[pid++] = guide.verts[vv].z; } } } positionBuffer->commit(positions); //positionBuffer->update(positions,positionStart,totalVerts,false); //free(positions); } if (tang) { int pid = 0; MFloatVector tangent0; MFloatVector tangent1; MFloatVector p0; MFloatVector p1; MFloatVector p2; shaveHairShape::Guides::const_iterator iter; for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) { const shaveHairShape::Guide& guide = *iter; if (!guide.hidden) { for (int v = 0; v < SHAVE_VERTS_PER_GUIDE-2; v++) { int vv = v+1; int vvv = v+2; if(v == 0) //root { p0.x = guide.verts[0].x; p0.y = guide.verts[0].y; p0.z = guide.verts[0].z; p1.x = guide.verts[1].x; p1.y = guide.verts[1].y; p1.z = guide.verts[1].z; tangent0 = p1 - p0; tangent0.normalize(); tangent1 = tangent0; } else if(vv == (int)SHAVE_VERTS_PER_GUIDE-1) //tip { p0 = p1; p1.x = guide.verts[vv].x; p1.y = guide.verts[vv].y; p1.z = guide.verts[vv].z; tangent0 = tangent1; tangent1 = p1 - p0; tangent1.normalize(); } else { p0 = p1; p1.x = guide.verts[vv].x; p1.y = guide.verts[vv].y; p1.z = guide.verts[vv].z; tangent0 = tangent1; if(vvv < (int)SHAVE_VERTS_PER_GUIDE) { p2.x = guide.verts[vvv].x; p2.y = guide.verts[vvv].y; p2.z = guide.verts[vvv].z; tangent1 = p2 - p0; } else { tangent1 = p1 - p0; } tangent1.normalize(); } tang[pid++] = tangent0.x; tang[pid++] = tangent0.y; tang[pid++] = tangent0.z; tang[pid++] = tangent1.x; tang[pid++] = tangent1.y; tang[pid++] = tangent1.z; } } } tangBuffer->commit(tang); } // index data MHWRender::MIndexBuffer* indexBuffer = NULL; int numItems = itemList.length(); for (int i=0; irequiredVertexBuffers(); int numBufs = itemBuffers.length(); MHWRender::MVertexBufferDescriptor desc; for (int bufNum=0; bufNumname().asChar()); printf("\tBufferName: %s\n", desc.name().asChar()); printf("\tDataType: %s (dimension %d)\n", MHWRender::MGeometry::dataTypeString(desc.dataType()).asChar(), desc.dimension()); printf("\tSemantic: %s\n", MHWRender::MGeometry::semanticString(desc.semantic()).asChar()); printf("\n"); } } fflush(stdout); } //indexing if (item->name() == kGuidesItemName) { // Wireframe index buffer is same for both wireframe and selected render item // so we only compute and allocate it once, but reuse it for both render items if (!indexBuffer) { indexBuffer = data.createIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); //indexBuffer = new MHWRender::MIndexBuffer(MHWRender::MGeometry::kUnsignedInt32); if (indexBuffer) { unsigned int* buffer = (unsigned int*)indexBuffer->acquire(totalLines*2); if (buffer) { int idx0 = 0; int idx1 = 0;//positionStart; // 0; shaveHairShape::Guides::const_iterator iter; for (iter = mGuideCache->begin(); iter != mGuideCache->end(); iter++) { const shaveHairShape::Guide& guide = *iter; if (!guide.hidden) { for (int v = 0; v < SHAVE_VERTS_PER_GUIDE-1; v++) { //simple indexing buffer[idx0] = idx1; buffer[idx0+1] = idx1+1; idx0 += 2; idx1 += 2; } } } indexBuffer->commit(buffer); //data.addIndexBuffer(indexBuffer); item->associateWithIndexBuffer(indexBuffer); } } } } } } #if MAYA_API_VERSION >= 201600 void shaveGeometryOverride::updateSelectionGranularity( const MDagPath& path, MHWRender::MSelectionContext& selectCtx ) { // The selection level defaults to object, so we only have to set // it if we want to select components. // MHWRender::DisplayStatus displayStatus = MHWRender::MGeometryUtilities::displayStatus(path); if (displayStatus == MHWRender::kHilite) { MSelectionMask mayaSelectionMask; if (MGlobal::selectionMode() == MGlobal::kSelectComponentMode) { mayaSelectionMask = MGlobal::componentSelectionMask(); } else { mayaSelectionMask = MGlobal::objectSelectionMask(); } MSelectionMask supportedComponents(MSelectionMask::kSelectCVs); supportedComponents.addMask(MSelectionMask::kSelectMeshEdges); if (mayaSelectionMask.intersects(supportedComponents)) { selectCtx.setSelectionLevel(MHWRender::MSelectionContext::kComponent); } } #if MAYA_API_VERSION >= 201650 // pointSnappingActive() actually became available in API version 201610 // (2016 Extension 1), but we don't want to force our 2016 users to update // to the extension if they don't have to. // else if (pointSnappingActive()) { selectCtx.setSelectionLevel(MHWRender::MSelectionContext::kComponent); } #endif } #endif void shaveGeometryOverride::cleanUp() { } #if MAYA_API_VERSION >= 201600 MStatus shaveGeometryOverride::registerComponentConverters() { MStatus st = MHWRender::MDrawRegistry::registerComponentConverter(kKnotsItemName, guideVertComponentConverter::create); if (st) { st = MHWRender::MDrawRegistry::registerComponentConverter(kGuidesItemName, guideComponentConverter::create); } return st; } MStatus shaveGeometryOverride::deregisterComponentConverters() { MStatus st = MHWRender::MDrawRegistry::deregisterComponentConverter(kKnotsItemName); if (st) { st = MHWRender::MDrawRegistry::deregisterComponentConverter(kGuidesItemName); } return st; } #endif bool shaveGeometryOverride::disableRenderItem( MHWRender::MRenderItemList& list, const MString& itemName, MHWRender::MGeometry::Primitive primType, MHWRender::MGeometry::DrawMode drawMode ) { int index = list.indexOf(itemName, primType, drawMode); if (index >= 0) { list.itemAt(index)->enable(false); } return (index >= 0); }